Context
eslint-factory/src/rules/no-core-setoutput-non-string.ts and its newer twin no-core-exportvariable-non-string.ts are the same rule applied to two @actions/core methods. They embed a verbatim-duplicated nonStringKind() function (setoutput lines 25-43, exportvariable lines 26-44), identical message IDs, and identical suggestion logic. They have drifted on client matching:
no-core-exportvariable-non-string matches CORE_ALIASES ({core, coreObj}) — see no-core-exportvariable-non-string.ts:75.
no-core-setoutput-non-string hardcodes callee.object.name !== "core" (no-core-setoutput-non-string.ts:74), so it is blind to coreObj.setOutput(...).
Grounded blind spot
actions/setup/js/parse_mcp_gateway_log.cjs uses coreObj as the core alias and calls setOutput through it:
parse_mcp_gateway_log.cjs:215 — coreObj.setOutput("aic", roundedAIC)
parse_mcp_gateway_log.cjs:221 — coreObj.setOutput("ambient_context", roundedAmbientContext)
parse_mcp_gateway_log.cjs:257, :278 — further coreObj.setOutput(...) sites
These specific args are identifiers/strings today, so it is a coverage/consistency gap, not a live bug — but coreObj is a real, used alias, so any coreObj.setOutput("x", 0) / coreObj.setOutput("x", arr.length) would silently escape while the same call on core is flagged.
Relationship to #45864
#45864 asks the exportVariable rule to extend to coreObj — but the current exportVariable rule already uses CORE_ALIASES; the remaining gap is the mirror case in the setOutput rule described here. Consolidating the twins fixes the alias direction once and prevents the two from drifting again.
Proposal
- Extract the shared
nonStringKind() + kind constants + wrapWithString/useEmptyString suggestion builder into a shared module (alongside core-aliases.ts).
- Change
no-core-setoutput-non-string to match CORE_ALIASES instead of the hardcoded "core", matching the exportVariable twin.
- Add a
coreObj.setOutput(nonString) invalid test case.
Note on already-tracked value-detection gaps
The shared nonStringKind still carries the setOutput refinements previously filed — negative numeric literals parsed as UnaryExpression (#42680) and numeric members beyond .length such as .size/.byteLength (#42682). Consolidating means those fix once for both twins rather than being re-implemented per rule; this issue is scoped to the alias/dedup work and simply flags that the exportVariable twin currently inherits the same gaps.
Acceptance criteria
nonStringKind and suggestion logic live in one shared module used by both rules.
coreObj.setOutput("x", 0) is flagged identically to core.setOutput("x", 0).
- Existing tests for both rules pass; new
coreObj.setOutput invalid test added.
Generated by 🤖 ESLint Refiner · 517.5 AIC · ⌖ 13.1 AIC · ⊞ 4.6K · ◷
Context
eslint-factory/src/rules/no-core-setoutput-non-string.tsand its newer twinno-core-exportvariable-non-string.tsare the same rule applied to two@actions/coremethods. They embed a verbatim-duplicatednonStringKind()function (setoutput lines 25-43, exportvariable lines 26-44), identical message IDs, and identical suggestion logic. They have drifted on client matching:no-core-exportvariable-non-stringmatchesCORE_ALIASES({core, coreObj}) — see no-core-exportvariable-non-string.ts:75.no-core-setoutput-non-stringhardcodescallee.object.name !== "core"(no-core-setoutput-non-string.ts:74), so it is blind tocoreObj.setOutput(...).Grounded blind spot
actions/setup/js/parse_mcp_gateway_log.cjsusescoreObjas the core alias and calls setOutput through it:parse_mcp_gateway_log.cjs:215—coreObj.setOutput("aic", roundedAIC)parse_mcp_gateway_log.cjs:221—coreObj.setOutput("ambient_context", roundedAmbientContext)parse_mcp_gateway_log.cjs:257,:278— furthercoreObj.setOutput(...)sitesThese specific args are identifiers/strings today, so it is a coverage/consistency gap, not a live bug — but
coreObjis a real, used alias, so anycoreObj.setOutput("x", 0)/coreObj.setOutput("x", arr.length)would silently escape while the same call oncoreis flagged.Relationship to #45864
#45864 asks the exportVariable rule to extend to
coreObj— but the current exportVariable rule already usesCORE_ALIASES; the remaining gap is the mirror case in the setOutput rule described here. Consolidating the twins fixes the alias direction once and prevents the two from drifting again.Proposal
nonStringKind()+ kind constants +wrapWithString/useEmptyStringsuggestion builder into a shared module (alongsidecore-aliases.ts).no-core-setoutput-non-stringto matchCORE_ALIASESinstead of the hardcoded"core", matching the exportVariable twin.coreObj.setOutput(nonString)invalid test case.Note on already-tracked value-detection gaps
The shared
nonStringKindstill carries the setOutput refinements previously filed — negative numeric literals parsed asUnaryExpression(#42680) and numeric members beyond.lengthsuch as.size/.byteLength(#42682). Consolidating means those fix once for both twins rather than being re-implemented per rule; this issue is scoped to the alias/dedup work and simply flags that the exportVariable twin currently inherits the same gaps.Acceptance criteria
nonStringKindand suggestion logic live in one shared module used by both rules.coreObj.setOutput("x", 0)is flagged identically tocore.setOutput("x", 0).coreObj.setOutputinvalid test added.