npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

claude-sprint-gate

v9.10.1

Published

Claude Code Sprint Gate — autonomous sprint lifecycle manager with pluggable scorecards. v8.3 introduces multi-agent mode foundation — master is the default role; /sprint-role slash command + role file mechanism + per-session NATO nickname auto-assignment

Downloads

5,046

Readme

CCSG — Claude Code Sprint Gate

Current version: 9.10.1 · npm · GitHub

What's new — v9.10.1: review-sprint detection regex tighten (hotfix)

Dogfood-surfaced false-positive in the v9.10.0 review-sprint detection. The fallback filename heuristic at plugin/hooks/ccsg.sh:311 matched any sprint slug containing the word "review" (case-insensitive) — including v9.10.0's own ship sprint about the reviewer feature. The Stop hook then demanded a review_status on what was actually a normal feature-ship sprint.

Fix. Tightened the filename fallback regex from grep -qi 'review' to grep -qE 'review-correction-for' — the canonical infix produced by ccsg.sh's review-sprint generator (sprint-S<N>-review-correction-for-S<SOURCE>-<slug>.md). The type: review frontmatter check remains the primary signal and is unchanged; hand-authored review sprints with non-canonical filenames should declare it explicitly.

Impact.

  • Generator-produced review sprints: identical detection (still match by canonical filename + frontmatter).
  • Hand-authored review sprints: identical if frontmatter set; over-broad slug matching no longer trips false-positives.
  • Non-review sprints with "review" in the slug: bug → fixed.

No new features, no test surface changes, no behavior change for any other code path. 59/59 integration tests still GREEN.

What's new — v9.10.0: reviewer-role parity with CSG (Claude/Codex cross-tool collaboration)

CCSG and Codex Sprint Gate (CSG) now share a common reviewer-role contract over the same sprint-docs/.ccsg/ runtime state. The goal: a Claude session executes a sprint, a Codex session reviews it (or vice versa), with hard-gated separation between execution and audit even across tool boundaries. Existing CCSG functionality preserved bit-for-bit; reviewer mechanics are additive and signal-gated.

