chore: spell out legacy decision-log citations inline and add the ledger-citation gate (comment-only) - #393
Merged
Merged
Conversation
…h the decisions spelled out inline (no code change)
…gnore and the unreleased CHANGELOG entry (no code change)
…ed and retired-meaning positive controls, scope, non-vacuity, self-clean
…-local) or retired-meaning ledger ids in tracked source outside .devflow/ and CHANGELOG.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two decision logs shared one id space: the old repo log (
.devflow/decisions/decisions.md, 22 entries, removed from git on 2026-06-27 in88ddbcc; recoverable withgit show 88ddbcc~1:.devflow/decisions/decisions.md) and the current learning ledger, which mints ids automatically as max+1. Roughly seventy source comments still cited the old log's numbers, and six of those numbers now mean something else in the current ledger. This PR fixes the source side: every legacy citation in tracked source is replaced by the decision spelled out inline, and a mechanical gate keeps unminted or retired-meaning ids out of tracked source from now on. Ledger entries are untouched; released CHANGELOG sections are immutable and untouched.What changed
Commits 1–2 (sweep, no code change). 70 lines across 15 files.
watch.rsnames the two invariants once in its module doc (freshness rule: the dependency set and the--varsfile are re-derived from fresh compile output / from disk on every rebuild; reconcile rule: the idle tick only re-arms watches cheaply, a full rescan happens only on watch loss/recovery) and uses those names at every site;cli_watch.rspoints at that module doc.resolver.rs,resolver_tests.rs,error.rs,evaluator.rsspell out frontmatter-first import ordering, leaf re-validation of dynamically assembled content, and origin-rides-along-the-data. Bundler packages (loader.ts,vite-plugin/src/index.ts,hmr-harness.mjs, the four HMR/watch e2e specs) get the same treatment..gitignore:62now explains the.devflow/history in words. One[Unreleased]CHANGELOG line loses a parenthetical id.Commit 3 (RED) + commit 4 (gate).
scripts/verify-ledger-citations.mjsscans tracked files (excluding.devflow/**andCHANGELOG.md) for ledger-id tokens and fails on (R1) a number above the frozen ceiling{ADR: 17, PF: 53}— an id the ledger has not minted, i.e. a plan-local number — or (R2) one of the six frozen denylisted ADR numbers that carried a source-local meaning until this sweep. ExportsCEILING,DENYLIST,classify,scanText; exit 1 on findings or zero files, exit 2 on git failure; the token regex is assembled from pieces so the script is self-clean. The spec (8 tests) covers the real tree (≥500 files, ≥1000 tokens — measured 550 / 1217), both rejection rules with positive controls, a legitimate id, scope exclusions, the empty-repo fail-closed path, self-cleanliness of script and spec, and the golden constants. It is picked up by the existingnpm run test:gatesglob, so CI'ssource-hygienejob enforces it with noci.ymlorpackage.jsonchange. Documented residual: a semantic re-collision on an id that is both minted and legitimately cited is not mechanically detectable; the added-lines grep in every PR is the control.Proofs (each "expect nothing" check paired with a planted positive control that fired)
:!.devflow :!CHANGELOG.md: 70 lines before, no output after; control (ledger/KB files still match) printed.cli_watch.rs(5) andhmr-harness.mjs(2): gone; the current-meaning sites elsewhere are untouched (control prints two inwatch.rs)..gitignore: no output; controls fired. Rust: 3 hit pairs, all incrates/mds-cli/tests/cli_watch.rs(~2826, ~3173, ~3289) — backslash-continued string literals insideassert_eq!panic messages that the sweep table mandated; message text only, no executable change. Reported here rather than treated as clean.git diff --name-only: exactly 17 files (the 15 sweep files + the two newscripts/files); no.devflow/**, nopackage.json, no.github/**. The pre-existing local.gitignoreworking-tree hunk is not in any commit.Gates (local, on
dfaaac2)cargo fmt --all -- --checkclean (the Rust sweep is a byte-for-byte fmt no-op); both clippy invocations clean;cargo nextest run -p mds-core --test assert_promotions1 passedcargo nextest run -p mds-core -p mds-cli: 2361 passed (unchanged vs main); doctests 53;cargo +1.88 checkcleannpm run test:gates: 220 pass / 0 fail (212 + 8);node scripts/verify-ledger-citations.mjs→✓ ledger-citation gate: scanned 550 file(s), 1217 citation token(s); none unminted or retired-meaning; positive controls in a throwaway repo: a retired-meaning id → exit 1 "source-local meaning", an unminted id → exit 1 "not minted"node scripts/verify-no-control-bytes.mjs✓;npm run build -w @mdscript/bundler-utils -w @mdscript/vite-plugincleanSnyk code scan unavailable (MCP ENOENT); the
security/snykCI check is SCA-only.Not a release-surface PR. No issue is closed by this PR; context lives in the local v0.4.3 step-D hand-off (decision DEC-1, fix the source side). The learning ledger's own entry about this collision is the Learning agent's to amend after merge.