Skip to content

docs: Phase 7 — language tags, pagelint --fix, and the acceptance pass (spec 011 complete) - #76

Merged
iancooper merged 3 commits into
masterfrom
docs/spec-011-phase-7
Aug 6, 2026
Merged

docs: Phase 7 — language tags, pagelint --fix, and the acceptance pass (spec 011 complete)#76
iancooper merged 3 commits into
masterfrom
docs/spec-011-phase-7

Conversation

@iancooper

Copy link
Copy Markdown
Member

Closes out Spec 011. Three commits, one per task; 43 of 43 tasks done.

Task 7.2 — language tags on the last 34 fences, rule 4 to a repo-wide error

The backfill and the rule that stops it recurring, in one commit — tagging without
enforcing leaves the tags to decay like every other unenforced convention here, which
is what the audit measured in the first place.

Not one of the 34 was code: 1 bash block (two dotnet add package lines) and 33
text — ASCII flow diagrams, directory trees, validation-message dumps, trace output.
That is the explanation for the debt rather than a curiosity. An author writing C#
reaches for ```csharp because they want the highlighting; an author drawing a
box-and-arrow diagram has no language in mind and types a bare fence. The untagged
population was never latent code debt — it was the absence of a name for "not code".

Corroborated by a number that did not move: the using-directive debt is 802 blocks
across 93 pages before and after. Tagging any of the 34 csharp would have added it to
rule 6's population.

pagelint.py goes 836 warnings → 802, and 802 is the using debt entire. Rule 4 was
proved red before it was trusted green: retagging a fence back to bare gives
1 errors and exit 1.

Task 7.3 — pagelint.py --fix

A version bump is 110 banners, and retyping one segment 110 times gets abandoned
half-done. Rehearsed end to end with APPLIES_TO temporarily at V11/V5: 110 stale,
110 fixed, 0 left for a human. Page types survived exactly (50/33/27), all five
Prerequisites segments survived — checked first, because that is the failure
apply_banners.py actually shipped — one line changed per page, and the 17 files
without a trailing newline still have none. Second run is a no-op.

There is no migration map. A stale value names a set of products and the fix is
whichever APPLIES_TO entry names exactly that set, so one edit to the tuple is the
whole bump.

It never decides a page type. Given **Guide** · Applies to **Brighter V9** it
fixes the version, leaves the type, and the page still fails rule 2 — --fix cannot
launder a bad page type into a green build.

The language-tag half had ground truth: the 34 fences 7.2 tagged by hand. Replayed
against that tree it tags 26 and holds 8, and all 26 match the hand verdict. No
wrong tag; the only failure mode is doing nothing and saying so.

Task 7.4 — acceptance pass, AC1–AC8

Seven passed as they stood. AC5 failed and is fixed: the linter can emit eight rule
labels and the ledger listed seven — NO H1 was never written down. Nothing was broken
by it, because every page has an H1 and the rule has never fired, which is exactly why
it survived four sessions of the ledger being read and edited. A rule that never fires
is invisible to everything but an enumeration.

A second drift of the same shape turned up while checking AC6: docs.yml's comment
still said untagged fences stay warnings.

Two checks that had never been run, both of which could have been false silently: every
page's banner type matches its reviewed verdict in pagetypes.tsv (110/110), and every
page's Applies to matches the TSV's applies column (110/110, 100/5/5). AC4 was
re-derived without the linter — 688 distinct non-navigation ## slugs, 0 repeated.

State

pagelint.py 0 errors / 802 warnings across 110 pages; linkcheck.py clean at 112
files; --changed origin/master green and non-vacuous. The remaining 802-block
using-directive debt is deliberate and is AC1's baseline to shrink.

🤖 Generated with Claude Code

https://claude.ai/code/session_012tcdwxVb8NmKaX2S6fvyFg

iancooper and others added 3 commits August 5, 2026 15:15
…11, Task 7.2)

The backfill and the rule that stops it recurring, in one commit. Tagging without
enforcing leaves the tags to decay like every other unenforced convention here,
which is what the audit measured in the first place.

Not one of the 34 was code: 1 bash block (two `dotnet add package` lines) and 33
`text` — ASCII flow diagrams, directory trees, validation-message dumps and trace
output. That is the explanation for the debt rather than a curiosity. An author
writing C# reaches for ```csharp because they want the highlighting; an author
drawing a box-and-arrow diagram has no language in mind and types a bare fence.
The untagged population was never latent code debt; it was the absence of a name
for "not code".

Corroborated by a number that did not move: the using-directive debt is 802 blocks
across 93 pages before and after. Tagging any of the 34 `csharp` would have added
it to rule 6's population.

pagelint.py goes 836 warnings to 802, and 802 is the using debt entire — the split
recorded before this change holds exactly. Rule 4 was proved red before it was
trusted green: retagging Telemetry.md:207 back to a bare fence gives 1 error and
exit 1, printed without the (warning) label. --changed origin/master is green and
non-vacuous at 15 files / 38 hunks.

