Skip to content

Release: v4.6.0 — prose-staleness checks, hardened same day - #336

Merged
Data-Wise merged 24 commits into
mainfrom
dev
Aug 16, 2026
Merged

Release: v4.6.0 — prose-staleness checks, hardened same day#336
Data-Wise merged 24 commits into
mainfrom
dev

Conversation

@Data-Wise

Copy link
Copy Markdown
Owner

Summary

Test plan

  • Full suite: 2491 passed / 19 failed — all 19 failures in test_branch_guard_dogfood.py, pre-existing/environmental (installed hook symlinks to the sibling cc-config repo, out of scope per cross-repo permission boundary — same documented issue as v4.5.0's own release notes). Not caused by this release's diff.
  • pre-release-check.sh 4.6.0 — PASSED, all 10 checks green
  • docs-staleness-check.sh — GREEN (0 issues), including a live catch-and-fix mid-release: bump-version.sh left docs/REFCARD.md's embedded date/headline stale (still describing v4.5.0), fixed and re-verified GREEN
  • markdownlint — 0 errors across all changed files
  • ./scripts/bump-version.sh --verify — ALL CONSISTENT

🤖 Generated with Claude Code

Data-Wise and others added 24 commits August 7, 2026 21:56
Records the v4.5.0 release (repo-triage skill, 8 ecosystem tutorials,
nav fix) in .STATUS milestone narrative; archives the two completed
specs that drove this release.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Closes the one content gap left after the v4.5.0 docs sync: repo-triage
had SKILL.md/REFCARD/getting-started coverage but no task-focused
cookbook recipe, unlike triage-a-red-pr's equivalent workflow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ncy gate)

REFCARD.md's quick-reference box had the right version NUMBER (4.5.0,
bump-version.sh's literal-string target) but stale content around it:
release date (2026-07-17, actually v4.1.0's) and highlight line (still
describing v4.1.0's /craft:restore, not v4.5.0's repo-triage skill) —
same bump-version.sh blind-spot pattern as the docs/index.md fix earlier
this release. Skill count also read 40, not 41.

skills-agents.md's TL;DR line said "8 specialized agents" two lines
above its own correct "2 specialized agents" — stale since the v4.0.0
folio split (8→2 agents), never caught because count-consistency only
anchors on specific badge/count patterns, not every prose mention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Captures today's REFCARD.md/skills-agents.md incident (correct version
numbers, stale surrounding prose) as 3 concrete Phase 7 checks to add
to docs-staleness-check.sh: release-date claims, agent/skill count
prose, and version-highlight staleness proxy. External research
(fiberplane/drift, Vale, dosu.dev freshness scoring) confirmed no
existing tool fits craft's count/version-anchored staleness model —
extending the existing gate with targeted patterns, no new dependency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…check 2)

3 blocking findings from adversarial review, all fixed:
- Check 3 (version-highlight staleness proxy) was falsified by its own
  target: bump-version.sh touches REFCARD.md's version line every
  release, so the whole-file git-log-touch proxy would never flag the
  exact file that caused this SPEC to exist. Dropped rather than built
  as a per-line proxy — not worth the complexity for an advisory-only
  signal until checks 1-2 prove insufficient alone.
- Check 2 (agent/skill count prose) false-positived on 90+ legitimate
  matches (orchestration mode-limit prose, a fictional-plugin tutorial,
  an intentional-bug troubleshooting example) when scoped as a blanket
  regex. Rescoped to structured line shapes only (TL;DR lines, version
  boxes, badge/count-summary lines).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Quick Start only documented the dev-only local-plugins symlink path,
with no real install instructions for external users. Added the two
actual install paths (Homebrew, Data-Wise marketplace) and moved the
symlink instructions under a "developing craft itself" note.

Also fixed 5 stale "47 commands" mentions (current: 48) scattered
through prose/callouts — the exact class of bug docs-staleness-check.sh
doesn't catch (free prose, not a structured badge/count line); see
SPEC-doc-staleness-prose-gaps-2026-08-07.md for the planned gate fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ly edits

homebrew-tap#211 (craft's stale unprotect-caveat fix) tripped the
Formula Drift Guard CI check on first push — content changed with no
version/revision bump, so brew upgrade would never have detected it.
Documents the rule in /craft:dist:homebrew and the release skill's
homebrew reference so it doesn't have to be rediscovered by trial and
error next time a tap formula gets a content-only fix outside a
version release.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Same blind spot as this SPEC's craft-side checks (fixed number,
stale surrounding prose) exists in homebrew-tap's manifest.json
caveats_extra text — confirmed live via the /craft:git:unprotect
caveat bug fixed in homebrew-tap#211. Out of scope here (D2: craft
only) — recorded so it isn't silently forgotten.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Records this session's continuation work: 3 stale-prose doc bugs found
and fixed, a cookbook recipe added, SPEC-doc-staleness-prose-gaps
brainstormed/spec'd/adversarially reviewed (not yet built), and
homebrew-tap#211 merged (stale caveat text + required revision bump).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New OpenCode MCP bridge cheat sheet (opencode + opencode-async), routing
table and mandatory-monitor guidance in both delegation tutorials, and a
brainstorm -> dual-review -> grill -> spec chain scoping delegation to two
use cases (arch:review adversarial review, brainstorm research escalation)
with the rest parked. Both docs were adversarially reviewed by codex and
opencode/deepseek-v4-pro themselves before this commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The 📚 Teaching mkdocs nav section, README's Teaching Mode section, and
the underlying commands/utils/{teach_config,teaching_validation,
semester_progress}.py + utils/detect_teaching_mode.py all documented
/craft:site:{progress,validate,publish} — deleted in the v4 command-prune
(commands/site/ now has only deploy.md). A prior SPEC's "keep the utils,
they're consumed by 4 files" rationale is dead: all 4 consuming files are
gone. Confirmed via background audit this session; verdict was REMOVE.

Removed: 7 docs/teaching-* pages, 4 dead utils, 10 dead test files
(including tests testing modules that no longer import), 1 pytest marker,
1 test.md domain row. Fixed 6 resulting dead links across REFCARD.md,
tutorials/index.md, getting-started/choose-path.md, guide/
claude-code-2.1-integration.md, guide/integration-testing.md (trimmed its
whole "Category 3: Teaching Workflow" section + renumbered), and
cookbook/common/deploy-course-website.md (kept — it's actually about
folio's live site:build/check commands, only its teaching cross-links
were dead).

Verified: full suite 2462 passed / 20 failed (both failure classes
pre-existing and unrelated — test_branch_guard_dogfood.py's documented
cross-repo symlink baseline, and a pre-existing broken link in
REVIEW-repo-triage-2026-08-07.md dated 2026-08-07, untouched tonight).
markdownlint + mkdocs build --strict clean. Command/skill/agent counts
unaffected (48/41/2) — teaching was never a real command/skill/agent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bumps the mcp-dev-dependencies group in /mcp with 2 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [esbuild](https://github.com/evanw/esbuild).


Updates `@types/node` from 26.1.2 to 26.2.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `esbuild` from 0.28.1 to 0.28.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](evanw/esbuild@v0.28.1...v0.28.2)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: mcp-dev-dependencies
- dependency-name: esbuild
  dependency-version: 0.28.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: mcp-dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
CLAUDE.md's Project Structure table still claimed 8 agent definitions;
the real count has been 2 since v4.0.0. The row sits outside the
bump-version.sh count cascade, whose CLAUDE.md pattern only matches the
bold "**N agents**" form, so it survived every version bump since.

README.md's tagline claimed "1994 tests passing" — stale, unguarded by
any validator, and unbackable next to a suite with known pre-existing
failures. Dropped the hard number rather than substituting another one
that will drift the same way.

Verified: docs-staleness-check.sh GREEN, validate-counts.sh 4/4,
markdownlint 0 errors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… harness

README's highlight block still advertised v2.36.0's Deterministic Workflow
Engine five minors after v4.5.0 shipped. Replaced with the v4.5.0 headline
from docs/NEWS.md (repo-triage skill + 8 ecosystem-tool tutorials).

SPEC-doc-staleness-prose-gaps amended with:

- Amendment section recording two live findings from a currency read on
  7db7b56. E1: Phase 7's scan pattern is strictly plural, so CLAUDE.md's
  "8 agent definitions" went unseen for five minors -- the singular form
  must fold into the shared matcher, not sit on top of it. E2: NEWS.md and
  .STATUS disagree on the v4.5.0 release date because the GitHub release
  crossed the UTC boundary, so check 1 needs a single authority plus a
  one-day window or it flags a correct repo on first run.
- ADR-007 requirement: records the commitment to pattern-scoped prose
  gating over Vale/semantic/LLM-in-CI, the rejected whole-file git-touch
  proxy and why it was falsified, the severity posture, and the revisit
  trigger.
- Test-harness design: 9 fixtures across clean/defect/falsepos, a
  table-driven runner asserting on --json output, and a rule that a check
  without a defect fixture is a rejected change.

Acceptance criteria extended by 5 items to cover all of the above.

Verified: markdownlint 0 errors, docs-staleness-check.sh GREEN.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…aps live

