Add per-section verbatim fidelity for markdown files - #397
Conversation
A carried markdown file can now be intent overall while individual `## sections` are checked verbatim - byte-identical to the hub canonical, EOL-normalized. This closes the propagation gap #305 names: a universal rule block (write-safety, git rules) could silently rot or fall behind a newly added rule downstream while its heading still passed the presence check, because the whole file is intent and the audit only grepped for the heading. - audit.py: extract_section (cuts the `## heading` body at the next sibling H2, keeps nested H3), verbatim_sections, and per-section check_verbatim wiring in audit_repo. A verbatim section reuses the existing region-verbatim engine (stale-vs-modified by git history), so a downstream paraphrase or missing rule surfaces as DRIFT, not a false clean. --selftest case added. - files.schema.json / validate.py: a section object may carry fidelity (intent default, or verbatim, markdown-only). - files.json: mark AGENTS.md's three universal, repo-agnostic rule sections verbatim - Repository Boundaries and Write Safety, Git and Commit Rules, Verification Discipline. They carry no repo-specific content (verified: no SHAs, no ptr727/<repo> refs, no placeholders). Branching Model and the rest stay intent (Branching Model cites this repo's own SHAs). - fidelity-model.md: document the section granularity and why these three sit at verbatim. Verified: validate green, selftest passes, and a live audit of Financial-Modeling flags all three sections as DRIFT (its AGENTS.md carries older paraphrases missing the newer rules) - the propagation gap, now visible. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR extends the spec/audit fidelity model to support per-section verbatim fidelity for Markdown documents, allowing a file to remain intent overall while specific ## sections are enforced as byte-identical (EOL-normalized) to the hub canonical. This closes a propagation gap where downstream paraphrases could silently miss newly added universal rules while still passing a heading-presence check.
Changes:
- Add per-section
fidelitysupport (intent|verbatim) forsections[]entries inspec/files.json, validated by schema andspec/validate.py. - Teach
spec/audit.pyto exclude verbatim sections from heading-presence checks and instead hash-compare extracted##section bodies via the existing verbatim engine (with a new--selftestcase). - Mark select
AGENTS.mduniversal rule sections asverbatiminspec/files.jsonand document the new granularity inspec/fidelity-model.md.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| spec/validate.py | Validates per-section fidelity values and enforces that section-level verbatim is Markdown-only. |
| spec/files.schema.json | Extends the schema to allow a fidelity field on section objects. |
| spec/files.json | Marks selected AGENTS.md sections as verbatim while keeping the file overall intent. |
| spec/fidelity-model.md | Documents that verbatim can apply to Markdown section regions in addition to files and workflow job regions. |
| spec/audit.py | Implements section extraction and per-section verbatim checks, plus self-test coverage for the new section region behavior. |
Rewrite the clause-joining semicolons in the validate.py section-fidelity comment and the extract_section docstring as separate clauses, per the repo prose convention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… in extract_section Two gaps in the section extractor the per-section verbatim check relies on: - The heading line was excluded from the region, and the match was case/whitespace-insensitive, so a downstream repo could re-case or re-space the heading and still pass the hash. Include the matched heading line in the region so its exact bytes are hashed (the locate-match stays case-insensitive, so a re-cased heading is found and then flagged as drift rather than read as a missing section). - Any line whose stripped form started with "## " was treated as a section boundary, including one inside a fenced code block, which could truncate the region and hide drift after it. Track ``` / ~~~ fences and only treat a real, unfenced H2 as the boundary. Selftest extended to cover both (heading in region, a fenced ## kept inside the body, a re-cased heading rehashing). Re-verified live: finmod still flags all three sections, hub self-audit clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
spec/audit.py:171
- extract_section() only recognizes H2 headings that start with the literal "## " (single space) and matches the target via exact string equality on the stripped line. Markdown allows arbitrary whitespace (multiple spaces/tabs) after the hashes, so a valid heading like "## Git and Commit Rules" would fail to match and be reported as "region absent downstream" rather than a stale/modified verbatim diff.
elif not fenced and stripped.lower().startswith("## "):
if capturing:
break # a sibling H2 ends the section
if stripped.lower() == target:
capturing = True
…r whitespace tolerance Match the heading by its parsed text (the text after the "## " marker, case- and whitespace-folded) instead of an exact case-insensitive line compare, so a heading with an extra marker-gap (e.g. "## Alpha") is still located rather than read as a missing section - the exact heading bytes remain in the hashed region, so the re-spacing still surfaces as drift. Add a selftest case for the whitespace-tolerant locate. Re-verified live (finmod still flags all three sections, hub self-audit clean). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
spec/audit.py:158
- extract_section() docstring says the heading locate is "case- and whitespace-folded", but the implementation only case-folds and strips surrounding whitespace (it does not fold internal whitespace). Tighten the wording so the doc matches the actual matching behavior, which avoids misleading future readers about what variations are tolerated.
EOL-normalized to `\\n`. The match that locates the heading is by its parsed text (the text after the `## `
marker, case- and whitespace-folded), so a re-cased or re-spaced heading is still found rather than read as
a missing section. The heading line's exact bytes are then part of the hashed region, so that re-casing or
Closes the propagation gap #305 names for the docs class: a universal rule block could silently rot — or fall behind a newly added rule — downstream while its heading still passed the presence check, because
AGENTS.mdisintentas a whole and the audit only grepped for headings. The new history-rewrite rule (#394) was the live test case: nothing flagged that Financial-Modeling's paraphrased "Git and Commit Rules" was missing it.What
A carried markdown file can now be intent overall while individual
## sectionsareverbatim— byte-identical to the hub canonical, EOL-normalized, reusing the existing region-verbatim engine (stale-vs-modified by git history).extract_section(cuts a## headingbody at the next sibling H2, keeps nested###),verbatim_sections, per-sectioncheck_verbatimwiring, and a--selftestcase.fidelity(intent default, or verbatim, markdown-only).AGENTS.md's three universal rule sections verbatim: Repository Boundaries and Write Safety, Git and Commit Rules, Verification Discipline. Verified repo-agnostic (no SHAs, noptr727/<repo>refs, no placeholders).Branching Modeland the rest stayintent(Branching Model cites this repo's own historical SHAs — the trap that makes it not universal).Verification
validate.pygreen;audit.py --selftestpasses (incl. the new section case); a live audit of Financial-Modeling flags all three sections as DRIFT — itsAGENTS.mdcarries older paraphrases. DRIFT is advisory (0 defect/letter), so it surfaces the re-vendor need without redding the gate — the idempotent artifact recording current state.Follow-up (next PR)
Extend
fidelity_honesty.py+reports/divergences.mdto show the fleet-wide section burn-down (which repos diverge on which sections), so the propagation state is one regenerable artifact.🤖 Generated with Claude Code