Skip to content

Release: v4.0.0 (folio split + v4 surface rider) - #294

Merged
Data-Wise merged 111 commits into
mainfrom
dev
Jul 16, 2026
Merged

Release: v4.0.0 (folio split + v4 surface rider)#294
Data-Wise merged 111 commits into
mainfrom
dev

Conversation

@Data-Wise

Copy link
Copy Markdown
Owner

Summary

Craft's biggest breaking release: the docs-authoring/publishing surface (24 commands, 6 agents, 6 skills) is extracted history-preserving into a new public plugin, Data-Wise/folio (released as folio v1.0.0), alongside a v4 command-roster consolidation.

Verification

  • python3 -m pytest tests/: 2637 passed, 0 failed
  • All 6 CI-required bash suites green (branch_guard_e2e's only failure is the documented runner-sensitive perf microbenchmark, confirmed via SKIP_PERF=1)
  • bash scripts/pre-release-check.sh 4.0.0: PASSED
  • bash scripts/doc-coverage-check.sh: PASSED (0 blocking gaps)
  • folio v1.0.0 already released and verified (brew install/brew audit clean) — this PR is the second half of the coordinated release train (folio-first, craft tag last, per the runbook)

Test plan

  • CI green on this PR
  • Tag v4.0.0 + GitHub release after merge
  • Homebrew formula regenerates via homebrew-release.yml

Data-Wise and others added 30 commits July 8, 2026 20:34
Bumps the mcp-dev-dependencies group in /mcp with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


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

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.1.1
  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>
…hestrate + reviews)

Full exploration→plan spine for shrinking craft from a 115-command monolith
into a native-first system (craft workflow core + folio docs/publishing):

- docs/ideas/craft-refactor-proposal.html — adversarial Reviews #1 & #2 +
  Proposal v2 (native-first, workflow-based breakup)
- docs/specs/GRILL-craft-refactor-2026-07-09.md — first-pass grill ledger (7 branches)
- docs/specs/SPEC-craft-native-first-breakup-2026-07-09.md — 14 locked decisions,
  4-phase plan, 4-axis verification gate
- docs/plans/ORCHESTRATE-craft-native-first-breakup.md — executable plan
  (ORCHESTRATE-only; no worktree, no code)

Planning artifacts only — no changes to craft's command/skill tree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
Commands with a stream redirect such as stderr-to-stdout passed the
coarse redirect guard (any non-caret char after the redirect symbol
qualifies, including an ampersand) but the fine extraction pattern
excluded the ampersand, so grep -oE matched nothing and exited 1.
Under this script's set -euo pipefail, a bare VAR=$(pipeline)
assignment propagates that as a script-ending failure -- silently, no
stderr, surfacing to users as 'PreToolUse:Bash hook error / No stderr
output' on completely benign commands piping cat/head output.

Appended '|| true' to all four BASH_TARGET extraction pipelines
(redirect, tee, cp, touch) so a genuine no-match behaves like the
empty-string fallback the existing -z BASH_TARGET checks already
handle, instead of killing the hook. Detection is unchanged -- a real
write-through target combined with a stderr redirect is still caught.

108/108 pre-existing tests still pass; 5 new tests added, 2 of which
reproduce the exact crash against the pre-fix script (verified via
git show) to prove they're real regression tests, not decoration.
… dead docs (115→94) (#279)

* feat(prune)!: native-first Phase 1a — remove 21 dead-namespace commands (115→94)

v3.0.0 increment. Recon-gated prune of deprecated commands with zero external
callers, per SPEC-craft-native-first-breakup + Phase 0 recon.

Deleted (21): workflow/ (5) + task/ (3) namespaces entirely, check/ (1),
git/{init,git-recap,sync} + git/docs/{safety-rails,undo-guide,learning-guide}
(true shims → skills/dev/git/references/), site/{nav,init,add,audit,consolidate,
theme,create,preview}. Kept 15 as thin aliases (live flow-cli/tutorial/homebrew
callers). site/docs/frameworks.md retained.

HELD via ADR-002 rich-body gate (restored, NOT deleted): git/docs/refcard,
check/gen-validator, workflow/insights — carry unique logic not preserved in
their skills. Caught by test_skill_referenced_commands_exist during build.

Fixes: bump-version --counts-only + swept 20 files 115→94; count-cascade
(plugin.json breakdown, README, exclusions.txt historical README:42 entry);
de-brittled test floors (hub >=100→59); OPT_IN drops deleted spec-review;
removed obsolete test_git_init suite; repointed 3 true-shim refs in git SKILL.md.

Tests: full suite 0 real failures (2676 passed; expected XFAIL/XPASS only);
validate-counts 94/45/8 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG

* docs(prune): exclude dead docs (specs/plans/archive) from built mkdocs site

Phase 1b (v3.0.0). SPEC R1-B2/R1-B6: dead process-artifact dirs leave the
PUBLISHED site but stay in-repo/git as live capture targets. No retention rule (C2).

- exclude_docs EXTENDED (not overwritten): specs/ added; plans/+archive/ preserved
- 12 inbound links into specs/_archive rewritten to GitHub blob URLs (R1-B6 sweep)
- 29 stale nav entries + 2 parent headers pointing into excluded dirs removed
- mkdocs build --strict exit 0; broken_links + doc-accuracy tests green

Done via bounded Stage-2 workflow (docs-exclude agent), verified before commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG

* docs(spec): record native-first refutation — ship Phase 1, drop Phases 2-3

Stage-2 workflow (3 pilots + 4-axis gate + routing test) resolved the gating
unknown NO-GO: router can't dispatch cross-plugin AND 3/3 pilots = thin-keep.
Native-first thinning premise refuted for these commands; Phase 1 (prune +
docs-exclude) is the banked, verified value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG

* chore: drop tasks/ execution scaffolding before PR

Working artifacts (agent-skills plan/todo + Phase-0 recon) don't belong on dev
per merge-cleanup convention. The durable recon finding (delete 21 / hold 3 /
native-first refuted) is recorded in the SPEC OUTCOME block.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG

* test(git-shim): drop deleted sync.md + git-recap.md from shim-correctness array

CI's "Validate Plugin Structure" job (branch-guard bash suites) asserted
commands/git/{sync,git-recap}.md exist; Phase 1a deleted them (zero external
callers). Removed from the SHIMS map; the 7 kept git aliases remain. This bash
suite runs separately from pytest — missed in the pre-push pytest-only pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…irst Phase 1)

- .STATUS: Phase 1 prune MERGED (115→94), native-first thinning refuted,
  Phases 2-3 dropped, verification-gap note (pytest ≠ CI bash suites)
- ORCHESTRATE: marked CLOSED with outcome banner (not a live checklist)

