Development record
2026-09-25 — One fence rule for the tree (the run A orchestrator's technical ruling for the fences lane, recorded by the lane implementer). `internal/core/mdrecord` is the tree's one notion of fences and HTML comments: `Mask` and `Unclosed` follow CommonMark for a top-level fence, and `Read(lines, rule)` is the single pass every reading goes through. It is extended once, by a `Rule`: `TopLevel` is CommonMark's top level, and `ListNested` also opens a fence indented four or more columns, which only a list item can hold, closes it within three columns of its opener, and ends it at a column-0 line (fenced code never continues lazily). A run at one to three columns is read by `ListNested` exactly as `TopLevel` reads it, because whether it sits in a list item turns on the list around it and mdrecord does not parse lists; a committed issue record quotes a frontmatter block in a top-level fence whose indented closer is followed by a column-0 line, and the list-item reading refused it. That reading is kept unexported and consulted by `FencedUnderEveryRule`, which masks a line only where every reading agrees. The site reads `ListNested` (it renders fences in list items) with comments masked and refuses an unclosed fence or comment; the lint rules read `FencedUnderEveryRule` with comments left visible, because a mask that hides prose misses a finding silently. The reading exclusion floor fails closed: its verifier does not share the redactor's site walk, reads a line as code only where every reading agrees, lets no comment hide a heading (comment text travels in the bundle, so an excluded heading inside a comment is refused, not redacted by guess), and refuses a fence either rule leaves unclosed. `TestNoSecondFenceRule` in mdrecord is the armed guard: it names any non-test Go file that writes a fence delimiter and keeps state across lines, its allowlist is empty, and a stale entry fails it. Captures resolved on this ruling: iss-2609250955207041, iss-2609250955051598, iss-2609250955209513, iss-2609251044055902, iss-2609251045302499.
2026-09-25 — One fence rule for the tree (the run A orchestrator's technical ruling for the fences lane, recorded by the lane implementer). internal/core/mdrecord is the tree's one notion of fences and HTML comments: Mask and Unclosed follow CommonMark for a top-level fence, and Read(lines, rule) is the single pass every reading goes through. It is extended once, by a Rule: TopLevel is CommonMark's top level, and ListNested also opens a fence indented four or more columns, which only a list item can hold, closes it within three columns of its opener, and ends it at a column-0 line (fenced code never continues lazily). A run at one to three columns is read by ListNested exactly as TopLevel reads it, because whether it sits in a list item turns on the list around it and mdrecord does not parse lists; a committed issue record quotes a frontmatter block in a top-level fence whose indented closer is followed by a column-0 line, and the list-item reading refused it. That reading is kept unexported and consulted by FencedUnderEveryRule, which masks a line only where every reading agrees. The site reads ListNested (it renders fences in list items) with comments masked and refuses an unclosed fence or comment; the lint rules read FencedUnderEveryRule with comments left visible, because a mask that hides prose misses a finding silently. The reading exclusion floor fails closed: its verifier does not share the redactor's site walk, reads a line as code only where every reading agrees, lets no comment hide a heading (comment text travels in the bundle, so an excluded heading inside a comment is refused, not redacted by guess), and refuses a fence either rule leaves unclosed. TestNoSecondFenceRule in mdrecord is the armed guard: it names any non-test Go file that writes a fence delimiter and keeps state across lines, its allowlist is empty, and a stale entry fails it. Captures resolved on this ruling: iss-2609250955207041, iss-2609250955051598, iss-2609250955209513, iss-2609251044055902, iss-2609251045302499.
Frontmatter
| id | dcn-2609250000000001 |
|---|---|
| type | decision |
| date | 2026-09-25 |
.abcd/work/decisions/dcn-2609250000000001-one-fence-rule-for-the-tree-the-run-a.md
open on GitHub ↗ · commit history ↗
mentions5
- iss-2609250955051598internal/core/site keeps a private fence toggle (sections.go isFenceLine, used by Sections, Blocks, and compose.go auditIsMet and releaseOf) that diverges from mdrecord.Mask, the tree's CommonMark reading, and returns wrong sections. isFenceLine matches any line whose trimmed text starts with three backticks and flips a boolean. So it never sees a tilde fence, closes a four-backtick fence on a three-backtick line, closes on a line that carries an info string, and cannot see an HTML comment. Probe at 70daf701 via Sections: a # line inside a ~~~ block becomes a section, and so does a # line inside a three-backtick block quoted in a four-backtick fence (both give sections Doc, a shell comment, Real). A heading parked in <!-- --> becomes the section Parked. An unclosed ~~~ fence is not refused, even though Sections refuses an unclosed backtick fence as its quietest failure. The same walk decides auditIsMet (a ~~~-fenced Acceptance rollup: MET 1 counts as a verdict, the failure iss-2609090951277880 closed for backtick fences) and releaseOf (a ~~~-fenced credit stamps a version). The fix is not a drop-in swap for mdrecord.Mask, because the site deliberately reads fences indented under list items and mdrecord recognises only the 0-3 space indent, so the indent rule needs a decision first.
- iss-2609250955207041The cold-reading exclusion floor can be walked through by a fence shape both of its halves misread the same way, so an excluded heading's section travels in the bundle and nothing refuses. redactExcluded finds headings through site.Sections, whose private fence toggle (iss-2609250955051598) flips a boolean on every line that starts with three backticks. verifyRedaction re-checks with bodyFenceMask, a second private backtick-only toggle, so the two agree on the wrong answer. Probe at 70daf701: a document holding a four-backtick fence that quotes a bare three-backtick line, then '## Private Notes' with a body line, then a ~~~ block holding a three-backtick line. redactExcluded with the heading Private Notes excluded returns nil error and the body line still in the output. Under CommonMark (mdrecord.Mask) the heading is live. The four-backtick block is one fence, and the three-backtick line inside the tilde block is literal. So the manifest asserts a refusal that did not happen, the silent-bypass class iss-2608301350533102 closed for a delimiter inside the frontmatter. The fix is not a one-line swap. Moving the verifier onto mdrecord.Mask would make it refuse this shape, but mdrecord also masks HTML comments, and comment text travels to the reader, so the floor's view of comments needs a decision.
- iss-2609250955209513internal/core/lint fenceMask (lint.go), which about a dozen rules use to skip example text (citations, persona, context currency, delivery state, cross-store, subverbs and the rules in lint.go), is a private backtick-only toggle that diverges from mdrecord.Mask. It flips on any line whose trimmed text starts with three backticks. So a tilde-fenced example is linted as live prose, which gives false findings. And a three-backtick line inside a ~~~ block flips the mask on, so the live prose after the block is masked until the next backtick line, and a real finding there is silently missed. Probe at 70daf701: in lines [~~~, three-backtick-go, ~~~, blank, LIVE PROSE LINE], fenceMask marks the live line masked, and in [~~~, example prose, ~~~] it marks the example unmasked. Moving onto mdrecord.Mask is not a mechanical swap. mdrecord also masks HTML comments, so every rule would stop reading commented text, and it recognises only the 0-3 space fence indent. Both are per-rule decisions.
- iss-2609251044055902Three more private fence toggles diverge from mdrecord, the tree's CommonMark fence rule, and each gives a wrong result on a probe. site parseCLIReference (check.go) flips on any line starting with three backticks, so a flag in a tilde fence or in a four-backtick fence quoting a three-backtick line is never credited and the reference check reports drift that is not there. surface markerLines (appendix.go) flips on either a backtick or a tilde line, so a backtick line inside a tilde fence leaves the toggle on and a live appendix marker after the fence is refused as ErrMarkerInFence. memory fenceMask (coverage.go) closes a fence on a run indented four or more columns, which CommonMark reads as content, so a blockquote example inside the fence becomes a quoted span and the live quote after it is masked when the real closer reopens the toggle. Found by the TestNoSecondFenceRule detector ported onto mdrecord.
- iss-2609251045302499surface ProseShapeClaims (appendix.go) decides whether a sub-verb path is written as an invocation through codeRegions, which pairs backtick runs and so covers a backtick fence but never a tilde fence. A sub-verb path inside a tilde-fenced example, written without an abcd prefix, is not a claim, so a stale sub-verb there passes the brief surface drift check silently. Probe: prose '~~~ / capture list / ~~~' against a tree registering 'abcd capture list' yields no claim. Found in the fence sweep beside TestNoSecondFenceRule, which does not flag it because codeRegions keeps no per-line state.