Reviewer role — first-class third role alongside master/worker:

  • Audit context, not development context. Cannot create execution claims, write product code, or modify development sprint files.
  • Uses separate claim namespace sprint-docs/.ccsg/review-claims/ (NOT sprint-docs/.ccsg/claims/). Hard-gated by claim-guard.sh reviewer-lane gates.
  • Source sprint creator/executor blocked from reviewing that source. Feedback author blocked from fixing the resulting correction sprint. Both gates enforced by claim-guard.sh.
  • Reviewer write-lane: only to sprint-docs/.ccsg/review-claims/*.lock and claimed sprint-open/*review*.md. Enforced by sprint-ownership-guard.sh reviewer recognition.
  • New scheduler token CCSG_ARM_REVIEWER joins the v9.9.2 four-token model (sentinel-only, no master claim). prompt-guard.sh also accepts CSG_* token prefixes for cross-tool compatibility in mixed CCSG/CSG projects.
  • New slash command /sprint-review for the reviewer claim + feedback workflow. /sprint-role accepts reviewer. /sprint-start --role=reviewer arms reviewer-flavored loop with CCSG_ARM_REVIEWER token.

Review sprint lifecycle in ccsg.sh (signal-gated, opt-in):

When a verification sprint completes, CCSG checks for ANY reviewer signal:

  1. sprint-docs/.ccsg/reviewer-required flag file exists
  2. Registered agents/*.json with role: reviewer
  3. Source sprint frontmatter review_required: true

If ANY signal: CCSG generates a pending review sprint at sprint-open/sprint-S<NEW>-review-correction-for-S<SOURCE>-<slug>.md with full frontmatter (type: review, review_status: pending, source creator/executor session_ids). Stop is blocked until a reviewer claims and resolves. No signal → no review sprint created — solo and master/worker projects with no reviewer signal see ZERO behavior change from v9.9.x.

State machine: pendingpass (archive + release reviewer claim) | findings (handoff to master/worker for correction; reviewer can stop with findings even if review-checklist incomplete — findings IS the handoff signal) → correction-ready (re-review required by separate reviewer session).

Hook coverage drift remedy:

CCSG hook files sprint-ownership-guard.sh, feature-charter-guard.sh, evidence-resolver.sh, scenario-witness-guard.sh existed but were missing from PreToolUse matcher registrations. v9.10 closes the drift:

  • Edit / Write / MultiEdit matchers now also fire sprint-ownership-guard.sh + feature-charter-guard.sh
  • Bash matcher now also fires evidence-resolver.sh + scenario-witness-guard.sh
  • (scenario-witness-runner.sh stays as command-style runner for /sprint-witness, NOT registered as PreToolUse hook)

deploy.sh, bin/ccsg.js, plugin/scripts/install-hooks.py, and plugin/hooks/hooks.json all updated to register these in the canonical matchers.

Library additions (plugin/lib/ccsg_agents.py):

  • _review_claims_dir(root) — helper for .ccsg/review-claims/ path resolution
  • _read_front_matter(path) — minimal YAML frontmatter reader for review-sprint type/status detection
  • find_claimable_review_sprint(root, session_id) — reviewer scan with source/feedback-author exclusion
  • try_atomic_review_claim(root, review_sprint_n, source_sprint_ref, session_id, nickname) — atomic claim with O_EXCL
  • release_review_claim(root, review_sprint_n, session_id) — release owned-by-session
  • CLI dispatches: find-review-claimable, review-claim, release-review

Cross-tool collaboration substrate:

CCSG and CSG share sprint-docs/.ccsg/ runtime state. Reviewer-role mechanics enforce separation-of-concerns regardless of which tool armed which role. A typical cross-tool workflow:

  1. Claude (CCSG) session armed as master → executes sprint S
  2. Reviewer signal active (reviewer-required file or registered Codex reviewer agent)
  3. CCSG ccsg.sh creates pending review sprint when verify archives
  4. Codex (CSG) session armed as reviewer → claims review, audits source/proof/verify, writes pass/findings
  5. If findings: master (Claude or Codex) corrects, sets correction-ready, separate reviewer re-reviews

Token compatibility: prompt-guard.sh accepts both CCSG_* and CSG_* token prefixes. CCSG-generated prompts always use CCSG_*; CSG-generated prompts use CSG_*; both sides recognize both for cross-tool tick continuity.

Preservation guarantees (production-critical):

  • All v9.9.2 token-arm semantics preserved: CCSG_ARM_SESSION (master claim), CCSG_ARM_WORKER / CCSG_TICK_SESSION (sentinel-only). New CCSG_ARM_REVIEWER joins as fourth sentinel-only token.
  • All v9.9.2 ownership-verification semantics preserved on /sprint-stop.
  • All v9.9.0 anti-deflection / operator-input-as-directive doctrine preserved.
  • Master/worker workflows are bit-for-bit identical to v9.9.2 in all projects without reviewer signals.
  • Newly-registered hooks (sprint-ownership-guard, feature-charter-guard, evidence-resolver, scenario-witness-guard) ALREADY default to passthrough when their preconditions don't apply — silent no-ops for non-reviewer workflows.

What's new — v9.9.2: hotfix — master-claim session-binding + tick self-recognition

Empirical incident 2026-05-06: a cron-fired loop-tick session hijacked master role from the active master, then a subsequent operator /sprint-stop in the tick session cascadingly nuked the prior master's state files (cron cancelled, .ccsg-active-session deleted, role file deleted, agent file deleted). Root cause: the v9.7 loop-prompt template carried the CCSG_ARM_SESSION token (the same token /sprint-start uses for master claim), so every cron-fired tick session triggered loop-detect.sh's master-claim logic and overwrote .ccsg-active-session with its own session_id. Combined with /sprint-stop master-path keying off the role file (not authoritative session), any tick session that briefly held master could nuke the real master's state.

v9.9.2 fixes the bug at three layers:

  • New token: CCSG_TICK_SESSION — used by the loop-prompt template (cron + ScheduleWakeup re-fires) instead of CCSG_ARM_SESSION. loop-detect.sh recognizes the new token as sentinel-only — it does NOT claim .ccsg-active-session, does NOT overwrite master ownership. Master role belongs only to whoever last did /sprint-start. Three-token model now: CCSG_ARM_SESSION (operator-explicit master claim), CCSG_ARM_WORKER (worker, sentinel-only), CCSG_TICK_SESSION (loop tick, sentinel-only).
  • TICK PRE-FLIGHT in the loop prompt — the Step 2 loop-prompt template now opens with a probe-and-compare block that probes the tick session's session_id (via the v8.4.11 nonce mechanism), reads sprint-docs/.ccsg-active-session, and exits cleanly with STALE_TICK: if the tick session is NOT the current master. Stale ticks from superseded masters self-disqualify; they don't even attempt sprint planning. No token waste, no accidental work, no opportunity for cascading state damage.
  • /sprint-stop master-path ownership verification — before any destructive op (cron cancel, role file delete, .ccsg-active-session removal, per-agent file delete), /sprint-stop verifies the calling session_id matches .ccsg-active-session. Mismatch → REFUSED with explicit error message naming the actual current master. Prevents the hijack-cascade pattern where a stale role file lets a superseded session nuke the live master's state.
  • /sprint-start semantics preserved — operator's explicit /sprint-start invocation still claims master and overwrites any prior claim unconditionally (per operator directive: "a new agent should claim master by default and overwrite the last master agent-id"). The fix is targeted at AUTOMATED tick sessions; operator-explicit commands are honored.
  • Backward-compatibleprompt-guard.sh accepts all three tokens; existing in-flight CronCreate jobs that still carry CCSG_ARM_SESSION (legacy v9.7-v9.9.1 prompt templates) continue to fire without breaking. Their tick sessions will trigger the v8.4.3 master-claim path, but the new ownership-verification in /sprint-stop and the existing sprint-ownership-guard prevent damage. Within 7 days (cron auto-expiry) all in-flight legacy crons expire and the new template takes over fully.
  • Version + marker bumped 9.9.1 → 9.9.2. No new hooks added; no deploy.sh changes required. Three files touched in plugin/: loop-detect.sh, prompt-guard.sh, commands/sprint-start.md Step 2, commands/sprint-stop.md Step 0.5.

Why ship this hotfix immediately: the bug actively corrupts master state on every operator's machine that runs a cron-tick + /sprint-stop sequence. The empirical incident produced 4+ minutes of degraded enforcement and required manual recovery (clear stop flag + re-arm). Without the fix, every long-running CCSG-armed project is exposed.

What's new — v9.9.1: hotfix — ccsg --repair-hooks for settings.json restore

Empirical incident on 2026-05-05: a third-party agent-harness plugin's install-hooks step overwrote the PreToolUse Edit|Write|MultiEdit|Bash matcher block in ~/.claude/settings.json, dropping every CCSG hook (claim-guard, sprint-first-guard, spec-guard, loop-detect, prompt-guard, verify-structure-guard, custom-mode-citation-guard, safe-push-guard, storyrun-discipline-guard) silently. Active CCSG-armed sessions kept running but with the entire enforcement layer dead — no sprint-first BLOCK, no claim-ownership BLOCK, no verify-doc structure check, no probe-nonce response. Master sessions could /sprint-start and appear to arm but .ccsg-active-session would never get written because the hook that responds to the CCSG_ARM_SESSION token wasn't registered.

v9.9.1 ships a recovery path:

  • New: plugin/scripts/install-hooks.py — idempotent merge installer. Reads existing ~/.claude/settings.json, adds CCSG's required hook entries to PreToolUse / UserPromptSubmit / Stop, preserves every non-CCSG entry (other harness plugins, custom user hooks). Skips duplicates by hook-filename match. Safe to run repeatedly.
  • New: ccsg --repair-hooks — operator-facing wrapper command. Invokes the merge installer with sane defaults. Optional --dry-run flag previews the diff without writing. Run after any time another tool's installer might have stomped on CCSG's hooks.
  • Backup before write — every merge run creates <settings>.pre-ccsg-merge-backup so any operator can roll back.
  • Version + marker bumped 9.9.0 → 9.9.1. Block-template teaching unchanged from v9.9.0; this is purely a recovery / install-resilience patch.

Why ship this: in a multi-tool agent ecosystem (Claude Code + CCSG + goldmem + Cursor + …), settings.json is shared state that every tool's installer touches. Tools that overwrite matchers instead of merging will silently disable other tools. CCSG can't prevent third-party tools from doing this, but it CAN provide a one-command recovery path so the operator's reaction is ccsg --repair-hooks instead of "edit the JSON by hand."

What's new — v9.9.0: operator-weight-inversion strip

Empirical evidence from a 2026-05-03/04 cold-start session (Pulscale UI/UX migration arc + goldmem standalone bootstrap, ~13 hours of operator wall-clock) showed CCSG was treating operator-typed slash commands and stated directives as conversation openers rather than as directives. Six instances of the same failure pattern surfaced in one day; the operator had to re-state the same directive multiple times to override it.

v9.9 strips the deflection across the framework:

  • /sprint-start Scenario B auto-chains /sprint-setup — when CCSG infrastructure is missing, the framework runs setup automatically as the first phase of /sprint-start execution, then continues to arming. The "Path A (full setup) vs Path B (skip CCSG)" menu is removed; the slash command IS the directive.
  • /sprint-start Steps 3 + 4 are now mandatoryScheduleWakeup + CronCreate fire as part of arm completion. Agent must not ask "should I fire the loop?" after operator already invoked the command that fires it. If either tool call fails, agent reports the specific error and fails loudly, never offering "would you like to retry / skip / fire only the cron."
  • /sprint-setup Phase 0 — Absorb existing requirements doc — when the project already has SPEC.md / PRD.md / ISSUES.md / REQUIREMENTS.md / any *.md with ## Requirements / ## Goals / ## Mission / ## User Stories / ## Out of Scope / ## Stop Condition style headings, derive mission.md / strategic-intents.md / stop-condition.md from it with one confirmation prompt — instead of running a from-scratch interview that re-extracts content the operator already wrote down. From-scratch interview is now the fallback only when no absorbable doc exists or operator declines absorption.
  • Past-version banner stamps stripped from user-facing hook outputCCSG v7.0 ADVISORY (non-blocking)CCSG ADVISORY (non-blocking); CCSG ARCHIVE-CLEANLINESS (v9.0 ADVISE)CCSG ARCHIVE-CLEANLINESS (advisory); etc. The framework is at v9.9 — banners that announce themselves as "v7.0" feature versions erode trust by suggesting outdated framework. The live framework version stamp in claude-md-block-template.md BEGIN marker stays — that's the anti-stacking marker.
  • Past-version annotations stripped from command-doc headings## Invocation: /sprint-feature --amend FT<NN> (v7.7.0)## Invocation: /sprint-feature --amend FT<NN>; (v8.4.1 LOAD-BEARING)(LOAD-BEARING); etc. The agent reads command docs as instruction; "(v7.7.0)" history annotations contribute nothing to current execution and look like outdated guidance.
  • New CLAUDE.md block teaching: "Operator input is the highest-priority signal — never re-extract via deflection" — explicit doctrine documenting the failure pattern (Path A/B menus, "would you like" questions, "Recommendation:" blocks re-framing executed directives) and the principle that replaces it (the slash command is the directive; the framework executes; halt only with a single named question, never a menu).

Why it matters: the operator is one, finite, irreplaceable. The agent is fungible, scales with compute. Every menu the agent offers extracts operator life-minutes. The framework's job is to convert operator input into agent constraint mechanically, never to ask for the same answer twice.

Not in this ship (deferred to v9.10): heartbeat side-channel corrupt-file recovery, .ccsg-active-session auto-claim leak fix, regression test for v9.8.1 hotfix, per-agent-file current_phase updates, ccsg skills doctor command. v9.9 is purposely thematic — one root cause, six surface fixes.

What's new — v9.8.0: block compaction (version archaeology stripped, duplicates removed)

User caught a regression: the deployed CCSG block in CLAUDE.md was 166k chars (4× the Claude Code 40k performance-warning threshold) — and tonight's v9.4-v9.7 ships ADDED ~95 lines without extracting the duplicates the glossary system was supposed to remove. The block grew when it should have shrunk.

v9.8 does the extraction pass:

1. Verbatim duplicate removed. The Visual Verification Depth Standard section had ~46 lines duplicated VERBATIM (per-work-type minimums + "no observable surface" rejection + "why this exists" — all appeared twice). One copy retained.

2. Version archaeology stripped. ~72 instances of (vX.Y — LOAD-BEARING) style parentheticals in headers, plus inline narrative version mentions ("v7.8.0 introduced...", "v8.6 replaced...", etc.) — all pure historical reference, useless in the deployed block. Deployed CCSG block only needs to know "this is v" via the marker line; in-content version archaeology is dead weight.

3. Implementation code blocks elided. A 22-line React/JSX implementation example for the capture-mode URL banner pattern compacted to a 1-sentence description. Implementation details belong in project skills/docs, not framework block.

4. Historical-justification narrative cut. "Why this earns a major version", "Migration from v7.9.x", "Pre-v8.5 sprints produced..." paragraphs removed — these documented WHEN the rule changed, not WHAT the rule is.

Honest framing: ~10k chars cut (~6% reduction). Far short of the 50% target proposed earlier — the block is mostly LOAD-BEARING teaching that can't safely be removed without architectural redesign (lazy-loadable extension files, auto-skill activation, etc., for v10.0). The 6% cut is the safe extraction; further reduction needs structural change, not just stripping.

v9.8 still doesn't get the block under 40k chars. That's a v10.0 architectural problem, not a v9.x compaction problem. v9.8 makes the block ~6% smaller; v10.0 will require splitting the block into core + lazy-loaded extensions.

Tests: 40/40 GREEN. All 10 spot-checked load-bearing rules confirmed surviving cuts (CCSG_ARM_SESSION, Phase 1, Gate 18, Three-pillar scoring, fanout, honesty drops, post-cleanup, Try harder, byte-identical to goal, etc.).

What's new — v9.7.0: minimal loop-tick prompt (fixes the copy-paste flood regression)

Pre-v9.7 /sprint-start.md taught master to copy a ~2,000-char multi-paragraph instruction block VERBATIM into every ScheduleWakeup + CronCreate call. The "COPY VERBATIM" rule was load-bearing for the CCSG_ARM_SESSION token (prompt-guard requires it), but the rest of the block — WHAT-TO-DO-NOW step list, mode-driven planning protocol, intent-filter walk, CUSTOM-mode arc resolution, AT-END-OF-TURN protocol — accumulated over versions because each new teaching seemed safer to embed than to trust to CLAUDE.md being read.

Result: every wakeup fire echoed ~2,000 chars as <command-args> in the user-facing turn. Operator chat steering only added to it (CURRENT STATE / OPEN OPTIONS observed accumulating to ~2,400 chars across this session) — copy-paste flood every 4-5 minutes.

v9.7 fixes this:

1. /sprint-start.md Step 2 — minimal loop prompt (~500 chars). Required content:

  • ARM bash block (preserves CCSG_ARM_SESSION token; satisfies prompt-guard)
  • One-line directive: "Continue per CCSG block teaching in CLAUDE.md"
  • End-of-turn protocol (stop check + ScheduleWakeup forward)

Everything else — WHAT-TO-DO-NOW, mode protocol, MATURITY/USECASE/CUSTOM definitions — moved to the CCSG block teaching, which is auto-loaded from CLAUDE.md at every session start.

2. CCSG block — new "Loop-tick discipline (v9.7 — LOAD-BEARING)" section. Codifies:

  • The wakeup prompt is sacred; never augment with state
  • State lives in files (.active-scorecard, chain-queue.json, sprint-open/, .next-decision)
  • Operator chat steering becomes a memory rule (S1057 template), not prompt augmentation
  • Why the runtime <<autonomous-loop-dynamic>> sentinel does NOT apply (sentinel expansion would lose CCSG_ARM_SESSION; prompt-guard would then block)
  • Recovery for bloated prompts mid-session: /sprint-stop + /sprint-start

3. Note on the runtime sentinel. The Claude Code harness has a <<autonomous-loop-dynamic>> sentinel for /loop autonomous mode. It does NOT work for CCSG because runtime expansion drops CCSG_ARM_SESSION and prompt-guard blocks the call. CCSG's own minimal prompt (with ARM token preserved) is the equivalent solution.

Adoption (existing master sessions running on bloated v8.x-v9.6 prompts):

  • Old loops continue with bloated prompts until /sprint-stop + /sprint-start
  • After re-arm, master picks up the v9.7 minimal prompt template
  • For Pulscale specifically: /sprint-stop (master alpha), then /sprint-start <roadmap> to re-arm with v9.7

Tests: 39/39 GREEN (38 prior + 1 new asserting /sprint-start.md Step 2 prompt block is < 800 chars and contains CCSG_ARM_SESSION).

What's new — v9.6.0: proof depth signals (advisory hook + canonical reference)

Empirical analysis across the project's sprint-proof history surfaced a real degradation curve:

Era         CCSG ver  PNGs  Total KB   Pattern
─────────────────────────────────────────────────────────────────
Apr 21-25   v7.2.3    7-45  2-7 MB    story-UC-<section>-<persona>-<variant>-<element>.png
                                       multi-format (PNG + JSON + DER + TXT + MP4)
                                       single-spec-many-phases
Apr 28+     v8.0+      0-1   5-137 KB  collapse — 1 PNG or text-only
May 2-3     v9.0-9.5   5-7   1.4-1.8MB partial recovery, no naming convention

The v8.x rollout doubled CCSG block size (multi-agent foundation, claim discipline, archive cleanliness, 18 gates, three modes, glossary system) and the storyrun depth discipline got crowded out. By v9.5 master was capturing PNGs but applying the depth standard loosely — sub-50KB viewports, no light/dark variance, no storyrun naming, captions that didn't match PNG content.

v9.6 closes the loop with advisory mechanical signals that direct the LLM to self-correct:

1. depth-validation.sh (NEW Stop hook, ADVISORY). Scans recently-modified sprint-proof/sprint-N/ directories and surfaces named gaps:

  • Light/dark pair file-size similarity > 95% → "theme may not be implemented for the captured surface"
  • PNG file size < 50KB on UI routes → "clipped or sparse?"
  • PNG count vs work-type — storyrun rows expect >= 8 PNGs, UX proof >= 5
  • Missing story-<UC>-<section>-... filenames when storyrun row cited
  • SHA-duplicate PNGs → "padded duplicate; captures must show different DOM state"

Always exits 0. Output is informational. The LLM reads, decides whether to re-shoot, mark partially-met, file a follow-up sprint, or override with reasoning written into the verify doc.

2. CCSG block: canonical proof-shape reference. New section "Proof depth — canonical reference" pointing at sprint-proof/sprint-554/ as the depth target. Codifies:

  • Filename convention: story-<UC>-<section>-<persona>-<variant>-step-NN-<element|full|clip>.png
  • Multi-format expectation (PNG + JSON + DER + TXT + MP4 + webm)
  • Single Playwright spec covering all phases
  • Inline-transcription rule for verify docs (read PNG before grading; transcribe representative content)
  • Honest-failure framing — PNG capturing a known bug = honest evidence, not failure; mark partially-met + file follow-up, don't false-PASS around the bug

3. Design rule codified (saved memory). All quality dimensions are ADVISORY hooks. BLOCKING is reserved for binary-verifiable mandatory things (sprint file on disk, claim ownership). Quality is multi-dimensional and judgment-dependent — mechanical pass/fail kills the LLM's ability to apply context. Pattern matches v9.5 glossary-lint exactly.

Adoption: npx claude-sprint-gate installs the new hook + registers it in the Stop chain (after glossary-lint). Existing global installs need a manual settings.json append OR a re-run of npx claude-sprint-gate. Pulscale's CLAUDE.md picks up the v9.6 block at next /sprint-model-rebuild or via direct block swap.

Tests: 37/37 GREEN (33 prior + 4 new for v9.6).

What's new — v9.5.0: glossary system complete (auto-create + auto-grow + lint hook)

v9.4 introduced the glossary convention. v9.5 closes it out end-to-end:

1. Auto-create on setup. npx claude-sprint-gate now caches glossary-ccsg-template.md + glossary-project-template.md at sprint-docs/.ccsg/. The new /sprint-setup Phase 6.6 copies both to repo root if missing — glossary-ccsg.md (framework vocabulary, ready-to-use) + glossary-project.md (scaffold with empty sections + auto-detected project terms stubbed).

2. Auto-grow on /sprint-start. When the operator arms a session via /sprint-start, the agent scans the active scorecard / roadmap docs for project-specific vocabulary (bracketed tags, persona names, caveat IDs, repeated product nouns) and adds stub entries to glossary-project.md if not yet defined. Stubs are 1-line placeholders the agent expands as planning surfaces more meaning. Marked with <!-- stub — fill in --> so future passes can find and complete them.

3. Auto-grow during planning. New CCSG block teaching: when the agent encounters a project term in a roadmap or sprint plan that isn't in the glossary yet, it ADDS THE STUB THEN-AND-THERE rather than inline-defining (which would re-create the drift trap v9.4 was built to prevent).

4. Lint hook (ADVISORY). glossary-lint.sh registered as a Stop hook. Scans recently-modified files in sprint-open/, sprint-closed/, docs/, and project root for <term> references. Warns when a term lacks a glossary definition. Excludes content inside ``` fenced code blocks to avoid false positives. Always exits 0 — pure advisory in v9.5; promotion to BLOCKING is queued for v10.0 once the convention proves out in production.

5. Block substitutions continue from v9.4 — additional sections of the CCSG block migrated to <term> references (the <custom>-mode section, mode comparisons, etc.). Targeted, not exhaustive — full block migration to <term>-only is gradual; first-occurrence definitions are preserved everywhere.

6. CCSG block teaching update. The "Glossary" section now reflects auto-grow behavior, references both glossary files (project too, not just framework), and points at the lint hook as a drift signal.

Adoption:

  • New projects: npx claude-sprint-gate + /sprint-setup — both glossary files seeded automatically.
  • Existing projects: /sprint-model-rebuild pulls v9.5 block; manually copy templates from ~/.claude/glossary-ccsg-template.md and ~/.claude/glossary-project-template.md to repo root if not yet present (or re-run /sprint-setup to trigger Phase 6.6).
  • Pulscale (already adopted): no action — v9.5 block teaching activates on next compaction.

Tests: 32/32 GREEN (29 prior + 3 new for v9.5 lint hook + glossary auto-copy paths).

What's new — v9.4.0: glossary convention (<term> references for shared vocabulary)

The CCSG block in CLAUDE.md is loaded every session start + after every compaction. Measured redundancy: 12 inline re-explanations of Class A/B/C, 14 of mode names, 12 of "claim file", 7 of "strategic intent" — each carrying 3-5 lines of context. The same vocabulary drift hits projects: tonight's [OPERATOR]-tag doctrine got two corrections in 12 hours because the definition lived in three places (memory, roadmap notes, sprint plans).

v9.4 introduces a single source of truth for vocabulary:

  • glossary-ccsg-template.md bundled in the npm package. Copy into your repo root as glossary-ccsg.md to adopt. ~30 entries: <sprint>, <plan>, <verify>, <archive>, <arc>, <capability>/<integration>/<witness> for evidence classes, <usecase>/<maturity>/<custom> for modes, <claim>, <liveness>, <heartbeat>, <intent>, <gate>, <row>, etc.

  • CCSG block teaching update: a new "Glossary (v9.4 — LOAD-BEARING — READ FIRST ON SESSION START)" section instructs the agent to read glossary-ccsg.md on session start. Project-specific vocabulary (your personas, product nouns, tags) lives in glossary-project.md — same repo root, project-owned. Whenever a <term> (in prose) or [TAG] (in roadmap rows) appears, it resolves to a glossary entry. Doctrine corrections become single-file glossary edits — every doc using <term> inherits the new definition automatically.

  • Targeted block substitutions: high-frequency inline definitions in the block template now use <term> markup. Example: the canonical mode definition section says "Three modes (full definitions: <maturity>, <usecase>, <custom> in glossary-ccsg.md)" instead of repeating the def at every reference. The Three-class evidence rule uses <capability> / <integration> / <witness> tags. Targeted, not exhaustive — full block migration to <term>-only is queued for v9.5.

Tokenizer rationale. Single common-English words inside <> (<operator>, <capability>) tokenize to ~3 BPE tokens — fragmenting a hyphenated key like <atomic-temp-rename> would cost ~7. The <term> reference cost is constant; savings come from removing inline re-explanations.

Adoption (existing projects). Run /sprint-model-rebuild to pull the v9.4 block into your CLAUDE.md, then copy glossary-ccsg-template.md from the framework install into your repo root as glossary-ccsg.md. Optionally create glossary-project.md for your project-specific vocabulary. Pulscale is the reference implementation as of 2026-05-02.

Backwards compatible. Projects without glossary-ccsg.md still work — the block teaching degrades gracefully (no crash, just no term-lookup).

What's new — v9.3.0: heartbeat side-channel (mechanical liveness)

Master demonstrably skipped the per-tick update_own_agent({'last_heartbeat': ...}) instruction across long-running sessions — observed: zero agents/<nickname>.json mtime updates across 9 sprints / 2h+ in a v9.2 production run, while claim-guard.sh fired hundreds of times in the same window. Same pattern as the v9.1 → v9.2 depth-standard episode: when teaching does not move behavior, mechanical enforcement does.

v9.3 moves heartbeat publication out of agent responsibility entirely:

  • claim-guard.sh now refreshes last_heartbeat on the per-agent file matching the firing session's session_id, on every tool call (Bash/Edit/Write/Read/etc.), regardless of TOOL_NAME or sprint-relatedness.
  • The update fires BEFORE the v9.0 solo-mode passthrough so it works in both solo and multi-agent (heartbeat is observability, not enforcement).
  • Atomic temp+rename; lookup is by session_id match; failures are silent so the hook chain is never broken.
  • Per-tick update_own_agent({'last_heartbeat': ...}) instruction is REMOVED from CLAUDE.md teaching (no longer agent's job). Other fields (current_sprint, current_phase) remain agent-managed.

Net effect: agents/<nickname>.json mtime now tracks actual session liveness with ≤30s freshness during active work. Multi-signal liveness (heartbeat + git-activity + PID) returns alive on signal 1 instead of needing the git-activity fallback. Worker-spawned sessions checking master see fresh state without a roundtrip.

Trade-off documented: crash-recovery delay grows from "immediate on first wakeup tick" to "≤ 30 min (heartbeat threshold) + hourly cron" worst case. Acceptable: hourly cron always recovers, and the prior fast-reclaim path only mattered in multi-agent traffic that has not yet been observed.

What's new — v9.2.0: Depth Standard tightened with mechanical path triggers

v9.1 restored the depth standard to CLAUDE.md but master continued shipping thin proof for UI-touching sprints (S1024, S1025, S1031, S1033 — 4 of 7 had ≤3 KB total proof, no PNG, despite touching cloud/src/app/ route components and visible UI surfaces). The "MANDATORY full-viewport PNG" language was too soft — interpreted as "preferred."

v9.2 sharpens to mechanical, path-based triggers:

Path-triggered MANDATORY rules — no judgment call

If the impl commit touches ANY file matching the patterns below, a .png (or video) under sprint-proof/sprint-N/ is MANDATORY regardless of sprint size or LOC count:

cloud/src/app/**            → full-viewport PNG with browser chrome
cloud/src/components/**     → PNG showing component rendered in context
cloud/src/lib/uc01/**       → if effect renders in UI listing → PNG of that listing
web/**                      → PNG with browser chrome
src/components/**, src/app/**  → PNG required
**/*.tsx, **/*.jsx (route/page/component)  → PNG required

Effect-aware trigger — the rule fires not only on direct UI file changes but on backend changes whose effect surfaces in UI. Editing cloud/src/lib/uc01/registry-backfill.ts (backend) to fix a UI listing requires a PNG of that listing, even though no .tsx was touched.

Light AND dark variants required when change affects appearance. Two PNGs minimum: 01-feature-light.png + 02-feature-dark.png.

State coverage required: empty + populated for lists; pristine + invalid + submitted for forms; working + broken for guards.

"No observable surface" phrase becomes a HARD STOP rejection. Writing it means the agent didn't look hard enough. Every change has an effect. The standard now lists 9 effect categories with their proof class:

  • Test added → terminal-transcript of test exit
  • Type fix → terminal-transcript of tsc --noEmit clean
  • Log emitted → log-line-capture grep
  • DB mutated → db-query-result SELECT
  • HTTP shape → curl-response
  • IaC → terminal-transcript of helm template + kubectl --dry-run=server
  • CI workflow → terminal-transcript of actionlint
  • Doc-only → terminal-transcript of link/path-resolution check
  • ...

Anti-pattern catalog added — explicit examples of NOT acceptable proof:

  • "I tested locally" (claim not proof)
  • Element-local crops without browser chrome
  • Single-theme PNG when colors changed
  • Markdown spec-mapping (restates code)
  • Source-diff .txt (already in git)
  • Tiny .txt for UI-touching sprint

Audit-replay closure test — before archiving, ask: "can a stranger reading only sprint-proof/sprint-N/ (no verify doc, no code) verify the change works?" If no, re-archive after capturing missing proof.

Sprint size does not relax depth requirements. A 12-line UI bug fix needs the same screenshot a 200-line feature does. Effort scales to the surface, not to the LOC.

No hook changes. Pure teaching tightening — the standard is mechanical so master can apply it without judgment calls about "small fix vs big feature."

What's new — v9.1.0: Visual Verification Depth Standard restored to teaching block

A 13-hour Pulscale dev session at v9.0 revealed that proof output regressed dramatically from pre-v8.5 levels: 4.5× fewer PNGs, 19× less proof data per sprint (~258 KB → ~15 KB average). Master shipped MINIMUM viable proof to satisfy the existence-only proof-gate, not BROAD coverage that proves the work.

Root cause traced: the depth standard (light/dark variants, working/broken states, full-viewport PNGs with browser chrome, 6 proof classes per the v6.7.1 catalog) lived inside verify-structure-guard.sh's error messages — fired only when a narrow Bash command pattern matched. When master used PowerShell / glob / two-step archive flows, the hook didn't fire. The standard disappeared from the model's context. v8.6 replaced the hook's depth check with existence-only. v9 made even existence advisory.

Meta-lesson: teaching that lives only in hook error messages is fragile — it reaches the model only when the hook fires. The CLAUDE.md teaching block is read on every tick + every compaction, so durable standards belong there.

v9.1 fix: Visual Verification Depth Standard added directly to the CLAUDE.md teaching block, with explicit per-work-type minimums:

  • UI surface changes — MANDATORY full-viewport PNG with browser chrome (URL bar visible). Light AND dark variants when appearance affected. State coverage (empty / loading / populated / error). Sequence proof for flows.
  • Backend / API changes — before-and-after db-query-result. curl-response for happy + error paths. log-line-capture when feature emits events.
  • Workflow / journey sprints — continuous capture (.webm trace OR sequenced PNGs). Full-viewport at every navigation. db/log proof of persisted side-effects.
  • Code-only / non-UI — test-passing transcript + compile-clean transcript + at least one downstream-effect proof.

"No observable surface" is explicitly named as a dead escape. Every change has an effect — find it, capture it.

No hook changes. Pure teaching restoration. The standard is back where the model reads it every tick. Hooks stay advisory. Master is expected to produce broad proof from the start, not be enforced into it.

What's new — v9.0.0: Autonomy Restoration

A 13-hour Pulscale dev session at v8.6 revealed that v8.4.6→v8.6.2 cumulatively traded autonomy for predictability. Each patch added a hook to defeat one observed failure; cumulative rigidity stopped master from self-healing. ~7 hours of solo-master downtime across hallucinations, dropouts, proof bypass.

v9.0 keeps mechanisms that solved real corruption classes and removes the rest. Three moves:

1. Solo-mode passthroughs. claim-guard.sh + sprint-ownership-guard.sh check agent count: if ≤1 agent registered, race protection is moot — passthrough. Restores pre-v8.4.6 solo-master cadence. Multi-agent mode keeps full discipline.

2. Advisory conversion. proof-gate-stop-hook.sh + archive-cleanliness-guard.sh flipped from exit 2 (block) to exit 0 + stderr warning. Master/operator sees the issue, decides whether to address. No more Q&A turns blocked on historical proof gaps.

3. chain-queue mechanism removed. The static parser requiring ## Sprint queue markdown table format is gone. CUSTOM mode restored to original contract: drop ANY doc with goals into /sprint-start custom <path>, master reads it with Read tool at plan-time, master interprets goals into sprints. No intermediate chain-queue.json, no symbol substitution at arm time.

Closure verification teaching added. Master explicitly counts sprint-closed/sprint-N-*-verify.md files matching the doc's stated scope BEFORE declaring an arc complete. Mechanical count, not prose interpretation. Prevents "35/35 closed" hallucination class without static parser.

Kept from v8.5/v8.6:

  • reclaim_ownership() — closes 5h+ dropout class
  • Per-agent files (agents/<nickname>.json) — eliminates registry race
  • Multi-signal liveness (heartbeat OR git activity OR PID)
  • safe-push-guard.sh — force-push prevention (still BLOCKS — corruption class)
  • sprint-first-guard.sh — sprint file before code edit
  • All v8.5 Python helpers

Removed/relaxed:

  • chain-queue parser requirement at arm time
  • $ROADMAP_PATHS placeholder substitution
  • "Master MUST read chain-queue.json first" teaching
  • Blocking behavior on proof-gate + archive-cleanliness (now ADVISE)
  • Solo-mode multi-agent overhead (claim-guard + ownership-guard skip when ≤1 agent)

Migration from v8.6.x:

  • chain-queue.json files become orphan but harmless (master no longer reads them)
  • Solo-master flows revert to pre-v8.4.6 simplicity
  • Multi-agent flows keep all protections
  • No breaking changes; drop-in compat

The litmus test for every line of v9 code: "would removing this break master's ability to ship in a way master couldn't recover from?" Most v8.6.x rules failed that test.

What's new — v8.6.1: proof-gate activity gate (UX fix)

v8.6.0 proof-gate Stop hook fired on EVERY Stop, including non-sprint turns. Real example tonight: user asked an interactive question about a UC, hit Stop, got blocked with the historical S999/S1000/S1002 proof-gap message. Gap is real but enforcing it on every conversation turn breaks normal interactive use.

Fix: activity-gate at the top of the hook. Pass through if either:

  • .sprint-stop-requested flag exists (sprint mode paused), OR
  • git status --porcelain shows no pending changes under sprint-open/, sprint-closed/, sprint-docs/, sprint-proof/ (no sprint work this turn)

Result: proof-gate enforces only when the current turn actually touched sprint state. Q&A, docs reads, framework chats, etc. pass through silently.

What's new — v8.6.0: coherent resilience (chain-queue + ownership reclaim + proof-gate)

A 13-hour Pulscale dev session at v8.5 revealed three structural failures that v8.5 didn't address: ~7 hours of solo-master downtime across two arc-boundary dropouts, one hallucinated arc closure ("35/35 done" — actually 0/35), and two zero-proof sprints (S1000, S1002) that shipped clean. v8.6 closes all three.

The three structural fixes:

  1. chain-queue.json — flat sprint queue across the chain, built at /sprint-start time by parsing each roadmap's ## Sprint queue section. Sprint numbers automatically renumbered to avoid collision with already-archived sprint numbers. Master walks this file mechanically as the next-sprint authority — does NOT re-read prose roadmaps at arc boundaries (which is where the hallucination class lives). Closure check is mechanical: all entries.status == "closed". Cannot be hallucinated from prose interpretation.

  2. Wakeup-tick ownership reclaim (reclaim_ownership()) — when a wakeup-spawned fresh session arrives and finds .ccsg-active-session references a session whose per-agent file is stale (>30 min) OR missing, the new session ATOMICALLY takes over. Closes the long-arc-boundary dropout class — wakeups spawned in fresh sessions auto-recover instead of triggering the kill-switch loop and requiring manual /sprint-start.

  3. Proof-gate Stop hook (proof-gate-stop-hook.sh) — universal scan of sprint-closed/sprint-N-*-verify.md files at every Stop. Verifies each ## Visual Verification section cites at least one sprint-proof/sprint-N/ path AND that path exists on disk. Replaces the v6.x archive-time check, which was Bash-pattern-fragile (bypassed by PowerShell, glob, two-step archive flows). Universal — fires regardless of how the archive happened. Default cutoff: enforce on the last 30 archived sprints; operator-overridable via proof-gate-config.json.

Adjacent improvements:

  • is_synthetic_session() — heuristic detection of test session_ids (test-, wrong-, someone-, sid-, etc.) so synthetic test fires can route to a separate log namespace. Cleans up signal/noise in production log diagnosis.
  • sync_chain_queue_from_filesystem() — refreshes chain-queue.json statuses from filesystem ground truth on every master tick. Catches manual archive operations that bypass the helper.
  • New CLI: ccsg_agents.py {build-chain-queue, next-chain-sprint, update-chain-status, chain-complete, sync-chain-queue, reclaim-ownership, is-synthetic}.
  • All hooks: proof-gate-scan.py separated from the Bash hook for testability and clarity.
  • deploy.sh updated: installs proof-gate Stop hook + lib/proof-gate-scan.py, registers Stop hook in settings.json template.

Why v8.6 fixes what v8.5 didn't:

v8.5 ironed the multi-agent dispatch path (per-agent files, atomic claims, multi-signal liveness, fanout semantics, teaching presentism). All correct work, but gated on "when a worker arrives." v8.6 fixes the solo-master flow that was actually pain-shipping every day: arc transitions stay smooth, hallucinations are mechanically prevented, proof can't be silently bypassed. Solo master at v8.6 = pre-v8.5 cadence + v8.5 multi-agent readiness.

What's new — v8.5.3: teaching presentism (strip v-comparison narratives from operational sections)

User observation: agents reading the CLAUDE.md teaching block reproduced version-comparison summaries ("v8.5 changes from v8.4.x", "v8.4.4 bug: master pre-claimed every authored goal", "Solo master is identical to v8.2.4 peak") and used that vocabulary to misdiagnose their own bugs as framework version mismatches. Cause: the v8.5.0/v8.5.1/v8.5.2 edits to claude-md-block-template.md larded operational sections with migration-context — "v8.5 SSOT replaces v8.4.x", "v8.5 removed (auto-migrated by ...)", "the v8.4.x bug fix", "anti-patterns observed in production".

v8.5.3 strips the version-narrative. State-files table, Each-tick discipline, Fanout semantics, and Worker tick sections now read as present-tense operational rules. Stable LOAD-BEARING version-stamps on long-established rules (v7.9.4, v8.0, v8.1.x) preserved as audit-trail anchors. History/changelog content stays in this README — agents reading the per-tick teaching no longer recite migration scaffolding.

Why this matters operationally: when an agent fails (e.g., used a non-hex probe nonce), version-narrative teaching trains it to frame the failure as "this version doesn't fix that issue, must be a different layer's bug" instead of "I violated R8.0a, retry per spec." Present-tense teaching closes the misdiagnosis loop.

No code/helper/hook changes. Pure teaching cleanup.

What's new — v8.5.2: probe-nonce teaching fix (literal hex, no shell expansion)

v8.5.1 R8.0a teaching example used ${NONCE} shell substitution. This trained agents to use creative non-hex nonces. Master alpha hit it on a real re-arm at 12:00 local: produced ccsg-probe-test-arm-$(date +%s), which the hook regex [a-f0-9]{8,64} rejected (because hooks see the literal command BEFORE bash expansion). No sentinel got written; agent's ls /tmp/...*.id matched stale test fixtures from earlier framework-session testing; agent concluded "install is in test-mode" — wrong diagnosis, master entered the kill-switch loop.

v8.5.2 fixes:

  • R8.0a teaching rewritten — strong ⚠️ READ THIS BEFORE WRITING THE PROBE COMMAND warning at top: hooks fire BEFORE bash expansion; literal hex required; same exact hex string in both echo and cat paths
  • Probe code template uses literal hex (a3f7c2b8d1e9 example) — no ${NONCE} placeholder for agents to mis-substitute
  • Anti-pattern catalog added — explicit "do NOT do these" list: $(date +%s), ${VAR}, wildcard *.id
  • NEW CLI: python3 ~/.claude/lib/ccsg_agents.py probe-nonce — prints a fresh 12-char hex string (Python secrets.token_hex(6)). Agents that prefer dynamic nonces can call this in one Bash, capture stdout, then embed THE EXACT value as a literal in the next Bash probe

Verification: the probe mechanism itself was never broken — integration test 7 (in 25/25 suite) confirms it works. v8.5.2 closes the teaching trap that caused agents to misuse it.

No code changes to claim-guard.sh, helper internals, or hook chain. Pure teaching + one new CLI helper.

What's new — v8.5.1: CLAUDE.md teaching aligned with v8.5 layout

v8.5.0 shipped helpers + hooks correctly (25/25 integration tests green) but the claude-md-block-template.md teaching block still had operational references to the legacy agents-registry.json + sprint-queue-state.json. The filesystem was v8.5; master's in-memory mental model was v8.4.x. This would have silently re-introduced the shared-file race the moment a worker armed.

v8.5.1 fixes the teaching:

  • State files table — replaced agents-registry.json row with per-agent agents/<nickname>.json row; removed sprint-queue-state.json row; updated claim file schema to v8.5 intent/status fields
  • Each-tick discipline — heartbeat via update_own_agent() (atomic temp+rename) instead of registry edit; removed sprint-queue-state.json rebuild step; explicit "solo master is identical to v8.2.4 peak" assertion
  • NEW section: Fanout semantics (v8.5 §4.1) — the LOAD-BEARING rule: master claims own-next goal atomically; fanout goals are claim-less for atomic worker pickup. Solo master never writes fanout goals (no worker to consume them). Fanout is purely additive.
  • NEW section: Multi-signal liveness — worker's R8.0 check uses heartbeat OR git activity OR PID; ANY positive = live. Codified in tick code.
  • Stale-sweep — walks per-agent files instead of registry; uses list_agents() helper
  • Worker tickfind_claimable_sprint() + try_atomic_claim() patterns documented
  • Worker bounds — "NEVER touches other agents' per-agent files" replaces "NEVER mutates sprint-queue-state.json"
  • Master fanout imperative — explicit Pattern A (own-next + atomic claim) vs Pattern B (fanout, claim-less)

Result: master's in-memory teaching now matches the v8.5 filesystem layout. When a worker arms, master will write fanout goals correctly (claim-less); worker will atomically claim them; productivity flows. Solo master remains identical to peak v8.2.4 behavior.

Pure teaching alignment — no helper or hook changes. Integration tests still 25/25 GREEN.

What's new — v8.5.0: Multi-Agent Coherence (the v8.4.x consolidation)

v8.4.x shipped 12 patches over 2 weeks chasing multi-agent bugs as they surfaced. The 2026-05-02 production run with master alpha + worker bravo proved that even with all 12 patches applied, multi-agent dispatch never worked in practice — bravo claimed 0 sprints in 7 consecutive ticks despite Phase 4 fanout being explicitly designed for worker dispatch.

v8.5 fixes the root cause: CCSG had multiple sources of truth for the same concern, drifting under multi-agent operation. v8.5 enforces single source of truth per concern with atomic operations.

The big three fixes:

  1. Per-agent registry files (was: shared agents-registry.json with multi-writer race). Each agent owns one file at sprint-docs/.ccsg/agents/<nickname>.json. Atomic writes via temp+rename. No race possible.

  2. Multi-signal liveness (was: single hardcoded 600s heartbeat threshold). Worker checks 3 signals: registry heartbeat fresh OR git activity by master in last 10 min OR process PID still running. ANY positive = live. Master's natural 600-900s tick cadence no longer causes false-stale refusals.

  3. Claim release semantics (was: master claimed every authored goal, never released worker_eligible ones). New schema: {intent, status, claimed_by, claimed_by_nickname, role, claimed_at}. Master writes claim atomically with goal-file ONLY for own-next sprint. Fanout goals are claim-less for workers to atomically grab via O_EXCL.

Solo master at v8.5 = identical to v8.4.x peak. Authors one goal at a time, claims it, executes, archives. No worker overhead. Master only adjusts behavior (fanout 1-3 extra goals) when a live worker is detected via the per-agent files.

Workers dock on additively. When armed, they scan sprint-open/ for claim-less worker_eligible goals and atomically claim via O_EXCL. Master is never blocked. Worker idle = harmless (no log noise, no race contribution).

Other v8.5 hygiene:

  • Hook author guide (docs/CCSG-HOOK-AUTHOR-GUIDE.md) — 7 rules every hook must follow (root via shared helper, no exit 1, atomic writes, PYTHONIOENCODING, etc.)
  • Shared lib/ccsg-find-root.sh helper — phantom-immune project-root discovery, every hook agrees
  • Archive-cleanliness Stop hook (archive-cleanliness-guard.sh) — universal stale-claim detection, replaces fragile v8.4.8 PreToolUse pattern
  • /sprint-cleanup-stale-claims slash command — one-shot orphan removal
  • sprint-queue-state.json removed entirely (was dead weight, never updated)
  • All hooks audited: grep -rn "exit 1" plugin/hooks/*.sh returns empty

Migration from v8.4.x is automatic. First master tick post-upgrade splits the legacy registry, deletes queue-state, releases author-claims on worker_eligible goals, sweeps stale claims for archived sprints, normalizes remaining claims to v8.5 schema. Idempotent — safe to re-run; production-mirror tested against captured Pulscale state (14 stale claims swept, 1 active claim preserved, no data loss).

Test suite: bash tests/v8-5-integration-test.sh — 25/25 GREEN covering 7 scenarios (root discovery, registry split, multi-signal liveness, atomic claim race, migration, archive cleanliness, claim-guard probe).

Reference docs:

  • docs/v8-5-design-evidence.md — 13 empirical observations from the 2026-05-02 multi-agent night that motivated v8.5
  • docs/v8-5-spec.md — full design spec, 14 sections including SSOT invariant, architecture changes, file format, migration plan, success criteria

What's new — v8.4.12: claim-guard project root via git rev-parse (kill phantom shadow)

Master alpha hit a wall on Pulscale S1009 within minutes of v8.4.11 shipping. Symptom: claim-guard kept blocking master's writes despite a valid claim file at /sprint-docs/.ccsg/claims/sprint-S1009.lock. Diagnosis: master CD'd into cloud/ subdir, and a phantom cloud/sprint-docs/ directory (left over from earlier work) shadowed the real one. claim-guard's project-root discovery walked up from CWD looking for any sprint-docs/ — found the phantom first, looked for the claim inside the phantom at cloud/sprint-docs/.ccsg/claims/sprint-S1009.lock, found nothing, blocked.

The bug was inconsistency, not the phantom. sprint-first-guard.sh has used git rev-parse --show-toplevel since v6 (correct). claim-guard.sh (v8.4.6 onward) used the legacy walk-up. Two different project-root algorithms in the same hook chain.

v8.4.12 fix: claim-guard prefers git rev-parse --show-toplevel (canonical project root). Falls back to walk-up only if not in a git repo. Aligns with sprint-first-guard.sh. Verified against master's exact failing scenario — PASS.

This is the kind of inconsistency the v8.5 consolidation pass will sweep more broadly: all hooks in the chain should agree on what the project root is. v8.4.12 fixes the most acute symptom; v8.5 will codify the rule.

What's new — v8.4.11: worker session_id probe (closes the v8.4.10 second-order gap)

After v8.4.10 fixed gate-18 to be claim-aware, worker mode was still broken on Windows. Root cause: $CLAUDE_SESSION_ID is unset in current Claude Code agent bash environments. The R8.0a fallback chain (CLAUDE_SESSION_ID → uuidgen → python uuid) silently landed on uuidgen, generating a fake WSID. Hooks see the real session_id (via stdin JSON), so claim files keyed to the fake WSID never matched — every worker write blocked at gate-18 + claim-guard regardless of v8.4.10's claim-aware passthrough.

v8.4.11 fix: probe-nonce session_id discovery.

  1. claim-guard.sh gains a probe responder. When a Bash command literally contains ccsg-probe-<nonce>, the hook reads session_id from its stdin JSON and writes the full session_id to ${TMPDIR:-/tmp}/ccsg-session-by-nonce-<nonce>.id. No-op for all other commands. Runs before all other gates (no project / claim-state dependency).

  2. sprint-start.md § R8.0a rewrite. Worker arm picks a unique 12-16-char hex nonce, runs ONE Bash tool call whose command literally contains ccsg-probe-<nonce>, then reads the sentinel. Hook is PreToolUse, so the sentinel exists before the same Bash call's main command runs. No race, no thrash. Falls back loud (with diagnostic instructions) if sentinel doesn't appear.

The session_id from the sentinel IS the real Claude Code session_id — the same value gate-18 + claim-guard compare against. Worker claim files now key correctly; gate-18 Gate 5 (v8.4.10 claim-aware passthrough) can finally do its job.

No new ownership model, no new hook, no new file. ~30-line patch across two existing files — fits the v8.4.x discovery cadence rather than waiting for v8.5 consolidation.

What's new — v8.4.10: gate-18 (sprint-ownership-guard) is now claim-aware

The hook hierarchy gap from the v8.4.6/7/8/9 push: gate-18 (sprint-ownership-guard.sh) was at v7.3.3 — pre-multi-agent — and blocked ALL non-owner writes to sprint-state regardless of claim files. Worker bravo claimed S1001 successfully (claim-guard verified), then halted at gate-18 because the worker is not the .ccsg-active-session owner. Master holds that ownership; the worker has its own claim file. Two ownership models that didn't talk to each other.

v8.4.10 adds Gate 5 to sprint-ownership-guard.sh — claim-aware passthrough:

For each Edit/Write/MultiEdit hook fire:
  Gate 1: not a CCSG project → passthrough (existing)
  Gate 2: no armed session → passthrough (existing)
  Gate 3: this session IS .ccsg-active-session owner → passthrough (existing)
  Gate 4: no file path → passthrough (existing)
  Gate 5 (NEW): extract sprint-N from path; if claims/sprint-S<N>.lock owned by THIS session → passthrough
                OR if writing OWN claim file (first-claim attempt, claim file doesn't yet exist) → passthrough
  Gate 6: namespace match (sprint-open/, sprint-closed/, etc.) → BLOCK (existing)

Verified by 4-case test suite: bravo first-claim write → PASS, bravo verify-file write in sibling worktree → PASS, wrong-session write to claimed sprint → BLOCK, master alpha write → PASS.

This closes the multi-agent enforcement series. The hook hierarchy now coherent:

  • .ccsg-active-session = project-level master ownership (gate-18 Gate 3)
  • claims/sprint-S<N>.lock = sprint-level worker ownership (gate-18 Gate 5 + claim-guard)
  • Master always passes Gate 3; worker passes Gate 5 for its own claimed sprints; everyone else blocked

v8.5 consolidation planned: v8.4.x has been a discovery-driven patch series. v8.5 will be a coherent consolidation pass — single filename convention, single ownership-model documentation, hook-hierarchy spec, no more bandaid patches.

What's new — v8.4.9: claim-guard regex hotfix (filename convention without S prefix)

v8.4.6/7/8 had a silent bug. The claim-guard hook regex looked for sprint-S<N> in goal-file paths, but Pulscale's actual filename convention is sprint-<N>-*.md (no S prefix on file paths; the S is only in claim file names like claims/sprint-S<N>.lock). Result: claim-guard never fired on real production sprint files since v8.4.6.

Discovered when master alpha wrote sprint-open/sprint-1001-maya-uc01-server-state-audit.md for v8.4.4 look-ahead authoring without first writing a claim file. Hook should have blocked. It didn't. Inspection: grep -oE 'sprint-S[0-9]+' against sprint-1001-... returns nothing → SPRINT_NUM empty → hook passthrough.

The reason discipline still appeared to hold: master and bravo were following CLAUDE.md teaching ("write claim file first") because the teaching DOCUMENTS the hook's existence. Self-fulfilling discipline. But the hook was dead code.

v8.4.9 hotfix: make the S optional in all regex patterns:

  • sprint-S[0-9]+sprint-S?[0-9]+
  • Archive case glob sprint-open/sprint-S*sprint-open/sprint-*

5 regex changes per hook file. Verified by 5-case test suite: WRITE without claim → BLOCK ✓, WRITE with own claim → PASS ✓, WRITE with wrong session → BLOCK ✓, ARCHIVE without rm → BLOCK ✓, ARCHIVE with rm → PASS ✓.

Hook now actually backs up the discipline. The race-protection layer that should have been live since v8.4.6 is finally enforcing.

Real fix planned for next minor (v8.5)

The hotfix preserves both filename conventions (sprint-<N> and sprint-S<N>). For the next minor, the framework should choose ONE canonical convention and migrate. Options:

  1. Canonicalize to sprint-S<N> everywhere (filenames + claim files + commit messages). Breaking change for existing CCSG-managed projects; requires migration script.
  2. Canonicalize to sprint-<N> (filenames + claim files + commit messages). Less breaking; aligns with Pulscale's existing convention. Requires updating template + teaching.
  3. Keep both, ship a manifest-based detection instead of regex. More robust but more complex.

Decision deferred to v8.5 design pass. v8.4.9 keeps the framework backward-compatible with both.

What's new — v8.4.8: claim-file cleanup at archive (closes the last v8.4.x discipline gap)

claim-guard.sh now enforces claim-file deletion at archive time. When archive detected (command contains sprint-open/sprint-S<N> AND sprint-closed/): if claims/sprint-S<N>.lock exists AND command does NOT include rm <claim> → BLOCK. Two valid patterns:

# Pattern A: separate
rm sprint-docs/.ccsg/claims/sprint-S<N>.lock
git mv sprint-open/sprint-S<N>-*.md sprint-closed/

# Pattern B: combined (recommended)
rm sprint-docs/.ccsg/claims/sprint-S<N>.lock && git mv sprint-open/sprint-S<N>-*.md sprint-closed/

Implementation: literal substring matching via bash case (the v8.4.6 attempt's regex failed silently inside the hook for environment reasons; v8.4.8 sidesteps via portable substring match).

Step M-Archive teaching added to master tick CLAUDE.md.

Multi-agent enforcement series complete — every isolation invariant now has hook-level mechanical enforcement:

  • v8.3.1 safe-push-guard.sh (force-push block + pull-rebase)
  • v8.4.3 loop-detect.sh role-aware (CCSG_ARM_WORKER token)
  • v8.4.6/v8.4.7 claim-guard.sh ownership + Windows path
  • v8.4.8 claim-guard.sh archive cleanup

What's new — v8.4.7: claim-guard.sh Windows-path hotfix

Pure hotfix to v8.4.6. The Python open(mingw-path) pattern in claim-guard.sh failed silently on Windows git-bash (Python on Windows doesn't resolve /c/Development/...-style paths; FileNotFoundError suppressed by 2>/dev/null; CLAIMED_BY came back empty; hook misreported "Claim owner: " on master's retry).

Fix: pipe file content via stdin (cat $CLAIM_FILE | python3 stdin) — no path ever passes to Python. Works on both Linux and Windows. Memory note saved for future hook authoring: NEVER use python open(mingw-path) in CCSG hooks.

What's new — v8.4.6: claim-guard hook (race protection at the file-system level)

Race condition observed in production v8.4.5: master worked solo on S997 from 23:22 to 00:04 without writing claims/sprint-S997.lock. User armed bravo at 00:04. Bravo's first tick scanned sprint-open/, saw S997 unclaimed (no claim file), saw master's registry as current_sprint: None (stale), correctly claimed via O_EXCL. Both agents ran Playwright concurrently on S997 — racing test artifacts, port collisions, dual Mongo seeds.

Root cause: v8.3.0+ teaching said master MUST write claim files; nothing enforced it. Master's solo-mode lapse left no claim files for in-flight work. When worker armed, no signal said "this is mine."

v8.4.6 fix — claim-guard.sh PreToolUse hook (Edit/Write/MultiEdit/Bash matchers):

Mechanically blocks sprint-S file edits + commits referencing S when:

  • No claims/sprint-S<N>.lock exists, OR
  • Claim file is owned by a different session

The corrected gate (NOT "live workers exist") is "v8.3.0+ project signal": sprint-docs/.ccsg/ directory exists. In v8.3.0+ projects, claim discipline is mandatory ALWAYS — even in solo mode. Why: solo-mode lapse is the trap that creates the race when transitioning to multi-agent. Solution: claim files always exist for in-flight work; worker arming finds them and skips correctly.

Master tick gains Step M-Claim (CLAUDE.md teaching):

  • Before any sprint-S file edit OR commit referencing S: master MUST write claims/sprint-S<N>.lock with own session_id + role:master
  • Even when no worker is registered. Even in solo mode. Always.
  • The hook is the safety net; the discipline is the design.

Hook behavior matrix:

| Tool | Match | Behavior | |---|---|---| | Edit/Write/MultiEdit | path matches sprint-{open,closed,blocked}/sprint-S<N>-* | require claim file owned by self; else BLOCK | | Bash | command contains sprint-{open,closed,blocked}/sprint-S<N>-* path | same | | Bash | command contains git commit with S<N>: or Sprint <N>: in message | same | | All others | no sprint-S reference | passthrough | | Pre-v8.3.0 projects | no sprint-docs/.ccsg/ directory | passthrough (legacy compa