Durable lessons captured to memory: pytest-doesnt-cover-craft-ci-bash-suites,
native-first-thinning-refuted-for-craft-dev-ops, + rich-body-trap refinement.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
Completes the doc/website audit for the native-first Phase 1 prune (PR #279).
Removes ~264 references to the 21 deleted commands across 40 files; archives
5 whole-doc-about-deleted-command files to docs/archive/deleted-commands-v3/
and drops their mkdocs nav entries.

- Strip deleted-command refs: git/{init,sync,git-recap}, git:docs/{safety-rails,
  undo-guide,learning-guide}, workflow/{stuck,focus,recap,spec-review},
  task/{status,output,cancel}, site/{init,add,audit,consolidate,nav,theme,
  create,preview}
- Archive (git mv) + de-nav: git-init docs set + git/sync.md
- Fix 5 broken links/anchors to archived/removed content (strict build now clean)
- Counts left at 94/45/8 (managed by bump-version.sh); category subtotals are
  known YELLOW drift, not touched here

mkdocs build --strict: clean (0 warnings). validate-counts: green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
Fresh grill on the docs-authoring extraction (folio), standing on
docs-maintenance merits only (native-first refuted in PR #279). Locks:
B1 folio=tools+shared docs-standards (content per-project); B2 caller-based
cut (docs:update/changelog stay in craft, authoring→folio); B3 drop DOCS
from /do+/hub; B4 duplicate count tooling; B5 history-preserving subtree
split, craft v4.0.0 + folio v1.0.0. Supersedes the dropped Phase 3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
Tier-4 plan-orchestrator artifact built from GRILL-folio-split (B1–B5).
Cross-repo (craft + new folio repo), ORCHESTRATE-only (no worktree yet):
P0 caller-audit (gate) → P1 folio scaffold + docs-standards contract +
dup count tooling → P2 history-preserving subtree extraction + folio CI →
P3 craft amputation v4.0.0 (remove moved cmds, collapse site:deploy, drop
DOCS from /do+/hub, MIGRATION-v4) → P4 /folio:do + coordinated 2-repo
release. All 5 grill open questions mapped to phase tasks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
…terns

branch-guard's HAS_HEREDOC guard only gated Pattern 1 (redirect). Since
grep is line-oriented, a heredoc body line that happened to start with
touch/tee, or match cp's two-word shape, was scanned as real shell syntax
by Patterns 2-4 and could trigger a false bash_write_through confirm on
plain commit-message prose. Gate all four patterns on HAS_HEREDOC, same
as Pattern 1. 4 new regression tests (117/117 passing); reverting the fix
alone (tests unchanged) makes the 3 new false-positive tests fail and
leaves the real-detection regression test passing, confirming both the
bug and that the fix doesn't weaken genuine write-through detection.
…ied, zero-CI mode pointer

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
…ion amendments

Phase 0 OUTCOME recorded (Workflow wf_bf0549a1, adversarially verified,
partition_sound): STAYS = docs:update + docs:changelog + site:deploy-as-
shell; MOVES = 26; docs:claude-md:* trio = Phase 1 judgment call (leaning
STAYS). Grill B2 open question resolved.

Zero-CI impact amendments: folio repo must be PUBLIC (private CI would
bill against the account pool — post-2026-03 self-hosted runners aren't
free either); new Phase 1.5 protection-baseline task (protect-baseline,
dev stays hook-guarded only). craft itself unaffected (public, free CI,
protection unchanged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
Spec-driven artifacts for executing ORCHESTRATE-folio-split via dynamic
Workflow(): W1 fan-out-inside-phases/parent-owns-gates + W2 parent-only-
git/absolute-paths (grill-locked), W3 per-phase Workflow map, W4 schema+
adversarial-verify contracts, W5 budget envelope (~1.5-2.5M subagent
tokens). Boundaries: agents never run git; every gate needs transcript +
verifier sound=true; folio PUBLIC. tasks/plan.md + tasks/todo.md written
as working artifacts (untracked, feature-branch bound).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
…er, folio launches ~15

Move-vs-kill pass over the 26-command moves-set (bounded; B1-B5/W1-W2
locked): 8 of 26 are already deprecated (all site:* + nav-update,
replaced-by skills that move anyway) — kill at border with ADR-002
rich-body salvage (site-management router is 284 lines, NO references/,
vs ~80KB of bodies); site:progress = teaching residue, pure kill.
check-links merges into docs:check; site:docs:frameworks demotes to
skill reference; claude-md trio RESOLVED: stays in craft as one unit
(fixes review B5). Skills: 6 move / 3 stay (callers verified). Final
shape: craft 69 / folio ~15+6+6 — 40% less extraction work. Feeds the
single post-review amendment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
… ~22 cmds

Adversarial-creative review of the post-split surface: classification
shows 27 of 69 are ALREADY deprecated shims (v3 skill-migration debt),
2 more shims, 6 routers, 2 references — only 32 genuinely rich.
Proposal: complete the shim-kill at v4 (ADR-002 salvage gate, ~12%
rich-body base rate from PR #279), kill 2 teaching-residue utils,
demote 1 internal ref, subcommand-consolidate ci (8 to 1), arch (4 to
1), code-audits (5 to 1), orch (3 to 1), plan (2 to 1) via savant's
proven depth-1 pattern. Result: ~22 commands + ~40 skills. Does NOT
re-run refuted native-first (own skills, not third-party). Rider on
the v4.0.0 folio release: one major, one migration, one cascade.
Self-attacked on 6 angles, all survive with gates (skill
slash-invocability check per shim; nested skills excepted).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
…lls)

Scoped integration of agent-skills' security-and-hardening, code-simplification,
frontend-ui-engineering, and api-and-interface-design as optional content-injected
gates inside workflow/drive/fanout — not a 4th orchestration engine, not a change
to planning ownership. Avoids the subagent-nesting hazard documented in
agent-skills' own AGENTS.md by never adopting its /ship-style persona fan-out
inside craft's existing dispatch layers. Builds on SPEC-orchestrator-consolidation-2026-07-04.md.
…ngles recorded

SPEC-D's section 6 targets (commands/orch/drive.md, orch.md,
workflow-engine) collide with the v4 consolidation proposal (orch 3 to
1); the domain-gate concept is unaffected, only the paths go stale.
Header note prevents a parallel session implementing against stale
targets; adds two grill angles for when its turn comes (path staleness,
external-content dependency policy).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
…ider

Folds the 3-lens adversarial review (6 blockers, 9 majors) and the
border-filter brainstorm into ORCHESTRATE + SPEC:
- B1 mechanism: git filter-repo (subtree split cannot express the set)
- B2 ci.yml:92 floor edit in the SAME amputation PR (86 to 60, then 18
  at the 3.5 rider)
- B3 folio release choreography scoped into Phase 2.4 (homebrew +
  aggregator workflows, App install, tap manifest, marketplace row)
- B4/B5 counts corrected: craft 69 post-split (then ~22 via 3.5),
  folio 15/6/6; claude-md trio RESOLVED stays as one unit
- B6 new docs-content sweep task with grep-zero gate (strict build is
  false-green for prose refs)
- Majors: bare-protection-then-check bootstrap, folio floors retuned,
  release order folio-first/craft-tag-last + rollback runbook,
  commit-per-batch dirty-tree discipline, enumerated real test-breakage
  set, /folio:hub moved to Phase 2, hub DOCS section rebuilt (staying
  set + breadcrumb) instead of blanket drop, MIGRATION breadcrumbs
- Border filter: 7 border kills w/ ADR-002 salvage, check-links merge,
  frameworks demotion, skills 6-move/3-stay
- New Phase 3.5 rider (v4 surface, ~22 cmds) blocked on roster grill;
  folio site decision resolved (minimal folio-built site at v1.0.0)
tasks/plan.md + todo.md slimmed to pointers (ORCHESTRATE is the single
task source).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
R1-R4 locked (GRILL-craft-v4-roster): keep hot micro-shims next/done/
refine/brainstorm/check (check discovered as 315L dep shim during table
review — 5th hot entry); salvage gate = every dying shim over 150L (12
jobs incl. the dist trio and claude-md trio); consolidation shape =
thin router + verbatim bodies to skill references (ADR-002
line-conservation per migration); roster = 18 keep + 5 micro + 3 new
routers (ci, arch, code:audit) = 26, tolerance plus-minus 2 with
ledger-recorded deviations. Full 69-to-26 disposition in ROSTER-*.md.
ORCHESTRATE Phase 3.5 unblocked: now 5 concrete tasks (salvage, kills,
consolidations, cascade incl. hub regen + ci.yml floor to 18, suites).
The entire v4.0.0 train is now spec-complete.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
Planning chain complete: grill (B1-B5) + Phase 0 verified partition +
3-lens adversarial review amendment + border filter + v4 roster
(R1-R4, 26 commands) + 33-task execution breakdown. All approved.
Phases 1-4 await explicit go; nothing implemented yet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
T1.1 of the v4.0.0 train. Working artifacts (tasks/) ride the branch,
deleted before final merge per CP-4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PCmhbNUV6iSQrx1USQjACG
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ec-review, folio-split)

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

Phase 2 complete (feature/extraction pushed, PR #1 CI green locally and
on GitHub). Pre-Phase-3 gate cleared: T1.6 secrets confirmed real,
folio plugin validates, branch protection set, all 15 commands/6
agents/6 skills verified present against the ORCHESTRATE disposition
table. Phase 3 not started (user deferred).

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

Implements the 3-part fix proposed on the reopened craft#199:

- verify-surfaces.sh: a mismatched Cowork leg now reports N releases
  behind (via git tag position), not just "mismatch" — both in the
  human report and --json's new releasesBehind field on the cowork
  leg. A bare WARN was easy to let ride silently; savant's own Cowork
  install sat 10 releases behind before anyone noticed.
- scripts/cowork-recover.sh (new): one-shot diagnosis that reuses
  verify-surfaces.sh's JSON output and prints the actual working
  recovery path (uninstall -> reinstall -> full Cmd-Q relaunch) only
  when drift is found; no-ops cleanly when aligned or no Cowork store
  exists on the machine.
- downstream-verification.md: explicit disambiguation note so a
  future release doesn't get closed against the wrong verification
  again — Code-registered (@Local-Plugins, CLI) and Cowork/Desktop
  (GUI) are different surfaces that share confusingly similar naming;
  that naming collision is exactly what caused craft#199's mis-closure.
- TUTORIAL-dist-surfaces.md: corrected Step 4's guidance, which
  previously told readers to use in-app "Update" — the exact path
  that reliably no-ops. Now documents the real recovery steps and
  points at the new script.

12 new tests (82/82 total in test_verify_surfaces.sh); full targeted
pytest wrappers green. doc-coverage-check.sh and markdownlint clean
on all touched docs.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
CI verified real on GitHub (matched local replica), PR #1 squash-merged
to folio dev via --admin (classifier blocked plain merge for unrelated
reasons, not branch protection). Logged folio's actual release-readiness
gap (no e2e/dogfood tests, thin docs, still 0.1.0) and the user's
decision to defer that work until after Phase 3.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Records PR #280's merge and the feature/cowork-drift-recovery worktree/branch removal.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Data-Wise and others added 29 commits July 15, 2026 15:10
…287)

