fix(ci): unbreak the pre-push gate — strict-mode leak and apm-owned settings drift
Two pre-existing failures, both red at HEAD before ADR-0020 work began,
both invisible in an ordinary local run.
RUN_TESTS_STRICT leaked from the environment into test-run-tests.sh's
fixture children. The meta-test is itself a suite the runner discovers,
so under the gate's own invocation the variable propagated outer runner
-> batch_run -> the fixture's copy of run-tests.sh, flipping it strict.
Case 10c (a deliberate control asserting a skip is tolerated WITHOUT
strict) then failed. Six further cases were silently running strict too
and asserting against the wrong stream — case 9 was matching the stderr
strict block rather than the stdout skip list it was written to check.
run_fake now spawns via 'env -u RUN_TESTS_STRICT', so fixture strictness
is a property of the case, never of how the file was launched. No
assertion weakened; run-tests.sh itself is untouched.
pretty-format-json --autofix was re-sorting apm's output on the way into
every commit. .claude/settings.json is apm-owned (ADR-0018/0019) and its
exclude list named fifteen generated manifests but not this file, so
since 2e395a4 it has been committed in a key order apm would never write
— permanent drift on a file with an empty git diff. Content was always
byte-identical; only JSON key order differed. The exclude ships in the
same commit as the corrected file because otherwise the hook re-breaks
it during staging.
apm.lock.yaml: generated_at churn, plus lint's exec_status corrected from
'deployed' to 'gated_pending_approval' — executables.allow grants only
kyberforge#1.5.0, so lint's hooks/bin are genuinely gated.
New coverage: an ambient RUN_TESTS_STRICT must not reach a fixture that
did not ask for it, and under --strict the skip report goes to stderr
only with the stdout list suppressed. Neither was pinned.
This commit is contained in:
21
LESSONS.md
21
LESSONS.md
@@ -203,3 +203,24 @@ this skill to manage label definitions`). That is the `Kyberforge.VagueQualifier
|
||||
Fix: keep the opener rule opener-anchored and give mid-description prose its own rule with its own
|
||||
token list. A rule's scope anchor is part of its contract, not an implementation detail to relax when
|
||||
a new case does not fit.
|
||||
|
||||
## 2026-08-14 — A formatter in the commit path manufactures drift on a file with a clean git diff
|
||||
|
||||
`apm audit --ci` failed for weeks on `.claude/settings.json` while `git diff` on that file was empty —
|
||||
the worst possible pairing of signals, because the file matched HEAD exactly and every instinct says
|
||||
"nothing changed here". The content was identical to apm's output to the byte; only the JSON key
|
||||
order differed. `pretty-format-json --autofix` sorts object keys unless `--no-sort-keys` is passed,
|
||||
and its `exclude:` listed fifteen generated manifests but not this file, so from the commit that
|
||||
first wrote a hook entry there (`2e395a4`) onward, apm's insertion-ordered output was silently
|
||||
re-sorted on the way in. apm then replayed the install, produced its own order, and reported drift
|
||||
against a file no human had touched.
|
||||
|
||||
Two general points. First, a tool-owned generated file that passes through an autofixing formatter is
|
||||
drifted by construction, and the diff that would reveal it never appears in `git diff` — it only
|
||||
exists between the formatter's input and its output, which nothing stores. Second, the fix is
|
||||
self-undoing unless the exclude lands in the same commit: correcting the file alone means the hook
|
||||
re-breaks it as it is staged. Fix: when a tool declares ownership of a path, add that path to every
|
||||
autofixing hook's `exclude` at the moment ownership is declared, not when the drift is noticed. This
|
||||
repo gates marketplace-mirror, plugin-content and vale-style drift deterministically and has no
|
||||
equivalent gate asserting tool-owned paths stay out of formatter scope — `.claude/settings.json` was
|
||||
the sixteenth exclude and nothing prevents a seventeenth.
|
||||
|
||||
Reference in New Issue
Block a user