Skip to content

feat(registry): refuse a push with a stale FEATURES.md - #675

Merged
tieubao merged 4 commits into
masterfrom
feat/registry-freshness-guard
Sep 16, 2026
Merged

tieubao merged 4 commits into
masterfrom
feat/registry-freshness-guard

Conversation

@tieubao

@tieubao tieubao commented Sep 16, 2026

Copy link
Copy Markdown
Member

docs/FEATURES.md is a generated projection of lib/registry/feature-registry.sh. Its inputs are the whole feature surface, including tests/test-*.sh and docs/specs/SPEC-*.md, because the Specs and Tests columns are token greps.

That pair is the trap. Adding a test file moves rows for every feature the file names, and an author adding a test has no reason to think about a docs projection. PR #663 added tests/test-gitattributes-union.sh, which moved the /kit:docs row's Tests column. Nobody regenerated. tests/test-meta.sh went red on master and every PR merge commit inherited that red until PR #665 regenerated by hand.

The pin caught the drift on the wrong side of the push.

What changed

hooks/ship-gate.sh gains an arm beside the existing doc-projection arm. It fires when a kit-repo push edits a FEATURES.md input and leaves docs/FEATURES.md untouched, calls feature-registry.sh check, and exits 2 with the regenerate command when the projection has drifted.

tests/test-meta.sh now pins freshness through that same check verb instead of rebuilding the regenerate-and-diff by hand, so the gate and the suite cannot disagree about what fresh means.

The short-circuit. The regeneration costs about 20 seconds. A push that also carries docs/FEATURES.md skips it: an author who regenerated is not the failure mode, and whether they regenerated correctly is what CI pins. The expensive path runs only on the shape of the incident.

Scope. Kit repo only, by file existence. Escape hatch DWARVES_KIT_SKIP_REGISTRY_FRESHNESS=1, registered in lib/config/module-registry.md.

No run-all --changed rule was needed. The goal's pause-if clause fired: tests/test-meta.sh already carries the # always: marker that forces it onto every diff. Evidence is in the proof.

Also repins the Codex trust command's content hash for ship-gate.sh, which tests/test-codex-hooks.sh asserts and which any edit to that hook invalidates.

Verification

Command Exit
bash tests/test-registry-freshness-guard.sh 0 (11 assertions)
bash tests/test-meta.sh 0 (853/853)
bash tests/test-config-registry.sh 0 (50/50)
bash tests/test-codex-hooks.sh 0 (86/86)
bash tests/run-all.sh --changed 0 (44 suites)

Negative control through lib/gate/negctl.sh, verdict PASS: neutering the arm's input pattern turns the new suite red and restoring it turns it green.

Spec: docs/specs/SPEC-294-registry-freshness-guard.md
Proof: docs/verification/registry-freshness-guard.md
Board: ID-905

docs/FEATURES.md is a generated projection whose inputs include
tests/test-*.sh and docs/specs/SPEC-*.md, because the Specs and Tests
columns are token greps. An author adding a test file has no reason to
think about a docs projection, so the drift lands, the suite pin goes
red on master, and every later merge commit inherits it until someone
regenerates by hand.

hooks/ship-gate.sh gains an arm that fires when a push edits an input
and leaves docs/FEATURES.md untouched. It calls the registry's own
check verb, which tests/test-meta.sh now also uses for its freshness
pin, so the gate and the suite share one definition of fresh.

The regeneration costs about 20 seconds, so a push that carries the
regenerated file skips it; whether that regeneration was correct is
what CI pins. Escape hatch DWARVES_KIT_SKIP_REGISTRY_FRESHNESS=1.

run-all --changed needed no new selection rule: test-meta.sh already
carries the always marker that forces it onto every diff.

Spec: docs/specs/SPEC-294-registry-freshness-guard.md
The Codex trust command pins ship-gate.sh by content hash, so editing
the hook makes tests/test-codex-hooks.sh red until the pin follows.
@tieubao
tieubao merged commit b2d127c into master Sep 16, 2026
@tieubao
tieubao deleted the feat/registry-freshness-guard branch September 16, 2026 18:04
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