Skip to content

fix(gate-65, shellcheck): restore the required check — a whole-repo gate fires inside every fixture - #384

Merged
rubenvdlinde merged 3 commits into
mainfrom
fix/shellcheck-sc2294-eval
Aug 12, 2026
Merged

fix(gate-65, shellcheck): restore the required check — a whole-repo gate fires inside every fixture#384
rubenvdlinde merged 3 commits into
mainfrom
fix/shellcheck-sc2294-eval

Conversation

@rubenvdlinde

@rubenvdlinde rubenvdlinde commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Why this is urgent

Package invariants — one of the three REQUIRED checks on main — has been red
since 09:38 today.
Six queued gate PRs (#373, #376, #377, #378, #379, #380) cannot
merge, and none of them caused it.

Two independent things were red. Only one was blocking, and it was not the one that
looks scarier in the checks list.

check red since required?
ShellCheck 2026-08-03 no — worth fixing, never blocked anything
Package invariants today 09:38 yes

1. gate-65 fires inside every other suite's fixture (the blocker)

gate-65 coding-standard-adoption (#382) is deliberately not diff-scoped — it judges
the repo's standing configuration, which is right for its purpose. But its only
NOT-APPLICABLE path is "no composer.json and no phpcs.xml", and every synthetic
fixture in the corpus has a composer.json
, because the gate under test needs one.

So it fires inside other suites' fixtures and fails runs those suites require to end
green:

  • test_gate_license_triangle_scope.sh — 2 assertions, one of them the control that
    makes assertion 5 mean anything
    , died on status 1
  • test_gate_acceptance_matrix.sh — gate-65 declared with neither fixture nor reasoned row

The general lesson, worth writing down: a whole-repo gate is a global mutation of every
other gate's test environment.
Diff-scoped gates decline inside fixtures they do not
concern; a whole-repo gate cannot, so it quietly becomes a precondition of every "and the
run ends green" assertion in the corpus.

Fixes

Fixture adopts the standard — mirroring the compliant scaffold already in
test_check_coding_standard_adoption.sh: require-dev, cs:check/cs:fix,
.php-cs-fixer.dist.php, and an .editorconfig with indent_style = tab.

Arm 6 needed the same fix separately. It rewrites composer.json down to a bare name
to drop the licence field — which also un-adopts the standard. That arm is the control
for assertion 5, so it silently proving nothing was the worse of the two failures.

gate-65 gets its COVERED-ELSEWHERE.md row. It is not uncovered:
test_check_coding_standard_adoption.sh plants 15 single-defect probes, one per rule,
plus a negative control and a wiring assertion. That is precisely what the ELSEWHERE list
exists for — not an exemption.

2. ShellCheck SC2294 (not required, but red for 9 days)

eval "$@"eval "$*" in the probe() helper. All 15 callers pass their planted
defect as one quoted shell string, so there was never an array to preserve;
ShellCheck names the string form explicitly. Checked all 15 call sites — none passes more
than one argument, so $*'s IFS join cannot change any of them.

Verification

suite result
test_gate_license_triangle_scope.sh 11 PASS / 0 FAIL (was 2 FAIL)
test_gate_acceptance_matrix.sh green — 13 gates counted as covered elsewhere (was 12)
test_check_coding_standard_adoption.sh 23 passed / 0 failed, before and after
full helper suite on this branch 76 passed · 2 quarantined · 0 failed
full helper suite on origin/main 73 passed · 2 quarantined · 3 failed

⚠️ On the 76 vs 73: one of that difference is not a fix. test_gate_45_to_55_acceptance.sh
refuses to run without ajv (exit 2) rather than emit a false verdict, and require('ajv')
resolves upward — so it runs from an in-tree checkout and refuses from a /tmp
worktree. The main-branch run above was made in a /tmp worktree and this one had
NODE_PATH set. Real defects fixed: 2, which is exactly what CI reported.

ShellCheck has been red on main since 2026-08-03 on a single finding, and
because the required check runs against the PR MERGE ref it fails every open
PR — including six queued gate fixes that never touched this file.

All 15 probe callers pass their planted defect as ONE quoted shell string
('rm .php-cs-fixer.dist.php', "sed -i 's|...|...|' composer.json"), so the
string form is what the helper always meant; eval "$@" only looked
array-shaped.

Verified both arms of the suite: 23 passed / 0 failed before and after. That
is a real control, not a tautology — had eval stopped executing, the defect
would go unplanted and each probe would report 'did not detect'.
`Package invariants` — a REQUIRED check — has been red on main since 09:38
today, so the six queued gate PRs cannot merge. Both failures trace to gate-65
(coding-standard-adoption), added in #382.

gate-65 is deliberately not diff-scoped: it judges the repo's standing
configuration. Its only NOT-APPLICABLE path is "no composer.json and no
phpcs.xml", and every synthetic fixture in the corpus HAS a composer.json,
because the gate under test needs one. So it fires inside other suites'
fixtures and fails runs those suites require to be green. A whole-repo gate is
a global mutation of every other gate's test environment.

test_gate_license_triangle_scope.sh — the fixture now adopts the standard the
same way the compliant scaffold in test_check_coding_standard_adoption.sh does
(require-dev, cs:check/cs:fix, .php-cs-fixer.dist.php, and an .editorconfig
with indent_style = tab). Arm 6 needed the same treatment separately: it
rewrites composer.json down to a bare name to drop the licence field, which
also un-adopts the standard — that arm is the CONTROL that makes assertion 5
mean anything, so it silently proving nothing was the worse of the two
failures.

test_gate_acceptance_matrix.sh — gate-65 gets its COVERED-ELSEWHERE.md row.
It is not uncovered: test_check_coding_standard_adoption.sh plants 15
single-defect probes, one per rule, plus a negative control and a wiring
assertion. That is what the ELSEWHERE list is for.

Verified: license-triangle 11 PASS / 0 FAIL, acceptance matrix green (13 gates
counted as covered elsewhere, was 12), and the full helper suite on this branch
is 76 passed / 2 quarantined / 0 failed.
@rubenvdlinde rubenvdlinde changed the title fix(shellcheck): SC2294 — a red main that fails every open PR fix(gate-65, shellcheck): restore the required check — a whole-repo gate fires inside every fixture Aug 12, 2026
…heredoc

My own previous commit reintroduced a ShellCheck finding while fixing one. The
.php-cs-fixer.dist.php body is PHP and contains $config; written as printf
with single quotes, ShellCheck reads it as a shell expansion that will not
expand. A <<'PHP' heredoc says verbatim to the shell and to the reader at once,
and matches how the sibling scaffold already writes the same file.

Re-verified: license-triangle 11 PASS / 0 FAIL.
@rubenvdlinde
rubenvdlinde merged commit d090d19 into main Aug 12, 2026
31 checks passed
@rubenvdlinde
rubenvdlinde deleted the fix/shellcheck-sc2294-eval branch August 12, 2026 11:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant