fix(gate): scope Feature Map staleness to the section that changed - #22
Merged
Conversation
@code:FIX-GATE-SCOPE-001 Editing the PRD or Feature Map mid-cycle stalled the running Feature and forced the whole pre-specter chain again, because both the global and the per-Feature checklist bound to a digest of the entire feature-map.md. A refinement to Feature 012's section therefore invalidated Feature 006's in-flight cycle even though nothing Feature 006 depends on had moved. Split the binding by what each artifact actually owns. A `## Feature NNN:` heading is global content — it carries the map's decomposition — while the body under it belongs to that Feature alone: - global checklist binds to `global_sha256`: shared content plus the set of Feature headings. - per-Feature checklist binds to `scope_sha256`: the global skeleton plus its own section body. - the verify and analyze station bundles hash the map the same scoped way, so an unrelated refinement no longer invalidates reviewer reports either. Refining one Feature's section is now inert for every other Feature. Editing that Feature's own section, the commitment index, or the set of Features still binds, so decomposition and ordering changes keep re-verifying. Also add the owner override the per-Feature binding never had — the global side already had one, leaving the per-Feature staleness with no escape hatch at all. New `map-sha` subcommand emits both digests so the authoring commands record the right value; rev 2 -> 3 so installed gates self-heal. Checklists written before scoping keep working: a whole-file digest is still accepted, it simply re-stales on every unrelated edit until regenerated. Tests: 11 new cases covering the unrelated-section pass, the own-section, shared-content and added-Feature failures, all three override paths, and legacy binding compatibility. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pre-existing formatting drift, unrelated to the gate scoping change but failing `make ci` lint on master. Formatter-only; no assertions or test behavior are touched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@code:FIX-GATE-SCOPE-001 A line starting with "## " inside a fenced code block was read as structure, so it closed the surrounding Feature section. Everything after the fence then counted as global content, which put one Feature's body into the global skeleton — editing that Feature would re-stale every other Feature's checklist, restoring exactly the coupling this change removes. Feature Maps embed shell and markdown samples routinely, so this is a realistic shape rather than a corner case. Track fence state and treat "## " as a heading only outside a fence, in both the gate and the test oracle. Found by probing the filter against a map with a fenced sample during review preparation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@code:FIX-GATE-SCOPE-001 Both independent lanes returned FAIL. Confirmed findings, all fixed: - The commit-time backstop `check_feature_map_gate.py` still demanded a whole-file digest, so a checklist written per the new contract would be rejected at commit time and the scoping would not work in practice. Port the same partition to Python and pin the two implementations together with a parity test over five map shapes. - The `pre-verify` station bundle still hashed the whole map, so a Feature body edit stalled the global audit even though its checklist stayed current — the reported symptom was only half fixed. - A mismatched inner fence (`~~~` inside a ``` block) reopened the parser mid-body. - A Feature absent from the map produced a scope digest equal to the global skeleton, letting a checklist for a nonexistent Feature satisfy the gate. Reject it in both `map-sha` and the per-Feature gate. - `map-sha` interpolated an unvalidated argument into JSON; require a Feature number. Declined, with reasons recorded in docs/dev_daily.md: a `## ` sub-heading inside a Feature body (the map format uses `###` there, and the pre-lean gate partitioned identically), and field-count validation on the override record (it matches the existing global override validator, and forging one already requires repository write access). Residual risk, by design: a dependency stated only inside one Feature's body does not stale the Features that depend on it. Shared contracts belong to the map's global area. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@code:FIX-GATE-SCOPE-001 Second independent review round, FAIL again. All four findings fixed. The dependency finding corrects a design error in the previous commit. It recorded "shared contracts belong to the map's global area" as accepted residual risk, but /ms.featuremap requires every Feature section to carry a Dependencies subsection, so that instruction contradicted the map format the project actually generates. Rewriting one Feature's edges left every affected dependent bound to a DAG that no longer existed. Every `### Dependencies` subsection now belongs to the global skeleton, so a DAG edit binds globally while ordinary body refinement stays scoped. The remaining three were parser defects: - The backstop read the staged map in text mode, folding CRLF to LF, while awk kept the carriage return; a CRLF map hashed differently in the two implementations and rejected its own checklist. Read bytes, and match awk's record rules exactly: split on \n only, and terminate every emitted record with a newline whether or not the file ended with one. - `map_has_feature` had no fence state, so a sample `## Feature 999:` inside a code block registered as a real section while the digest filter correctly ignored it. Existence and partitioning now share the rule. - Fence tracking kept a boolean, so a four-backtick fence closed on an inner three-backtick line and leaked the rest of the body. Track the opening character and length; close only on a matching delimiter at least as long. The parity matrix grows to ten shapes and gains behavioral tests per case, since parity alone cannot catch both implementations being wrong together. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…l map @code:FIX-GATE-SCOPE-001 Third review round. Two confirmed findings fixed. The Python backstop matched Feature headings with `\d`, which accepts any Unicode decimal digit, while awk uses `[0-9]`. A map using non-ASCII digits partitioned differently in the two implementations, so a checklist one accepted the other rejected. All three implementations — gate, backstop, and the test oracle — now use an ASCII class, and the parity matrix covers it. A per-Feature checklist could also name any file in `**Feature Map**:` and have its binding checked against that file, while the station bundle kept hashing the canonical map. Pointing at an untouched copy therefore kept the binding current while the real map's edit went unnoticed — the exact invalidation this change exists to guarantee. The canonical path is now the only accepted one. This predates the branch, but it lives in the block rewritten here and defeats its central property, so it is closed rather than deferred. Three further findings are declined as pre-existing and unrelated to map scoping: substring matching on checklist Feature identity, no count check on the per-Feature Result field, and unvalidated reviewer Mode values. All three are on master, none is introduced or worsened here, and each needs its own fix with its own tests. Recorded in docs/dev_daily.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Antigravity passed with no defects for a second consecutive round. The Codex lane produced no report and its session went idle, so it is recorded UNAVAILABLE rather than passing; under the lean contract one unavailable lane caps a non-FAIL result at WARN, which advances. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
beomeodev
marked this pull request as ready for review
August 3, 2026 03:30
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.
Problem
Editing the PRD or Feature Map during a Feature cycle stalled the running Feature and forced the whole
/ms.pre-specterchain again. Both the global and the per-Feature checklist bound to a SHA256 of the entiredocs/prd/feature-map.md, so refining Feature 012's section invalidated Feature 006's in-flight cycle even though nothing Feature 006 depends on had moved.Change
Split the binding by what each artifact owns. A
## Feature NNN:heading is global content — it carries the map's decomposition — while the body under it belongs to that Feature:global_sha256: shared content plus the set of Feature headingsscope_sha256: the global skeleton plus its own section bodypre-verify,verify, andanalyzestation bundles hash the map at the scope they own, so reviewer reports stay valid too### Dependenciesstays global in every Feature:/ms.featuremaprequires it in each section, so the DAG lives inside the bodies even though it is shared stateNew
map-shasubcommand emits both digests for the authoring commands; gaterev2 → 3 so installed gates self-heal.Also adds the owner override the per-Feature binding never had, and rejects a checklist whose
**Feature Map**:names a file other than the canonical map.Compatibility
Checklists written before scoping keep working — a whole-file digest is still accepted; it simply re-stales on every unrelated edit until regenerated. Stations in flight when the gate is upgraded re-verify once, since the digest definition changes.
Verification
make cigreen: lint, types, 173 tests, coverage 90%+/ms.featuremapgenerates)Final round: Antigravity PASS with no defects for a second consecutive round; the Codex lane produced no report and went idle, so it is recorded UNAVAILABLE rather than passing. Under the lean contract one unavailable lane caps a non-FAIL result at WARN.
Deliberately out of scope
Three pre-existing gate defects unrelated to map scoping, all present on
masterand recorded indocs/dev_daily.mdfor separate fixes: substring matching on checklist Feature identity, no count check on the per-FeatureResultfield, and unvalidated reviewerModevalues.🤖 Generated with Claude Code