* docs: add ORCHESTRATE plan for guard cd-target resolution

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

* fix(guard): cumulative cd-target resolution in no-switch-guard.sh

Replace the single-hop `-C`-or-leading-`cd` git_dir extraction with a
clause-walking resolver: each bare `cd <path>` retargets every subsequent
clause (so `cd a && cd b && git switch x` resolves to b, not a), and a
`git -C <path>` sets that invocation's target against the effective cwd.
Last retarget wins.

Phase 1.1 judgment call (per ORCHESTRATE): parallel-inline logic in each
hook, NOT a shared sourced file. Two reasons: (1) the hooks derive their
base cwd differently — no-switch from process $PWD, branch-guard from the
JSON `.cwd` — a shared fn would be parameterized on that anyway; (2) a
shared file would force both install scripts to deploy + source it, adding
source-path-resolution fragility. Inline avoids both.

Split uses `awk gsub` (real newline on BSD & GNU; BSD sed's `\n` does not).
Paths containing `$`/backtick/quote are skipped (shell expansions / quoted
mid-command `;cd` in commit messages we can't statically resolve).

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

* fix(guard): name resolved cross-context repo in no-switch prompts

Phase 2.2/2.3: is_dirty() already checks `git -C "$git_dir" status` against
the resolver's target (verified). Add a [target repo: <name>] note to the
dirty-tree confirm and clean-switch announce so a cross-context switch is
unambiguous about which repo it acts on — mirrors branch-guard.sh's
"name the resolved repo/branch explicitly" checklist item from #284. Note
is empty when no cd/-C retarget occurred (same-repo switches unchanged).

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

* fix(guard): cumulative cd-target resolution in branch-guard.sh §8d0

Phase 3: retrofit #284's single-hop §8d0 resolver to cumulative clause
tracking. The old block resolved only ONE leading `cd`/`-C` (regex anchored
`^cd`, structurally unable to match a second cd). Replace with the same
left-to-right clause walk used in no-switch-guard.sh: each bare `cd <path>`
retargets every subsequent clause (cd a && cd b && git push → b), a
`git -C <path>` sets that invocation's target, last retarget wins.

Downstream re-derivation (IS_CROSS_REPO_TARGET, BRANCH, PROJECT_ROOT,
INTEGRATION_BRANCH, PROTECTION) fires against the LAST resolved target,
guarded by the existing -d/git-repo/branch checks — a spurious target from
a quoted mid-command separator that isn't a real git repo on a different
branch is simply ignored. Scope comment updated (v1→v2); quote-bearing
paths now also skipped. All #284 single-hop cross-context tests stay green
(121/121, Phase 3.3 regression check).

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

* test(guard): multi-hop cd + cross-context cases for both guards

Phase 4. branch-guard: cd a && cd b && git push (last-wins → b), mixed
cd + -C (b wins), and inverse cd feature && cd main (last-wins gates the
protected target). no-switch: BRAINSTORM Context Scan scenarios — dirty
session + clean worktree target (NOT blocked), clean session + dirty
worktree target (blocked), and pure multi-hop cd dirty && cd clean
(last-wins → NOT blocked). Verified the multi-hop cases FAIL against the
base dev scripts (which pick the first cd) and PASS against the cumulative
resolver — meaningful, failable coverage. Suites: branch-guard 124/124,
no-switch 39/39.

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

* docs(guard): document cumulative cd-target resolution

Phase 5. REFCARD-BRANCH-GUARD.md: new "Cross-Context Target Resolution"
section (resolution table + cumulative note + documented quote-parsing
limitation). CHANGELOG.md + docs/CHANGELOG.md: mirrored [Unreleased] Fixed
entry. markdownlint clean; changelogs verified identical.

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

* docs(orchestrate): mark guard-cd-resolution phases 1-5 complete

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

* fix(guard): use awk char-class for clause split (cross-awk safe)

Replace gsub(/&&|;|\|/) with gsub(/&&|[;|]/) in both hooks' cumulative
resolvers. `\|`-as-literal-pipe is well-defined on BSD awk (dev box) but
POSIX-underspecified for other awks; a character class is unambiguous
across BSD/gawk/mawk and costs nothing — matters for the macOS + Ubuntu-CI
portability constraint. Behavior identical; suites stay 124/124 + 39/39.

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

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* docs: add ORCHESTRATE plan for guards.json write-race fix

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

* fix(guard): add shared mkdir-based lock helper for guards.json

Phase 1 of the guards.json write-race fix. mkdir is atomic on both
macOS and Linux (flock has no CLI on macOS, only the syscall), so it's
used as the lock primitive with a staleness timeout so a crashed
holder can't wedge the lock permanently.

Standalone script (lib/guards-lock.sh), not a sourced function --
Operation 12's guards.json mutation is inline bash issued per-turn by
the LLM with no backing script, so it can't share a shell process with
install-guards.sh's separate invocation.

* fix(guard): wire lock helper into Operation 12's guards.json mutation

Phase 2 of the guards.json write-race fix. enable/disable/profile's
jq-mutate step and the mute-expiry auto-sweep (when it actually clears
a mute) now acquire lib/guards-lock.sh before the read-modify-write
and release immediately after, with a trap EXIT cleanup so a mid-write
failure can't leak the lock.

* fix(guard): wire lock helper into install-guards.sh guards.json writes

Phase 3 of the guards.json write-race fix -- the second writer found
during adversarial review. Both the fresh-create branch and the
per-guard merge loop now acquire lib/guards-lock.sh before their
read-modify-write and release immediately after, with trap EXIT
cleanup. The merge loop keeps a separate acquire/release per guard
iteration rather than one lock held across the whole loop, so a
concurrent Operation 12 mutation isn't blocked for the full duration.
Verified against a scratch HOME (fresh-create + idempotent re-run).

* test(guard): add guards.json concurrency regression suite

Phase 4 of the guards.json write-race fix. Formalizes this session's
ad hoc falsification test (2 concurrent jq-mutate loops, 40 writes
each, scratch copy) as tests/test_guards_registry_concurrency.sh:

- Case 1: two concurrent writers, distinct fields -- zero lost updates.
- Case 2: an Operation-12-style writer concurrent with an
  install-guards.sh-style merge writer against the same scratch file --
  proves the shared lock, not just per-writer self-consistency.
- Case 3: a pre-staged stale lock dir is force-broken within the
  timeout rather than hanging.

Every case runs against a mktemp -d scratch copy; a hard
assert_not_real guard aborts the suite if any path ever matches the
real ~/.claude/guards.json. Red-first verified: MODE=unlocked
reproduces lost updates on the pre-fix code path across repeated
runs; MODE=locked (default) passes 7/7 with zero lost updates.

Also fixes a bug found while red-lining the suite: lib/guards-lock.sh
logged a spurious "breaking stale lock" warning when a lock dir
vanished between its existence check and its stat call (the holder
releasing normally, not a crash) -- now retries immediately instead
of misreporting staleness.

* docs(guard): correct guards.json sole-mutator claim + CHANGELOG

Phase 5 of the guards.json write-race fix. skills/dev/git/SKILL.md
Operation 12 no longer claims to be the sole sanctioned mutator of
guards.json -- corrected to name both writers (Operation 12 and
install-guards.sh) and the shared lib/guards-lock.sh lock that now
coordinates them, per the second-writer finding from adversarial
review. Mirrored [Unreleased] Fixed entries added to CHANGELOG.md and
docs/CHANGELOG.md.

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
… + docs (#289)

Adversarially audited before implementation (background agent, read-only
review of the proposal). Three of four proposed changes needed revision
before landing:

- Self-containment prompt gains a required absolute-path prefix (Agent
  tool has no cwd param) but drops an over-broad confinement clause that
  would have contradicted this skill's own Cross-Repo Detection feature.
- New model-tier guidance reconciled against workflow-engine/SKILL.md's
  existing per-stage Model Routing rule (opposite instruction, different
  scope) with an explicit cross-reference instead of standing alone.
- EnterWorktree guidance applied only to the STOP-new-session Session
  Instructions template, not to orchestrate-dispatch's intro — that mode
  dispatches a background Agent and doesn't need an interactive cwd
  switch; adding it there would have undermined the mode's own
  2-concurrent-dispatch capability.
- Resumability wording changed from a literal-string claim ("stays
  exactly X") to "identical between initial and resumed dispatch", since
  X now includes a path.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
All three guard-hardening PRs merged to dev with green CI:
#287 (cd-target resolution), #288 (guards.json lock, no CHANGELOG
conflict with #287 despite both touching it), #289 (orchestrate-dispatch
hardening). Worktrees removed, local branches deleted via safe `-d`
(squash-merge content recognized without needing force).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ORCHESTRATE-*.md files belong on feature branches during development,
not on dev after merge (missed during the #287/#288 squash-merge cleanup).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Three claims in GRILL-branch-guard-target-resolution-2026-07-14.md are
now stale given this session's guard-hardening work: the disproven
no-switch-guard "compound-string-match/worktree-cleanup" framing, the
concurrency-safety item marked deferred (now fixed in #288, broader
than originally scoped), and the cd/-C cumulative-tracking question
marked unresolved (now locked and shipped in #287). Added forward
pointers to the actual resolutions rather than leaving a future
session to re-derive them from a stale doc.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…k-decision test

A/B timing against branch-guard.sh at 3 historical commits (pre-guard-rewrites,
pre-#284, pre-#287) all showed 207-360ms on this machine — the block-decision
path was never reliably under 200ms, independent of today's cumulative-cwd work.
Loosened the budget to 350ms with the evidence recorded inline; left the
allow-decision budget at 200ms since it passes reliably.
Adversarial review of the deferred Phase 3.6 plan. Key finding: commands/orch.md
already exists and already soft-routes to orch:drive/orch:workflow, so T3.6.2's
"build a fresh router" premise was stale. 10 decisions locked, net effect:

- 4 workstreams -> 2 (orch.md refactor, code:audit router); arch router dropped,
  plan:feature stays excluded per its pre-existing D2 lock
- Reframed as organizational cleanup, not command-count reduction (invocability
  preserved for every subcommand, so 46->~26 was never reachable this way)
- Split into 2 independent PRs instead of one bundled CP-3.6 gate
- orch.md's launch logic moves to a new skill; orch.md itself becomes the router
- Explicit discovery-cache regen step added to both workstreams' task lists

See docs/specs/GRILL-phase-3-6-router-consolidation-2026-07-15.md for full ledger.

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

Rewrites tasks/todo.md's stale Phase 3.6 section (T3.6.0-T3.6.7, planned as one
bundled CP-3.6 gate at "craft@26") with the post-grill scope: 3 independently
mergeable workstreams (orch.md refactor, code:audit router, ci router), each its
own PR. arch router dropped (D6); plan:feature stays excluded (pre-existing D2
lock); framed as organizational cleanup, not command-count reduction, since D3
preserves slash-invocability for every absorbed subcommand.

Also corrects a gap in the prior GRILL pass: the original Phase 3.6 plan had 5
target families, not 4 - `ci` (8 commands, 2117 lines, largest family) was
missed in the first grill and is added back here as Workstream C (D10), with a
real repo-flag format mismatch (short-name vs OWNER/NAME) found and scheduled
as an explicit fix rather than left latent.

Updates ORCHESTRATE-folio-split.md's Phase Overview rows for 3.5 (mark actually
CLOSED, was showing unstarted) and 3.6 (point at the new scope).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Caught at the start of implementation, before any orch.md/drive.md/workflow.md
change was made: D1/D7 were locked from line-counts and flag names alone,
without reading drive.md/workflow.md in full. They're already thin command
wrappers delegating to their own skills (drive-engine, workflow-engine) - the
exact end-state the plan wanted to build - and the 3 commands document
genuinely distinct execution engines with no real duplication. Same verdict
as arch (D6): drop it.

Phase 3.6 proceeds with 2 real workstreams (code:audit router, ci router).
orch.md/drive.md/workflow.md are untouched by this branch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
D3 (preserve slash-invocability) cited "T3.5.1 kept commands invocable via
skill, not deleted" as precedent -- factually wrong, T3.5.1 deleted the
dist:pypi/curl-install/marketplace commands entirely (840e57d). The actual
distinguishing factor: those 3 were already deprecated:true shims before
T3.5.1 touched them, unlike Phase 3.6's targets which T3.6.0 confirmed are
live and non-deprecated. Conclusion unchanged, citation fixed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Caught during implementation, before any file was changed: D8's "cohesive
audit shape" verdict for all 5 code:audit-family commands was decided from
line counts and a partial flag check. Full read of deps-audit.md/
deps-check.md/docs-check.md shows they're generic cross-project tools with
incompatible flag surfaces (deps-check has no --format/--fix at all;
docs-check has no formal arguments block and is really a generic docs+
deploy pipeline, not schema-audit-shaped).

Workstream B shrinks to command-audit + skill-standards only (298 lines) --
the only 2 that genuinely share shape and verified-compatible --format/--fix
semantics. deps-audit.md/deps-check.md/docs-check.md are untouched.

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

Phase 3.6 Workstream B (docs/specs/GRILL-phase-3-6-router-consolidation-2026-07-15.md,
tasks/todo.md T3.6.B1-B5): the only 2 of the original 5 code:audit-family
commands that genuinely share shape and --format/--fix semantics
(both validate craft's own frontmatter/schema).

- New skills/code/audit-router/SKILL.md: shared --format/--fix vocabulary,
  health-score formula, exit-code convention shared by both commands
- Salvaged both full bodies verbatim into references/{command-audit,
  skill-standards}.md (ADR-002 line-conservation)
- commands/code/{command-audit,skill-standards}.md shrunk to thin shims:
  frontmatter (arg surface) unchanged, both stay directly slash-invocable
- deps-audit.md/deps-check.md/docs-check.md untouched -- they're generic
  cross-project tools with incompatible flag surfaces, not schema-audit-shaped

Count cascade: 39->40 skills (bump-version.sh + manual sweep of 6 files
outside its tracked list: commands/dist/homebrew.md, docs/MIGRATION-v4.md,
docs/QUICK-START.md, docs/architecture.md, docs/commands.md,
docs/guide/getting-started.md, docs/guide/hub-live-dashboard.md, docs/index.md,
docs/skills-agents.md, docs/tutorials/TUTORIAL-code-skill-standards.md,
README.md). Commands unchanged at 46 (no command added/removed, only 2
shrunk in place).

Full suite: 2561 passed / 4 failed (all confirmed pre-existing on this
branch via git stash: branch-guard hook-install drift + 3 known
test_v115_adhd_enhancements baseline failures) / 43 skipped.
mkdocs build --strict: clean (0 errors). validate-counts.sh + bump-version.sh
--verify: ALL CONSISTENT.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both declared `category: code` while living in `commands/ci/`, which
help_file_validator._check_category_mismatch flags as a real defect
(expected category is derived from the parent directory).

Found during Phase 3.6 Workstream C investigation. No router was built:
a full read of all 8 commands/ci/*.md showed the family is genuinely
heterogeneous (detect.md duplicates skills/ci/SKILL.md, triage.md's
classify_failure() is extracted by tests/test_ci_triage_unit.py,
generate.md is 731 lines of unique CI templates), so this metadata fix
is the only real defect the investigation surfaced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both declared `category: orchestrate` while living in `commands/orch/`,
and no `commands/orchestrate/` directory exists — so the category was
unbacked. help_file_validator._check_category_mismatch derives the
expected category from the parent directory and flags this.

Pre-existing on dev; surfaced by a repo-wide validator sweep run while
fixing the same defect class on commands/ci/. Repo-wide mismatch count
is now 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 3.6 opened with 5 router workstreams and closes with 1 shipped:

- plan:feature — excluded (pre-existing D2 lock, unchanged)
- arch        — dropped (D6), no real duplication
- orch        — dropped (D11), already delegates to engine skills
- code:audit  — SHIPPED, shrunk 5 commands -> 2 (D12), 204822a
- ci          — no router (D13); metadata fix only

D13 records the ci full-body read: detect.md duplicates skills/ci/SKILL.md,
triage.md's classify_failure() is regex-extracted and exec()'d by
tests/test_ci_triage_unit.py (markdown IS the source of truth), generate.md
is 730 lines of unique templates. No shared shape to route.

Also withdraws D10's repo-flag normalization as a premise artifact: no
validator enforces cross-family flag-shape consistency, so it was only a
"defect" relative to a router vocabulary that no longer exists.

Root cause across D11/D12/D13: the grill locked load-bearing decisions from
line counts and flag names without reading the bodies. Every family that
looked cohesive from surface metrics collapsed on a full read. Rule recorded
for future consolidation phases: read every candidate body in full before
locking a router decision.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 3.6 is closed and committed; the branch is deliberately parked, not
abandoned. Records why it is not PR-ready so the next session does not
re-derive the diagnosis.

Scope reality: this branch is the whole v4 train (36 commits, Phases
3+3.5+3.6), 41 behind dev off a 2026-07-09 merge-base — not a small
Workstream B+C PR.

Three CI blockers with three unrelated causes:

1. test_branch_guard.sh — stale branch, NOT a regression. A
   characterization test encoding a known limitation that guard-hardening
   PRs #287-289 closed on dev; dev already renamed it `..._now_caught`
   expecting 2. The suite runs the INSTALLED hook at $HOME/.claude/hooks/,
   so its result is machine-global state, not branch state — which is why
   it passes on dev and fails here with identical code. Fixed free by a
   dev merge.
2. ci.yml:92 command floor 60 vs actual 46 — the open T3.4, stale a second
   time (targeted 86->60, then Phase 3.5 cut to 46). CI hard-fails. The new
   number is a decision (mass-deletion guardrail), not a mechanical edit.
3. test_git_shim_correctness.sh — the only true branch debt. T3.5.2 deleted
   commands/git/ but left the suite asserting those 7 shims exist. A dev
   merge does NOT fix it. Needs the suite updated/removed + its ci.yml:180
   line.

Root blind spot: these are bash suites (ci.yml:177-180); pytest does not run
them. T3.5.2 cited "full pytest 2575 passed" as evidence and shipped this.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Brings 41 commits of dev (guard-hardening PRs #287-289, the cwd resolver,
and /craft:git:issue-check from PR #283) onto the v4 folio-split branch.
34 conflicts resolved. Surface: 47 commands / 40 skills / 2 agents.

Conflict resolution:

- ~30 count-cascade conflicts (dev 95/45/8 vs branch 46/40/2) -> took the
  branch's post-split surface, then recomputed every tracked count
  mechanically via bump-version.sh --counts-only (14 files) rather than
  trusting hand-resolution. validate-counts.sh exits 0 at 47/40/2.
- commands/docs/quickstart.md (modify/delete) -> kept deleted; folio owns
  it (verified present at folio/commands/docs/quickstart.md). dev's only
  change was a count bump inside an example template.
- commands/git/issue-check.md -> KEPT AS A LIVE COMMAND, not folded into
  skills/dev/git/. Git's directory-rename detection had auto-relocated it
  to skills/dev/git/references/ (our side renamed commands/git/ ->
  skills/dev/git/references/ in T3.5.2), which would have broken
  tests/test_issue_check_unit.py: that test hardcodes
  commands/git/issue-check.md and exec()s the classify_issue() block out
  of it, so the command file is the single source of truth for the
  classifier. Same structural constraint recorded for commands/ci/triage.md
  in GRILL D13. Restored to commands/git/; its 12 unit tests pass.
- skills/dev/git/SKILL.md -> kept the branch's reference-doc rows, dropped
  dev's stale commands/git/docs/refcard.md row. Operation 13 (Issue Premise
  Check) merged in cleanly from dev.
- mkdocs.yml nav -> dropped dev's 7 deleted git command entries, kept the
  issue-check entry (blind --ours would have dropped it from the site).

Also corrects two now-false claims in skills/dev/git/SKILL.md ("commands/git/
is now empty", "there is no explicit /craft:git:* invocation path anymore") —
issue-check is a live command and the skill now says so, with the reason.

Long-tail doc counts (46 -> 47) updated in 6 live user-facing files.
Historical changelog entries, illustrative sample outputs, and spec/brainstorm
records deliberately left at their original numbers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
T3.4 — command floor 60 -> 40. The floor is a mass-deletion guardrail and
must sit BELOW the real surface. It went stale twice (86 -> 60) while the
actual count fell 94 -> 70 -> 47, leaving the floor ABOVE the surface: CI
hard-failed with "Expected at least 60 commands, found 47" on every run.
40 leaves ~15% headroom under the v4 surface of 47. Rationale is now a
comment in ci.yml so the next surface change does not repeat this.

test_git_shim_correctness.sh — inverted, not deleted. It asserted each
commands/git/*.md was a genuine thin shim; T3.5.2 then COMPLETED that
migration by deleting all 11 shims, making the assertions unsatisfiable
(7 FAIL on this branch, 21/21 on dev where the files still exist). The
shim-drift it guarded is now structurally impossible, so it instead guards
the migration's end state:

  1. the 11 former shims stay deleted (nobody silently re-adds one)
  2. skills/dev/git/SKILL.md still carries Operations 1-12
  3. issue-check stays live + non-deprecated (the one sanctioned exception)
  4. no unsanctioned command reappears under commands/git/

Verified by planted defect, not just a green run: resurrecting status.md ->
caught; renaming Operation 5 -> caught; marking issue-check deprecated ->
caught; clean baseline 25/25 before and after. ci.yml needs no change — the
suite keeps its filename.

Root blind spot both share: these are bash suites (ci.yml), and pytest does
not run them. T3.5.2 cited "full pytest 2575 passed" as its evidence and
shipped the breakage behind a green check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extracts craft's docs-authoring surface into the standalone folio plugin.

Phase 3.6 shipped 1 router of 5 planned: code:audit (5 cmds -> 2). orch, arch,
and ci were all dropped after reading the command bodies in full — every family
that looked cohesive from line counts and flag names collapsed on a full read.

Also fixes 4 category: metadata defects (repo-wide mismatches now 0), T3.4's
stale command floor (60 -> 40; it sat ABOVE the real surface of 47 and hard-failed
CI), and inverts test_git_shim_correctness.sh to guard the completed git migration's
end state rather than shims T3.5.2 deleted.

/craft:git:issue-check stays a live command: tests/test_issue_check_unit.py extracts
its classify_issue() block from commands/git/issue-check.md and exec()s it, so the
command file is the classifier's single source of truth (same constraint as
commands/ci/triage.md, GRILL D13).

CI verified green: 2586 passed / 0 failed, all 4 bash suites pass, floor gate
confirmed at 'Found 47 command files'.
Both were pre-existing errors, not off-by-one drift from the v4 merge — the
second was already false before it.

- Dry-run tip: "46 commands now support dry-run mode" -> 27. The 46 was the
  old command TOTAL pasted into a dry-run claim; it contradicted the same
  page's authoritative line ("27 of 47 commands support --dry-run").
- Documentation Commands section: "46 commands for documentation generation,
  validation, and site management" -> "2 commands for documentation updates
  and changelog automation". craft's commands/docs/ holds exactly 2 files
  (update, changelog); the other 17 rows in that table are migration pointers
  to folio, which now owns generation/validation/site-management. The old
  sentence described a surface craft no longer has.

Verified: 0 remaining "46 commands" claims in this file, markdownlint clean,
mkdocs build --strict exit 0 (the added MIGRATION-v4.md link resolves).

NOT fixed, needs a decision: line 10's "17 Categories: arch, check, ci, code,
discovery-usage, dist, do, docs, git, hub, orchestrate, plan, site, smart-help,
test, utils, workflow" is stale (discovery-usage was demoted to docs in T3.5.2,
orchestrate is now orch, no workflow dir exists) — but "category" has two
defensible definitions here that disagree: frontmatter-declared categories give
10 (arch ci code dist docs git orch plan site smart) while directories give a
different 10 (... utils). The current list mixes in root-level commands as
pseudo-categories and matches neither. Left alone rather than guessed at.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… gap (#291)

/craft:check never ran the shell suites CI invokes directly, because pytest does
not collect them. The local gate and CI measured different things, and the local
one reported green for a surface it never touched.

Root cause behind two of the three CI blockers the v4 folio-split branch carried:
T3.5.2 deleted commands/git/ citing 'full pytest 2575 passed' as evidence, while
tests/test_git_shim_correctness.sh — a CI-required check — had been red for days
asserting those exact files existed.

Implemented as a hot-reload validator (the extension point /craft:check already
scans), deriving the suite list from .github/workflows/*.yml rather than
hardcoding it. craft has 22 tests/*.sh but CI runs 6; a hardcoded list is the
drift this prevents. Advisory in default mode, blocking in release, per the
documented release-gated validator contract.

Verified by planted defect: replaying the real T3.5.2 bug (pre-fix suite restored
from d41de18) is caught — WARN in default, exit 1 in release. Also catches a
failing suite, a workflow referencing a missing suite, and skips cleanly in repos
with no workflows.

CI verified: pytest 2586 passed / 0 failed, all bash suites green, floor gate at
'Found 47 command files'.
…nce closed

.STATUS still claimed "Counts unchanged 95/45/8" and last_session 2026-07-15,
while dev is at 47/40/2 after today's two merges — the project's own state
record contradicting the repo by ~48 commands.

Records: PR #290 (v4 folio split, merge-commit 4af10f2, craft 94->47), PR #291
(ci-bash-suites validator, squash 88b221d), 5034aef (2 stale docs/commands.md
count claims), and the local branch-guard hook re-sync that cleared the last
known test failure.

The session recap keeps the reusable part rather than just the outcome: the
three CI blockers had three unrelated causes (stale branch / stale plan number /
real debt) and triaging them as one bucket would have produced three wrong
fixes. Also records the evidence discipline that paid off — planted defects over
green runs, reading the CI log rather than the badge, baselining before blaming,
and treating a recommendation refuted mid-execution as refuted rather than
authorized.

Open items carried forward: Phase 4, tasks/todo.md's nonexistent plan-of-record
path, docs/commands.md:10's "17 Categories" definition question, and 2
branch-guard false positives.

R09 status-drift verified back to its pre-existing baseline: my first draft said
"preserved for the v4.0.0 tag", which tripped Check B — .STATUS's milestone is
ONE line containing "SHIPPED", so every vX.Y.Z token on it must resolve to a real
git tag, and v4.0.0 is not cut yet. Reworded to "the eventual v4 release tag".
The 2 remaining warnings (v2.61.1, v1.16.0) are pre-existing historical prose,
confirmed by running the checker against HEAD's .STATUS in the real repo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… positives (#292)

* fix(guard): strip quoted spans + scope writes to PROJECT_ROOT

branch-guard.sh's Bash write-through detection (Patterns 1-4) coarse-scanned
the raw command string for shell metacharacters with plain grep, which has
no notion of quoting. Three false positives fired live in one session,
including during an audit of this exact bug class:

  awk 'NR>=203 && ...'                    -> '>' inside a single-quoted
                                              awk program read as a redirect
  grep -E '>[^=]' ...                     -> same, inside a grep pattern
  grep -n '...\"cp \|...' ...              -> literal "cp " substring inside
                                              a quoted search pattern read
                                              as a cp invocation

Same failure class Group 14c (2026-07-10) already fixed for heredoc bodies
via HAS_HEREDOC; this generalizes it to any quoted span, not just heredoc
bodies. COMMAND_SCAN strips quoted-span CONTENTS before the coarse `grep -q`
presence checks; extraction (`grep -oE`) still runs against the ORIGINAL
$COMMAND, so a real target quoted for spaces is unaffected.

A fourth, unrelated bug fired alongside it: `cp <file> /tmp/...` was flagged
as "creates a new code file on dev" even though /tmp is nowhere near the
repo — only /dev/* was excluded. Added a PROJECT_ROOT prefix check on the
resolved write target. PROJECT_ROOT is a realpath (`git rev-parse
--show-toplevel`); $CWD often isn't (macOS's /tmp -> /private/tmp, and
`mktemp -d` returns the /tmp form) — a naive literal-prefix compare broke
6 pre-existing in-repo-write tests during development. Fixed by
canonicalizing the target's directory via `cd && pwd -P` (POSIX, avoids
realpath/readlink -f, which aren't universally available) before comparing.

Verified by planted defect AND by replaying the actual live false positives
verbatim against the fixed hook (all resolve to exit=0), plus an isolated
confirmation that a genuine redirect on a fresh dev-branch repo still
returns exit=2 (blocked). New Group 22 (9 tests): 5 catch the false-positive
class (including the 2 real historical commands), 4 are regression guards
proving the fix doesn't weaken real detection (quoted target with spaces,
cp inside the repo, a real touch alongside an unrelated quoted decoy).

Full suite: 133/133 (was 124/133 mid-development, before the pwd -P fix).
E2E: 30/30 (1 skipped, unrelated). Full pytest: 2601 passed / 1 failed (the
1 is the expected repo-vs-installed-hook divergence this branch itself
causes, cleared by install-guards.sh — same pattern as PRs #290/#291).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(guard-audit): correct the skill's fictional config schema

Discovered while running this skill to audit the false positives fixed in
7b51969. Step 5's documented config schema (nested "branches" object,
"version": 2, allowed_extensions, pr_body_scan, force_push_allow,
dev_allowed_extensions) is read ZERO times by scripts/branch-guard.sh —
grepped for every one of those keys, all 0 matches. The real schema is a
flat branch-name -> protection-level map, read via a single lookup,
_json_get ".\"${BRANCH}\"" (branch-guard.sh:234). Following the skill's own
instructions would have produced a config file the guard silently ignores.

Also removed a fabricated "destructive git in PR body" rule (grepped the
script for pr_body/PR body/--body: 0 matches — no such scanning exists) and
replaced Step 4's fictional recommendations with the two real friction
classes this session actually found: quoted-span misdetection and
missing path scoping, neither fixable via any config key.

Added a classification step (Step 4) splitting false positives into
branch-policy (config-fixable) vs. detection-logic (requires a code PR to
branch-guard.sh, not a config change) — the skill's "never modify
branch-guard.sh" constraint was previously silent on what to do when the
bug IS in branch-guard.sh; now it says so explicitly rather than implying
a config workaround exists for everything.

Verified: skill_standards_audit.py 100/100, markdownlint clean, description
400 chars (well under the 1536 cap).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* perf(guard): collapse COMMAND_SCAN to one sed invocation

7b51969's quote-stripping added printf | sed | sed (3 forks) to every
Bash tool call on dev/main. dev's own dogfood perf budget
(test_branch_guard_under_200ms) went from 207ms (pre-existing, already
marginal on this machine — same class as the test_performance_cached_run
xfail note re: unreliable wall-clock timing on shared runners) to 236ms.

Collapsed to a single sed invocation (two -e expressions, here-string
instead of printf|pipe) -> 3 forks to 1. Re-measured 0.80-0.90s per pytest
invocation across 3 runs, consistently under budget. Full suite (133/133),
E2E (30/30), and full pytest (2602 passed / 0 failed) all still green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(guard): correct cross-boundary quote-pairing regression in COMMAND_SCAN

Adversarial review of PR #292's own Group 22 tests surfaced a real false
negative the quote-stripping fix introduced: two independent sed passes
(single-quote pass, then double-quote pass) let a single-quote-pair span
across two unrelated double-quoted strings that each contain one
apostrophe (e.g. "it's" ... "don't"), erasing a real redirect sitting
between them from COMMAND_SCAN. Confirmed live: a genuine `> new_file.py`
write-through on dev went completely undetected.

Fix: combine both quote types into one alternation pattern
(`'...'|"..."`) so quote-type resolves at the first quote character
encountered and can never pair across a boundary of the other type.

Verified via planted-defect replay: the new regression test fails
against the reverted two-pass sed and passes against this fix; full
suite 134/134.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Written BEFORE any v4.0.0/folio-v1.0.0 tag exists, per the plan's own
scheduling (T4.2 has no dependency on T4.0/T4.1/T4.1b/T4.1c and gates
T4.3). Covers:

- Path A: craft revert map (dev->main merge-commit revert, Homebrew
  re-point via craft.rb url+sha256, GitHub release annotated not
  deleted since a prior good version exists to point users to).
- Path B: folio revert map (release+tag deletion appropriate here
  specifically because this is folio's FIRST release - no prior version
  exists, so nothing to annotate a "superseded" note toward; formula
  removed entirely, not downgraded, since it's created fresh in T4.3).
- Explicit sequencing invariant: craft must not tag v4.0.0 before
  folio's release is confirmed fully live.
- Path C: the partial-rollback question (folio ships, craft stalls) is
  deliberately left unresolved - stated as a question with arguments on
  both sides, not answered. This was flagged during Phase 4 planning as
  needing a human decision; the runbook's job is to make sure whoever
  hits this scenario sees the question, not to guess an answer for them.

Homebrew automation detail confirmed by reading
homebrew-release.yml: it pushes directly to homebrew-tap's main via a
GitHub App token, no PR step - the runbook's revert commands reflect
that (direct commit+push, not a PR-and-merge cycle, for the tap repo
specifically; craft's and folio's own main branches DO go through a
revert PR, per their branch protection).
T4.0-T4.2 complete (folio sync, /folio:do router+tests+docs, rollback
runbook PR #293). T4.3 complete: folio v1.0.0 released, homebrew-tap
manifest entry (PR #141) + test_paths audit fix (PR #142), brew
install/audit verified clean. Adds the two /goal-ready SPEC files
authored during Phase 4 planning.
…T4.3)

folio.rb was added to homebrew-tap's manifest in T4.3 (PRs #141/#142);
homebrew.md's hardcoded count and table were stale, causing
validate-counts.sh's cross-repo homebrew.md vs manifest.json check to
fail (2 dogfood test failures).
Version bump from 2.61.2 to 4.0.0 via bump-version.sh (47 commands, 2
agents, 40 skills, 64 specs). Converts CHANGELOG.md/docs/CHANGELOG.md
Unreleased section to 4.0.0, expanding docs/CHANGELOG.md with the full
PR list since 2.61.1 (#279 through #293).

Closes gaps surfaced by pre-release-check.sh's doc-coverage and
docs-staleness gates:
- Missing REFCARD rows (code:demo, git:issue-check) and mkdocs.yml nav
  entries (code:demo, site:deploy); new docs/commands/code/demo.md and
  docs/commands/site/deploy.md written (no prior site page existed for
  either command).
- audit-router skill (T3.6) was never added to docs/skills-agents.md.
- v4 rollback runbook added to mkdocs.yml nav.

pre-release-check.sh 4.0.0: PASSED (remaining items are cross-repo or
pre-existing advisories: homebrew-tap's craft.rb desc count, a
pre-existing internal-doc nav gap, and .STATUS drift notes).

Full pytest suite: 2637 passed, 0 failed. CI bash suites all green
(branch_guard_e2e's sole failure is the documented runner-sensitive
perf test, confirmed via SKIP_PERF).
@Data-Wise
Data-Wise merged commit 678e4d4 into main Jul 16, 2026
13 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