The repo-triage row still read DISPATCHED 2026-08-07 after that work shipped
in v4.5.0 and its worktree was removed. Marked merged with its PR links and
added the feature/doc-staleness-prose-gaps worktree created this session.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… lines (#334)

* feat(docs-staleness): prose checks for release dates and shaped count lines

Implements SPEC-doc-staleness-prose-gaps-2026-08-07 and records the approach
as ADR-007.

Phase 7 anchored on structured tokens only, so four real bugs shipped while it
reported GREEN: REFCARD.md's wrong release date beside a right version number,
skills-agents.md's "8 specialized agents" two lines above its own correct "2",
CLAUDE.md's "8 agent definitions" for five minors, and README.md headlining
v2.36.0 at v4.5.0.

Two checks, both severity warning:

- Release-date claims within 4 lines of the current version token must fall
  within a day of that version's git tag date. The window is not slack: the
  GitHub release for v4.5.0 published 2026-08-08T03:44Z against a 2026-08-07
  tag, so a stricter check would flag a correct repo on its first run.
- Count prose is checked inside four structured line shapes only (version box,
  TL;DR opener, bolded count-summary badge, structure-table row), where the
  singular noun form is safe to match. The broad scan stays plural-only and
  unscoped -- widening it would reopen the 90+ false positives the adversarial
  review closed.

Harness: 11 fixtures across clean / planted-defect / known-false-positive, plus
a table-driven runner asserting on --json findings. Positive controls verified
by mutation rather than assumed -- removing the 40% floor fails 3 tests,
dropping the singular alternation fails exactly the E1 defect test.

Three findings from the build itself, all folded back into the SPEC:

- The 40%-of-expected floor must apply to shaped lines too. Without it the
  first build flagged five legitimate counts on dev: category subtotals in
  reference boxes, a bolded subset count, a narrative count about another
  plugin. All five are now falsepos fixtures.
- Matching TL;DR anywhere on a line flagged ADR-007's own context table, which
  quotes the line the ADR exists to explain. The shape now requires the line to
  open with TL;DR.
- The first build ran the script at 36s against a dev baseline of 8.0s, blowing
  the 30s timeout in test_pre_release_check_runs. Cause was ~1300 per-file awk
  spawns plus ~21000 greps over every line of every box block. Now one awk pass
  over the file list, an in-awk pre-filter so only count-bearing lines reach
  bash, and the date window computed once. 8.7s.

Full suite in-tree: 1 failed / 2460 passed / 49 skipped. The one failure is a
pre-existing broken link in REVIEW-repo-triage-2026-08-07.md, reproduced on the
dev baseline (20 failed / 2462 passed; dev's 19 branch-guard failures are skips
on a feature branch). docs-staleness GREEN, validate-counts 4/4, markdownlint
0 errors, mkdocs build --strict clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(docs-staleness): unparseable release-date authority must be vacuous

Self-review of #334 found it. compute_release_date_window exits silently when
the tag date will not parse, leaving an empty accept-window -- and an empty
window matches nothing, so every release-date claim in the repo was flagged at
once. One bad input became a repo-wide false-positive storm.

The no-tag case was already handled (skip); an unparseable tag was not. Both now
take the same path: the check is vacuous unless the authority actually parsed.

Regression test uses the clean/ fixture, which is correct against a real tag
date, so any finding it sees comes from the broken authority alone. Positive
control verified by reverting the guard: the test fails with
"release date '2026-08-08' for v4.5.0 (tag: not-a-date)".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(specs): repoint REVIEW-repo-triage at the archived GRILL

b1c4426 archived GRILL-repo-triage-2026-08-07.md to docs/specs/_archive/
without updating its one referrer, so test_no_broken_links has failed on every
branch since. It is the sole failure in the dev baseline and the only thing
red on this PR.

Unrelated to this branch's subject, fixed here because a dev-side fix would not
reach this PR's CI run. The other file archived in the same commit
(SPEC-ecosystem-tool-tutorials) has no referrers -- checked, nothing to fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(docs-staleness): release-date window was one line short of its contract

Found by /code-review on #334, confirmed empirically before acting.

The win-- decrement runs on the version-mention line itself, so win = 4 scanned
that line plus only 3 more. Both the inline comment and
docs/reference/REFCARD-DOCS-STALENESS.md advertise 4 following lines, and the
gap silently reads GREEN rather than flagging -- a layout as ordinary as
heading / blank / Type / blank / Released fell straight through it.

win = 5 restores the documented window. New defect fixture places a stale date
at exactly that far edge; positive control verified by reverting to 4, which
fails only that case:

  FAILED test_prose_staleness_fixture[defect-release-date-far-edge]
    - planted defect went undetected
  1 failed, 14 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(docs-staleness): pass 2 [f]ix applied nothing while reporting success

pass2_interactive_review's [f]ix branch printed "-> Fixed" and incremented
TOTAL_FIXED without touching the file. It is the same
reports-success-changes-nothing bug pass 1 was fixed for earlier (see the
BSD-sed note now on apply_line_fix), left behind in the other pass.

Extracted pass 1's python applier into apply_line_fix and pointed both passes
at it, so the two cannot drift apart again. It echoes "true" only when the file
actually changed, and "false" for a non-substitution fix_detail, a pattern that
matches nothing, or an out-of-range line -- Phase 8's doc-coverage findings
carry a `doc-coverage:surface:cmd` marker rather than a substitution and must
never be executed.

Both prose checks now emit a real substitution as their fix_detail instead of a
human-readable "old -> new" note, so confirming one in pass 2 actually edits the
file. They stay routed to pass 2 rather than pass 1: the surrounding prose is
hand-authored, so a human should see the line before the number changes under
it. The substitution swaps only the digits, leaving "agent definitions" intact.

Two tests pin the applier's contract, including that a "false" result leaves the
file byte-identical. Positive control verified by mutating apply_line_fix to
report success without writing -- the truthfulness test fails.

The pass 2 branch itself is not directly covered: it requires a TTY. Its
8 lines of fix_detail parsing mirror pass 1's.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: sync SPEC, ADR-007, REFCARD and changelogs with the four post-build fixes

All four planning/internal docs were written in bd95723 and none was updated
for the fixes that landed after it, so each described the first build rather
than what ships.

SPEC: new "Post-build findings" table recording all four bugs and, for each,
which mechanism caught it -- the existing suite's timeout, self-review,
/code-review, and reading adjacent code. No single review mechanism found more
than one, which is the point of recording it. Notes that two of the four are the
same failure mode (reporting a result never established) and that the pass-2 bug
is a recurrence of one pass 1 was already fixed for. Adds the fix-routing
decision, the 11th fixture, and 4 acceptance criteria.

ADR-007: two architectural rules the fixes established, both stated generally
for checks added later. A failed authority makes a check vacuous, never
universal -- absence of a usable comparison is not evidence of drift, and the
failure modes are not symmetric (a skipped check costs one missed bug; a check
that fires on every document costs the gate its credibility). And fixes must be
applied, not announced -- one shared applier, because patching the second site
would have left a third.

REFCARD: corrects the window to "the version line or the 4 lines below it",
documents the vacuous-on-unusable-authority rule, and records that choosing
[f]ix now really edits the file, including when it will decline.

CHANGELOG (both): the three script fixes plus the archived-GRILL link repoint.

Verified: markdownlint 0 errors, docs-staleness GREEN, validate-counts 4/4,
mkdocs build --strict clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(specs): spec the 9 prose-check defects found reviewing #334

Every finding reproduced against the real script before being written down --
F1/F2/F6 by constructing input, F5/F7 by reading the consuming code. Two are
verified as changing the design rather than just the code:

F5: ADR-007 justified `warning` over `error` by claiming both severities fail
the release gate identically. pre-release-check.sh:280 is headed "warn-only,
does not block release" and swallows the exit code, and docs-quality.yml sets
continue-on-error -- while skills/release/SKILL.md:143 says RED blocks and
YELLOW proceeds. The label is the only gate, so the severity choice IS the
block decision the ADR said it was not.

F7: docs-quality.yml checks out without fetch-tags, so no tag exists and check 1
silently skips on every CI run; and release writes the NEWS/REFCARD dates before
the tag is created. The check may only ever fire on a dev machine that already
pulled the tag.

Groups the nine into four root causes rather than listing them flat, because
patching nine sites individually leaves the classes open -- notably C3, where
the pass-2 [e] branch is the third instance of the same
reports-success-changes-nothing bug, introduced by #334's own change of the
finding's `file` field to path:lineno.

Nine decisions left open with tradeoffs rather than pre-resolved, for grilling.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(specs): grill the prose-check hardening spec — 5 branches locked

Two branches were reframed by evidence gathered during the grill, not argument.

D1 (authority): dropped the git tag for cross-file consistency. The tag is
absent in both environments the check exists to protect -- release runs Step 3b
(write doc dates) -> Step 3b.5 (staleness gate) -> Step 8 (create tag), so the
tag does not exist when the gate runs; and docs-quality.yml checks out without
fetch-tags. As built, check 1 fires only on a dev machine that already pulled
the tag. Comparing claims against each other needs no authority and works in
both places.

D2 (severity): split, check 1 RED and check 2 YELLOW. Verified that no consumer
reads the exit status -- pre-release-check.sh swallows it with || true,
docs-quality.yml sets continue-on-error -- while skills/release/SKILL.md:143
blocks on the RED label. The label is the only gate, so severity IS the block
decision ADR-007 claimed it was not. Severity now follows demonstrated
precision per-check rather than per-phase.

D3: fix the noun boundary, anchor the substitution on the full matched span,
and floor at max(2, 40%) -- all three, because F1's agent half is caused by the
floor being 0 for agents, and a prose-corrupting fix is worse than a false
warning.

D4: fix the [e]xclude no-op at the record level across Phase 7 and Phase 9.
Correction: this was relayed as a regression from #334 and is not -- dev already
has add_finding with "${file}:${lineno}" at 341, 497 and 548. Pre-existing, two
phases wide, and a live violation of the rule ADR-007 states.

D5: amend #334 rather than merge-then-harden. craft's docs are hyphen-heavy, so
shipping first means dev carries a --fix that can rewrite prose.

Spec updated where the grill overtook it: status, the C3 correction, and scope
(Phase 9 in, tag/fetch-tags out).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(plan): ORCHESTRATE for prose-check hardening + 6th grill branch

Routed by /craft:plan tier detection: SPEC present, GRILL present, no
ORCHESTRATE -> plan-orchestrator (tier 4). Worktree already exists, so no new
one was created.

Six phases mapped to the locked decisions, sequential, one commit each:
matcher/payload/floor (D3), check 1 to cross-file consistency (D1, D6), severity
split and the ADR correction (D2), exclusion round-trip at the record level
(D4), the two low findings plus doc sync, then verify and update PR #334 (D5).

Authoring the plan surfaced a gap the grill left: the spec listed 9 decisions
and the grill locked 5, so the release-date window discipline (spec D4 / finding
F3) was never resolved. D1 settled where the authority comes from, not how
claims are collected -- so prose like "Upgrading to v4.5.0 is a drop-in change"
would still open a window and turn a nearby unrelated date into a claim for that
version. The false positive survived the redesign in a new place.

Resolved as ledger D6: the window opens only when the version token appears in a
heading or a version-box line. Structural rather than numeric -- shrinking the
window narrows the class without closing it, and requiring version and date on
one line would miss the real NEWS.md layout the check exists for. D2 raises the
stakes, since check 1 is now RED and every surviving false positive blocks a
release.

Friction Prevention records the traps this work already hit once: the awk
pre-filter and bash matcher are two regexes expressing one rule and must move
together, and the 30s test_pre_release_check_runs budget is the only thing that
caught the last perf regression.

No implementation this session -- plan artifact only, per the confirm gate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(plan): grill the ORCHESTRATE — 5 plan-level branches, 4 found defects

Grilling the plan rather than the design. Four of the five branches found
something wrong with the plan; only one confirmed it.

D7 phase order: the record change moves to FIRST. As written, Phases 1-3
authored add_finding calls and tests against the old signature and Phase 4 then
rewrote all 12 call sites (Phase 6 x2, 7 x3, 8 x3, 9 x4) -- every prose finding
written twice, and the mechanical 12-site sweep landing last, where a missed
site is hardest to spot against three phases of other changes.

D8 fixtures: "delete ... or rewrite" narrowed to rewrite-only. D6 keeps a
window, so the off-by-one class is still live and
defect/release-date-far-edge.md is its only positive control -- the exact bug
/code-review caught. The plan permitted deleting it with an "or". Generalized
into Friction Prevention: never delete a positive control to make a redesign
easier.

D9 normalize the claim sites. Evidence gathered while grilling: craft has
exactly two release-date claims for the current version and they already
disagree -- NEWS.md 2026-08-08 (UTC) vs REFCARD.md 2026-08-07 (tag-local). Both
reachable under D6, so check 1 fires on craft today and only the one-day
tolerance keeps it GREEN. With D2 making it RED, the repo would sit permanently
one day from a blocked release with no prior warning.

D10: Phase 6 gains the .STATUS refresh and the at-merge ORCHESTRATE deletion.
Both required by craft's CLAUDE.md, neither enforced -- not gitignored, not in
exclusions.txt, no test.

D11: RED promotion is evidence-gated. Check 1 is redesigned and promoted to
release-blocking in the same PR, so it ships as warning and is promoted in
Phase 6 only after running clean over the real docs with the transcript quoted.
Does not reverse D2 -- makes RED earned rather than assumed.

Estimate corrected 2h -> ~3h; the original was wishful about Phase 1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e row

ORCHESTRATE-*.md files are feature-branch working artifacts, not dev content
(craft CLAUDE.md, grill decision D10). Nothing enforces this — the file is not
gitignored, not in exclusions.txt, and no test guards it.

.STATUS row flipped to MERGED and rewritten to say what actually shipped,
including what did not: a high-effort review found 9 further defects, 2 of them
HIGH, and the hardening is spec'd and grilled but not implemented. Recorded
plainly rather than left implicit, since check 2's --fix can corrupt prose until
that work lands.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Recreated rather than reused. After #334's squash-merge the old worktree's
branch no longer descended from dev, so `git diff origin/dev...HEAD` re-proposed
24 files / +1666 lines already merged, against 2 files of real difference --
the ahead/behind counts read "dev ahead 3, branch ahead 10" and meant nothing.
Continuing there would have made the next PR unreviewable. The branch name was
also wrong: doc-staleness-prose-gaps shipped, this is the hardening.

Deletion gated on the PR head matching the local tip (nothing added after the
merge) and the merge commit being an ancestor of dev, per the squash-merge
footgun -- `git cherry` misreports squash-merged branches as unmerged.

Nothing lost: SPEC and GRILL are on dev; the ORCHESTRATE was restored into the
new worktree from b5de1ae.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Full docs-staleness-check.sh (all 4 gating phases) and post-release-sweep.sh
already read GREEN/CLEAN. This is the advisory-only pass on top --audit-exclusions
plus real content review, per the project's doc-currency check.

REFCARD.md:9 claimed "2632+ passing" -- unguarded (no script owns this box
line; claude_md_sync.py only tracks CLAUDE.md's bold **Tests:** form), and
already wrong against the measured dev baseline (2462 passed / 20 failed). Same
class as the README fix earlier this session -- swapped the hard number for the
command that produces it, so it can't drift again. Also removed a duplicate
"All 1432 tests passing" claim further down the same file, equally unguarded.
Re-padded the box borders to the file's existing 63-char width.

Pruned 6 exclusions.txt entries, all dead for one of two reasons:
- docs/commands/orchestrate.md:4 agents -- file deleted (orch rename, v2.60.0)
- The other 5 sat under docs/cookbook/ or docs/examples/, both already
  whole-directory excluded (lines 27-28) -- the per-pattern entries were
  redundant from the start, and their target numbers have since drifted
  further (94 -> 115), so they matched nothing either way.

Verified: docs-staleness-check.sh GREEN (0 issues), --audit-exclusions "All
exclusions valid" (was 6 stale), validate-counts.sh 4/4, markdownlint 0 errors,
mkdocs build --strict clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
)

* fix(docs-staleness): carry finding location as its own field (D4/F4)

add_finding glued file+lineno into one string ("path:lineno"). Pass 2's
[e]xclude writes an entry against $file expecting a bare path, so every
Phase 7/9 exclusion request wrote a pattern that could never match
is_pattern_excluded's path:pattern parse -- it printed "Excluded" and the
finding came back on the next run. Predates PR #334; not a regression it
introduced (git show dev: confirms the same glued shape at lines 341/497/548
before this PR).

Splits the record into file + locator, threading the change through all
12 add_finding call sites, both consumers (pass1_auto_fix,
pass2_interactive_review), and findings_to_json -- JSON output shape is
unchanged (still emits "file":"path:locator" via a shared
finding_location() helper).

New test drives the real interactive [e] keystroke through a pty
(pexpect), not a Python reimplementation of pass 2's write logic --
verifies the exclusion actually suppresses the finding on a second run.

Phase 1 of 6 in the prose-check hardening (SPEC/GRILL-prose-check-
hardening-2026-08-15.md, decision D4). Adds the ORCHESTRATE plan file,
restored from the pre-merge worktree per handoff.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(docs-staleness): matcher boundary, span-anchored fix, real floor (D3/F1/F2/F6)

Check 2's noun trailer, `([^a-z]|$)`, treats `-` as a valid boundary
character, so hyphenated compounds read as counts: "30 command-line
entry points" matched as "30 command" (expected 48), "3 agent-facing
surfaces" as "3 agent" (expected 2). Replaced with PROSE_COUNT_TRAILER
(whitespace, closing punctuation, or end-of-line), shared by the awk
pre-filter and the bash matcher so the two can't drift apart again --
independent tuning of that pair already caused a 4x perf regression once.

Accepting the offered [f]ix on a genuine match corrupted the surrounding
prose: the substitution was built from a second, un-anchored "N noun"
regex with no trailer at all, so it could match a different (shorter)
span than detection did. Detection and the fix anchor now derive from
one captured span.

The 40%-of-expected floor is 0 for agents (`2 * 40 / 100 == 0`), so the
guard ADR-007 and two code comments cite as what makes shaped-line
counts safe did not exist for the smallest count type -- any "N agent(s)"
mention, however small, read as a stale-total candidate. Floored at
max(2, 40%).

New fixtures are positive controls, not just happy-path coverage: reverted
the source change and confirmed both new falsepos cases fail (docs/
architecture.md flags '30 command'/'3 agent'; docs/migration.md flags '1
agent') before restoring the fix. A new pty-driven [f]ix test asserts the
surrounding prose ("agent definitions") survives the substitution.

E1 pin (defect/structure-table-singular.md, clean/structure-table-
correct.md) and the release-date off-by-one guard
(defect/release-date-far-edge.md) both still pass unchanged. 21/21 tests,
~10s raw script run (well under the 30s pre-release-check timeout).

Phase 2 of 6 in the prose-check hardening (SPEC/GRILL-prose-check-
hardening-2026-08-15.md, decision D3).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(docs-staleness): check 1 -> cross-file consistency, heading-scoped window (D1/D6/F3/F7)

Check 1 compared every release-date claim against the current version's git
tag, but the tag doesn't exist at either point this check actually runs: the
release pipeline writes NEWS/REFCARD dates in Step 3b, before Step 8 creates
the tag, and docs-quality.yml's checkout has no fetch-tags. As built, the
check only ever fired on a developer machine that had already pulled a tag
that isn't the one being released -- not a gate anywhere it runs (F7).

Dropped the external authority. Every release-date claim for the current
version is now compared against every other one via a new majority_date()
helper: most claims wins, ties break to the LATER date (a stale-date bug is
a forgotten update, so the wrong claim is normally older than the correct
one, never newer -- with the common case of exactly two claims, NEWS.md and
a REFCARD-style box, every tie is 1-1, so this is what decides which one is
"authority"). The one-day tolerance survives as the agreement window between
claims, still absorbing the real UTC-boundary case (v4.5.0: NEWS.md
2026-08-08, REFCARD 2026-08-07). Vacuous with 0-1 claims, same posture as the
old missing-tag case -- accepted cost, cannot catch a date uniformly wrong in
every file.

Separately, the claim-collection window opened on ANY line containing the
version token, including running prose ("Upgrading to v4.5.0 is a drop-in
change") -- a nearby unrelated date then got collected as though it were
this version's release date (F3). Now requires the version token sit in a
markdown heading or a version-box content line (D6).

Test harness changed shape to match: check 1 needs a peer claim to agree or
disagree with, so its fixtures now pair via a new build_repo_multi() helper
instead of the single-fixture harness every other check uses. Reverted the
source change and confirmed the 4 new/changed release-date tests fail
against the pre-fix code (2 real assertion failures on the redesigned
checks, 2 "command not found" for the not-yet-existing majority_date) before
restoring the fix. 25/25 tests, ~12s raw script run.

Phase 3 of 6 in the prose-check hardening (SPEC/GRILL-prose-check-
hardening-2026-08-15.md, decisions D1 and D6).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(docs-staleness): severity split -- check 1 error, check 2 warning (D2/F5)

Check 1 (release-date consistency) now emits `error`; check 2 (count
prose) stays `warning`. Not cosmetic: pre-release-check.sh:280 runs this
whole script as `... || true`, discarding the exit code, and
docs-quality.yml sets continue-on-error -- no consumer reads the process
exit status. skills/release/SKILL.md:143 blocks on the RED label alone.
The severity string IS the block decision, not a secondary label on top
of one.

Check 1 earns error: after the D1 cross-file redesign it's near-binary,
two claims either agree or they don't. Check 2 stays warning: it matches
free-form prose patterns and produced three false-positive defects in
the PR that introduced it (a hyphenated-compound false match, a
zero-floor for the smallest count type, five sub-threshold counts caught
pre-merge) -- it hasn't earned a blocker yet.

Rewrote ADR-007's Severity section, which had asserted "both severities
already block the gate, so the label doesn't matter" -- checked during
this review and found false per the citations above. Removed the claim,
not softened it, per this branch's own rule (docs/adr's "reports success,
changes nothing" posture applies to a doc's claims as much as a script's
[f]ix). Also updated the ADR's authority description, still describing
release dates as checked against a git tag from before the D1 rewrite.

New test reverts the severity change and confirms it fails (check 1's
finding comes back "warning" instead of "error") before restoring the
fix. 26/26 tests.

Phase 4 of 6 in the prose-check hardening (SPEC/GRILL-prose-check-
hardening-2026-08-15.md, decision D2).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(docs-staleness): un-promote check 1 to warning, gate error on evidence (D11)

The previous commit shipped check 1 at severity error directly, skipping
D11's evidence gate: "Check 1 ships as warning in this phase. Promotion
to error happens in Phase 6, gated on evidence -- the check is being
redesigned and promoted in one PR, and the only evidence it is sound
would otherwise be tests written alongside it by the same author in the
same sitting." (ORCHESTRATE-prose-check-hardening.md Phase 4.)

Reverted check 1 to warning. Promotion to error belongs in Phase 6, after
a clean run across every tracked doc and both real claim sites
(docs/NEWS.md, docs/REFCARD.md) with the transcript quoted -- not on a
passing unit suite alone.

Rewrote ADR-007's Severity section to describe the gate rather than an
unconditional split: check 1 is *designed* to block (D2) but ships
warning until it earns error.

Replaced the severity test, which had pinned the premature promotion,
with two: one confirming both checks still ship warning pending the
gate, and a second (test_phase_status_goes_red_on_any_error_finding)
that pins the underlying mixed-severity mechanism directly against
phase_status_label -- an error finding among warnings goes RED, an
all-warning phase stays YELLOW -- independent of which check is promoted
when, since neither check emits error yet. 27/27 tests.

Still Phase 4 of 6 (SPEC/GRILL-prose-check-hardening-2026-08-15.md,
decisions D2 and D11) -- this corrects the previous commit rather than
starting Phase 5.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(docs-staleness): box-close discipline, doc/regex sync, date normalization (D9/F8/F9)

F8: an unclosed version-box (opening box character with no matching
closing one) leaked `inbox` mode into the rest of the file, so a later
structure-table row lost its own-type restriction. Closed at the FIRST
line carrying no box-drawing character at all, not only on an explicit
closing character.

Discovered while planting F8's fixture: the broad, unscoped plural-only
count scan shares check 2's F1 hyphen-boundary bug -- "7 agents-only"
matched as "7 agents" (expected 2), and this scan's findings are
auto-fixable, so it's worse than F2 (no human in the loop under `--fix
--non-interactive`). Not a separately enumerated finding; surfaced by
the hardening SPEC's own hyphenated-compound acceptance criterion
(command-line, agent-facing, skill-authoring, agents-only). Fixed with
the same PROSE_COUNT_TRAILER and span-anchored substitution as check 2.

F9: REFCARD-DOCS-STALENESS.md described `tldr` as "a line containing
TL;DR"; the regex requires the line to open with it (deliberate, so a
doc describing this bug isn't itself flagged for the example it quotes).
Fixed the doc.

D9: normalized the two real release-date claim sites. docs/NEWS.md's
v4.5.0 entry moved from 2026-08-08 (GitHub release UTC) to 2026-08-07
(tag-local); docs/REFCARD.md's box line gained the `v4.5.0` prefix it
was missing -- without it, the line sat 4 lines before the box's other
"v4.5.0" mention, so D6's forward-only window never reached it, and
REFCARD contributed zero claims regardless of date value. Both
CHANGELOGs' v4.5.0 headings synced to match. Recorded the convention in
skills/release/references/pipeline-steps.md so it doesn't drift again.
Verified live: injected a temporary mismatch into REFCARD.md's date and
confirmed check 1 caught it ("disagrees with other claims (majority:
2026-08-07)") before reverting the injection.

Added a "Superseded" pointer to the parent SPEC-doc-staleness-prose-gaps
-2026-08-07.md rather than rewriting its historical acceptance criteria,
and marked the hardening SPEC's own acceptance criteria against verified
evidence gathered across all five phases so far.

New/changed fixtures verified as positive controls by reverting each fix
and re-testing: falsepos/unclosed-box-leaks-into-table.md (uses the
singular noun form so it isolates the shape-classification bug from the
unrelated broad scan) and the broadened hyphenated-compound-tldr.md
(now covers skill-authoring, agents-only). 28/28 tests, ~11s raw script
run.

Phase 5 of 6 in the prose-check hardening (SPEC/GRILL-prose-check-
hardening-2026-08-15.md, decision D9).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(docs-staleness): Phase 6 verify + promote check 1 to error (D11)

Full-suite run surfaced 2 regressions not in the original F1-F9 list,
both introduced by earlier phases in this branch:

- PROSE_COUNT_TRAILER was missing `*` from its boundary-char class, so
  the broad plural-only count scan never matched markdown-bold badges
  like README's `**777 commands**` -- the char after "commands" is `*`,
  not whitespace/punctuation/EOL. Added `*` to the trailer and both
  noun-trim sed patterns that must stay in sync with it.
- bump-version.sh --verify's literal `Version: X.Y.Z` grep broke against
  Phase 5's REFCARD.md wording (`v4.5.0 (released ...)`, added to satisfy
  D6's window). Reworded to `Version: 4.5.0 (v4.5.0, released ...)`,
  satisfying both the grep and D6's same-line version-token requirement.

Both verified via revert-and-retest positive control before restoring.
Full suite: 2476 passed / 0 failed (dev baseline: 2462 passed / 20
failed).

D11 promotion gate cleared: docs-staleness-check.sh --json returned 0
count_consistency findings across every tracked doc, reaching both real
claim sites (docs/NEWS.md, docs/REFCARD.md). Live-fire transcript:
injecting 2020-01-01 into docs/REFCARD.md:7 produced "release date
'2020-01-01' for v4.5.0 disagrees with other claims (majority:
2026-08-07)", reverted after confirming. Check 1 promoted warning->error;
check 2 stays warning. ADR-007 and REFCARD-DOCS-STALENESS.md updated to
match (the latter also had stale pre-redesign prose describing the old
git-tag authority and the now-deleted CRAFT_RELEASE_DATE override).

.STATUS worktree row refreshed (D10); ORCHESTRATE Phase 6 + Acceptance
Criteria checkboxes marked with evidence.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* test(docs-staleness): drop dead CRAFT_RELEASE_DATE env var from fixtures

Flagged by an independent architecture review of PR #335: the only
consumer, resolve_release_date(), was deleted in this branch's Phase 3
redesign (check 1 moved from a single external authority to cross-file
consistency), but 3 test call sites still set the env var. Harmless
(unread by the script) but stale -- a future reader would assume it
still did something. FIXTURE_TAG_DATE itself stays; it still documents
the date fixtures are authored against (e.g.
clean/release-date-companion.md's literal "2026-08-07").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Consolidated the prose-check-hardening worktree row into a MERGED entry
(squash 7d79228) with the D11 promotion, arch-review, and mid-flight
rebase-conflict evidence; cross-referenced the superseded #334 row.
Also deletes ORCHESTRATE-prose-check-hardening.md, which the squash-merge
carried onto dev -- a feature-branch working artifact per craft's own
CLAUDE.md convention, not dev content.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Data-Wise
Data-Wise merged commit 2a3daa3 into main Aug 16, 2026
12 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