Rule 4 needs no --changed softening, unlike rule 6: it has no standing debt, so an
untagged fence today is one added today. CLAUDE.md's ledger row moves to `error` in
both columns, keeping the two-way parity AC5 checks.

The 149 space-separated fences (``` csharp) are left alone. They render as C# and
rule 4 has never flagged them; normalising them would touch 40-odd pages to change
nothing visible and bury a diff whose whole claim is that it is nothing but tags.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012tcdwxVb8NmKaX2S6fvyFg
… 011, Task 7.3)

A version bump is 110 banners. Retyping one segment across 110 pages gets
abandoned half-done, and a half-bumped corpus is worse than an un-bumped one:
some pages assert the new version, some the old, and nothing distinguishes a page
that was considered from one that was missed.

Rehearsed end to end rather than assumed. With APPLIES_TO temporarily moved to
V11/V5: 110 stale, 110 fixed, 0 left for a human — 100 Brighter, 5 Darker, 5 both,
reconciling with the 10 Darker-touching pages already in pagetypes.tsv. Page types
survived exactly (50/33/27). All five Prerequisites segments survived, checked
first because that is the failure apply_banners.py actually shipped in 5498cd6.
One line changed per page, 110 files at 1 insertion 1 deletion. The 17 files with
no trailing newline still have none, byte-compared against HEAD. Second run is a
no-op.

No migration map exists. A stale value names a set of products and the fix is
whichever APPLIES_TO entry names exactly that set, so one edit to the tuple is the
whole bump. Two entries claiming one set is a restructure, not a bump, and it
refuses.

It never decides a page type. Given `**Guide** · Applies to **Brighter V9**` it
fixes the version, leaves the type, and the page still fails rule 2 — --fix cannot
launder a bad page type into a green build. Only the version segment is
substituted, so the type and Prerequisites are out of reach by construction.

The language-tag half had ground truth: the 34 fences Task 7.2 tagged by hand.
Replayed against that tree it tags 26 and holds 8, and all 26 match the hand
verdict. No wrong tag; the only failure mode is doing nothing and saying so. One
of the 8 is exactly right — AsyncAPISupport's `dotnet add package`, the one block
that took bash. The rest are over-caution, mostly `key: value` firing on labelled
output. `text` is the only inferable tag because all 34 untagged fences were
prose; picking between csharp, bash, json and yaml belongs to the author.

Kept out on purpose: apply_banners.py's TSV lookup (one-off migration logic keyed
to a file recording years-old decisions), rule 6 (writing `// ...` for a reader
converts a debt into a declaration nobody made), and --fix --changed, which is
rejected with exit 2 because --changed only varies the strictness of a rule --fix
does not repair.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012tcdwxVb8NmKaX2S6fvyFg
Eight criteria walked, each verified by running something rather than by
recalling what an earlier session reported. Seven passed; AC5 failed and is fixed.

AC5 asks for CLAUDE.md <-> linter parity in both directions. pagelint.py can emit
eight rule labels and the ledger listed seven: NO H1, returned by check_banner
when a page has no title to hang a banner below, was never written down. It is
rule 1's precondition rather than a rule of its own, which is presumably why —
but the ledger's own claim is that every rule maps back, and this one did not.
Nothing was broken by it, because every page has an H1 and the rule has never
fired, which is exactly why it survived four sessions of the ledger being read
and edited. A rule that never fires is invisible to everything but an enumeration.

A second drift of the same shape, found while checking AC6: docs.yml's comment
still said untagged fences stay warnings on the repo-wide step. Task 7.2 made
them an error two commits ago and updated CLAUDE.md and the docstring, but not
the workflow comment — changed where it was enforced, not everywhere it was
described.

Two checks that had never been run, both of which could have been false silently:
every page's banner type matches its reviewed verdict in pagetypes.tsv (110/110),
and every page's Applies to matches the TSV's applies column (110/110, tallying
100/5/5), so the 10 Darker-touching pages really are the set that column names and
the next Darker release is the one-edit bump it was positioned to be.

AC4 re-derived without the linter: an independent fence-aware pass finds 688
distinct non-navigation ## slugs and 0 on more than one page. Confirming rule 3a
with the tool that enforces rule 3a would only have proved self-consistency.

AC1's baseline for whoever shrinks it: 802 C# blocks across 93 pages with no
using directives, left standing deliberately. AC7's redirects stated plainly
rather than ticked — no URL moved, so there is nothing to redirect from; the 28
anchor links the splits broke were repointed directly in Phase 6, because GitBook
redirects operate on pages, not fragments.

43 of 43 tasks. Spec 011 is complete and Spec 010 has been unblocked since 7.1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012tcdwxVb8NmKaX2S6fvyFg
@iancooper
iancooper merged commit 4001b0a into master Aug 6, 2026
6 checks passed
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