@thebassclef/lite 1.9.16 → 1.9.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/lite/.claude/agents/builder.md +7 -4
  6. package/dist/lite/.claude/bassclef-orientation.md +22 -0
  7. package/dist/lite/.claude/hooks/artifact-ingestion-gate.sh +11 -3
  8. package/dist/lite/.claude/hooks/assert-verify-steering.sh +1 -19
  9. package/dist/lite/.claude/hooks/plain-english-steering.sh +86 -41
  10. package/dist/lite/.claude/hooks/pre-commit-gate.sh +10 -2
  11. package/dist/lite/.claude/hooks/pre-push-shellcheck.sh +144 -0
  12. package/dist/lite/.claude/hooks/reviewer-dispatch-check.sh +153 -0
  13. package/dist/lite/.claude/hooks/session-reflection.d/83-install-written-paths-rebuild.sh +15 -32
  14. package/dist/lite/.claude/hooks/turn-prose-density-measure.sh +109 -0
  15. package/dist/lite/.claude/hooks/turn-prose-kiss-check.sh +28 -0
  16. package/dist/lite/.claude/rules/branching.md +2 -0
  17. package/dist/lite/.claude/rules/identifier-leak-prevention.md +2 -1
  18. package/dist/lite/.claude/rules/lite-manifest-schema-change-discipline.md +23 -0
  19. package/dist/lite/.claude/rules/reviewer-dispatch.md +95 -0
  20. package/dist/lite/.claude/settings.json +10 -0
  21. package/dist/lite/.claude/skills/build/SKILL.md +81 -6
  22. package/dist/lite/.claude/skills/eisenhower/SKILL.md +26 -0
  23. package/dist/lite/.claude/skills/launch/SKILL.md +45 -2
  24. package/dist/lite/.claude/skills/onboard-repo/SKILL.md +204 -40
  25. package/dist/lite/.claude/skills/onboard-repo/concise-prompt.sh +73 -0
  26. package/dist/lite/.claude/skills/onboard-repo/write-state-marker.sh +81 -0
  27. package/dist/lite/.claude/skills/pre-mortem/SKILL.md +56 -0
  28. package/dist/lite/.claude/skills/sprint/SKILL.md +24 -3
  29. package/dist/lite/.claude/skills/temperance/SKILL.md +15 -4
  30. package/dist/lite/README.md +2 -0
  31. package/dist/lite/bassclef-version.json +5 -5
  32. package/dist/lite/lib/install-written-paths.sh +250 -3
  33. package/dist/lite/lib/prose-marker.sh +203 -0
  34. package/dist/lite/lib/tier-dependency-audit.sh +52 -25
  35. package/dist/lite/presence/install/bassclef-sync.template.sh +86 -8
  36. package/dist/lite/scripts/generate-lite-manifest.sh +15 -0
  37. package/dist/lite/scripts/launch/local-serve.sh +149 -8
  38. package/dist/lite/standards/bassclef-wiring-manifest.json +12 -0
  39. package/dist/lite/standards/lite-manifest-schema-changes.md +49 -0
  40. package/dist/lite/standards/lite-manifest.json +90 -27
  41. package/dist/lite/standards/prose-marker-format.md +105 -0
  42. package/package.json +1 -1
@@ -66,43 +66,26 @@ _iwp_fragment_main() {
66
66
  return 0
67
67
  fi
68
68
 
69
- # Source lib — subshell so caller scope stays clean
69
+ # Source lib — subshell so caller scope stays clean.
70
+ # Per bassclef-upstream#2200 — bulk-rebuild verb replaces the prior
71
+ # 451×700ms serial register loop that timed out before completion.
72
+ # New verb builds one jq write + one ajv validate at the end.
70
73
  (
71
74
  # shellcheck source=/dev/null
72
75
  . "$lib" 2>/dev/null || exit 0
73
76
 
74
- if ! declare -F install_written_paths_register >/dev/null 2>&1; then
75
- _iwp_fragment_log "skip reason=lib-missing-verb"
76
- exit 0
77
- fi
78
-
79
- # Reset manifest to empty, then walk lite-manifest entries
80
- rm -f "$write_manifest" 2>/dev/null || true
81
- install_written_paths_rebuild 2>/dev/null || true
82
-
83
- local registered=0
84
- local skipped=0
85
-
86
- # Each lite-manifest entry has .path field — register when file exists on disk
87
- while IFS= read -r path; do
88
- [ -z "$path" ] && continue
89
- local full
90
- case "$path" in
91
- /*) full="$path" ;;
92
- *) full="$project_root/$path" ;;
93
- esac
94
- if [ -f "$full" ]; then
95
- if install_written_paths_register "$path" "bassclef-sync-template" 2>/dev/null; then
96
- registered=$((registered + 1))
97
- else
98
- skipped=$((skipped + 1))
99
- fi
100
- else
101
- skipped=$((skipped + 1))
77
+ if declare -F install_written_paths_rebuild_from_lite_manifest >/dev/null 2>&1; then
78
+ # Preserve verb's stderr (ajv-fail warnings etc.) by piping to trace
79
+ # instead of swallowing with 2>/dev/null (architect-review F3).
80
+ local verb_stderr
81
+ verb_stderr=$(install_written_paths_rebuild_from_lite_manifest "$lite_manifest" 2>&1 >/dev/null) || true
82
+ if [ -n "$verb_stderr" ]; then
83
+ _iwp_fragment_log "bulk-rebuild stderr=$verb_stderr"
102
84
  fi
103
- done < <(jq -r '.entries[]?.path // empty' "$lite_manifest" 2>/dev/null)
104
-
105
- _iwp_fragment_log "rebuild complete registered=$registered skipped=$skipped"
85
+ _iwp_fragment_log "bulk-rebuild dispatched lite=$lite_manifest"
86
+ else
87
+ _iwp_fragment_log "skip reason=lib-missing-bulk-rebuild-verb"
88
+ fi
106
89
  ) || true
107
90
 
108
91
  return 0
@@ -0,0 +1,109 @@
1
+ #!/bin/bash
2
+ # tier: lite
3
+ # install-class: dual
4
+ # Turn-prose density measure — Stop hook that reads the last assistant message
5
+ # from the transcript, counts non-standard hyphenated compounds, and writes
6
+ # the number to a marker file at state/markers/turn-prose-density/last-turn.json
7
+ # via lib/prose-marker.sh per standards/prose-marker-format.md.
8
+ #
9
+ # Density = count of [a-z]{2,}-[a-z]{2,}(?:-[a-z]{2,})? matches MINUS the
10
+ # 25-entry standard-tech allowlist. Proxy for jargon load distinct from
11
+ # grade-measure's existing hyphen_words count (which includes standard tech).
12
+ #
13
+ # The pre-turn plain-english-steering.sh reads the marker on next turn
14
+ # (extended at Step 2d) and injects the density number into the steering
15
+ # block alongside grade + appositive misses.
16
+ #
17
+ # Sibling scorers:
18
+ # - turn-prose-grade-measure.sh — grade metric
19
+ # - turn-prose-kiss-check.sh — appositive miss count (Step 2c extension)
20
+ #
21
+ # Input (stdin): JSON with transcript_path + session_id
22
+ # Output: writes state/markers/turn-prose-density/last-turn.json
23
+ # Exit: 0 always (measurement only, no gate)
24
+ #
25
+ # Override: SKIP_TURN_PROSE_DENSITY=1
26
+
27
+ set +e
28
+
29
+ # === Override path ===
30
+ if [ "${SKIP_TURN_PROSE_DENSITY:-0}" = "1" ]; then
31
+ exit 0
32
+ fi
33
+
34
+ # === Parse stdin JSON ===
35
+ STDIN_JSON=$(cat 2>/dev/null || echo '{}')
36
+ if ! command -v jq >/dev/null 2>&1; then
37
+ exit 0
38
+ fi
39
+ TRANSCRIPT_PATH=$(echo "$STDIN_JSON" | jq -r '.transcript_path // ""' 2>/dev/null)
40
+
41
+ [ -z "$TRANSCRIPT_PATH" ] && exit 0
42
+ [ ! -f "$TRANSCRIPT_PATH" ] && exit 0
43
+
44
+ # === Resolve lib path (install-class: dual) ===
45
+ # Project install — lib relative to this hook. Operator install — lib under $HOME.
46
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
47
+ LIB_PATH=""
48
+ for candidate in \
49
+ "${SCRIPT_DIR}/../../lib/prose-marker.sh" \
50
+ "${HOME:-/}/lib/prose-marker.sh" \
51
+ "${CLAUDE_PROJECT_DIR:-}/lib/prose-marker.sh"; do
52
+ if [ -f "$candidate" ]; then
53
+ LIB_PATH="$candidate"
54
+ break
55
+ fi
56
+ done
57
+
58
+ [ -z "$LIB_PATH" ] && exit 0
59
+ # shellcheck disable=SC1090
60
+ source "$LIB_PATH" 2>/dev/null || exit 0
61
+
62
+ # === Extract last assistant text ===
63
+ LAST_ASSISTANT_LINE=$(grep '"type":"assistant"' "$TRANSCRIPT_PATH" 2>/dev/null | tail -1)
64
+ [ -z "$LAST_ASSISTANT_LINE" ] && exit 0
65
+
66
+ MESSAGE_TEXT=$(echo "$LAST_ASSISTANT_LINE" \
67
+ | jq -r '.message.content[]? | select(.type == "text") | .text' 2>/dev/null)
68
+ [ -z "$MESSAGE_TEXT" ] && exit 0
69
+
70
+ # === Strip code blocks + inline backticks + URLs (prose only) ===
71
+ SCRUBBED=$(echo "$MESSAGE_TEXT" | awk '
72
+ BEGIN { in_code = 0 }
73
+ /^```/ { in_code = !in_code; next }
74
+ in_code { next }
75
+ { print }
76
+ ' | sed -E 's/`[^`]*`//g; s|https?://[^ )]+||g')
77
+
78
+ # Short message after strip → density=0
79
+ STRIPPED_LEN=${#SCRUBBED}
80
+ if [ "$STRIPPED_LEN" -lt 50 ]; then
81
+ write_prose_marker "turn-prose-density" "0" "short message under 50 chars"
82
+ exit 0
83
+ fi
84
+
85
+ # === Compute density via python ===
86
+ # Standard-tech allowlist excluded. Pattern per standards/prose-marker-format.md.
87
+ DENSITY=$(printf '%s' "$SCRUBBED" | python3 -c '
88
+ import sys, re
89
+
90
+ STANDARD_TECH = {
91
+ "pre-commit", "real-time", "data-driven", "end-to-end", "fire-and-forget",
92
+ "run-time", "compile-time", "read-only", "write-only", "fail-soft",
93
+ "fail-fast", "post-commit", "pre-push", "post-merge", "op-ed",
94
+ "self-test", "self-check", "re-read", "re-run", "re-fire",
95
+ "op-cost", "copy-paste", "cut-off", "set-up", "built-in",
96
+ }
97
+
98
+ text = sys.stdin.read().lower()
99
+ pattern = re.compile(r"\b[a-z]{2,}-[a-z]{2,}(?:-[a-z]{2,})?\b")
100
+ matches = pattern.findall(text)
101
+ density = sum(1 for m in matches if m not in STANDARD_TECH)
102
+ print(density)
103
+ ' 2>/dev/null)
104
+
105
+ [ -z "$DENSITY" ] && DENSITY=0
106
+
107
+ # === Write density marker via lib ===
108
+ write_prose_marker "turn-prose-density" "$DENSITY" 2>/dev/null
109
+ exit 0
@@ -83,6 +83,20 @@ for _candidate in \
83
83
  done
84
84
  unset _candidate
85
85
 
86
+ # === Source lib/prose-marker.sh (deep module per Longrun 2 Step 2b #2197) ===
87
+ # Provides write_prose_marker for appositive-miss count persistence.
88
+ # Silent-fail — no lib = no marker write; existing stderr display path unchanged.
89
+ for _candidate in \
90
+ "${SCRIPT_DIR}/../../lib/prose-marker.sh" \
91
+ "${HOME_OVERRIDE:-${HOME:-/}}/lib/prose-marker.sh"; do
92
+ if [ -f "$_candidate" ]; then
93
+ # shellcheck disable=SC1090
94
+ source "$_candidate"
95
+ break
96
+ fi
97
+ done
98
+ unset _candidate
99
+
86
100
  # === Parse stdin JSON ===
87
101
  STDIN_JSON=$(cat 2>/dev/null || echo '{}')
88
102
  TRANSCRIPT_PATH=$(echo "$STDIN_JSON" | jq -r '.transcript_path // ""' 2>/dev/null)
@@ -417,8 +431,15 @@ for pattern, label in patterns:
417
431
 
418
432
  for f in flagged[:5]: # cap at 5 findings per turn to avoid noise
419
433
  print(f)
434
+ # Emit total count on final line as sentinel — bash strips before display.
435
+ # Per Longrun 2 Step 2c (#2197) — scorer-stack appositive marker.
436
+ print(f"__APPOSITIVE_COUNT__={len(flagged)}")
420
437
  ' 2>/dev/null)
421
438
 
439
+ # Extract the count sentinel; strip it from the display output.
440
+ APPOSITIVE_COUNT=$(echo "$APPOSITIVE_FINDINGS" | grep '^__APPOSITIVE_COUNT__=' | tail -1 | sed 's/^__APPOSITIVE_COUNT__=//')
441
+ APPOSITIVE_FINDINGS=$(echo "$APPOSITIVE_FINDINGS" | grep -v '^__APPOSITIVE_COUNT__=')
442
+
422
443
  if [ -n "$APPOSITIVE_FINDINGS" ]; then
423
444
  cat >&2 <<EOF
424
445
  [turn-prose-kiss-check: ADVISORY — appositive discipline (bassclef-upstream#1313)]
@@ -434,6 +455,13 @@ Override one-time: SKIP_APPOSITIVE_CHECK=1.
434
455
  V1 advisory; V2 (deferred) BLOCKs after calibration.
435
456
  EOF
436
457
  fi
458
+
459
+ # === Write appositive-miss marker via lib/prose-marker.sh (Longrun 2 Step 2c) ===
460
+ # Count is total matches (not capped at 5 — that cap is display-only).
461
+ # Silent-fail — missing lib or write error does not affect existing path.
462
+ if declare -f write_prose_marker >/dev/null 2>&1 && [ -n "$APPOSITIVE_COUNT" ]; then
463
+ write_prose_marker "turn-prose-appositive" "$APPOSITIVE_COUNT" 2>/dev/null
464
+ fi
437
465
  fi
438
466
 
439
467
  # === Emit findings + exit per mode ===
@@ -13,6 +13,8 @@ Branch naming:
13
13
 
14
14
  Every branch ties to a GitHub Issue. No branch without an issue.
15
15
 
16
+ **Exception — `/launch --local`.** Adopter prototype branches created by `/launch --local` are exempt from the issue-per-branch rule. Local prototype mode exists for adopters who do not yet have a GitHub repo or who iterate ahead of filing issues. Per bassclef-upstream#2175 Batch A (F2) + bassclef-cli#328 Finding 2.
17
+
16
18
  Flow: issue → branch from main → work → PR → CI passes → squash merge → delete branch
17
19
 
18
20
  Rules:
@@ -30,8 +30,9 @@ No commit lands with these patterns in message OR staged content:
30
30
 
31
31
  - **Hostname shapes**: `[A-Za-z][A-Za-z0-9]*-MacBook-(Air|Pro)`, `DESKTOP-[A-Z0-9]{4,}`, `*-server-*`, `*-desktop-*`
32
32
  - **Absolute paths**: `/Users/<name>`, `/home/<name>`
33
+ - **GitHub no-reply derived slug**: `operator-[0-9]+\+[a-zA-Z0-9][a-zA-Z0-9-]*` — the shape `/personas` historically derived from a GitHub no-reply email local part (`<id>+<handle>`), leaking account ID plus handle into committed persona + use-case + jobs docs. Per bassclef-upstream#2146. Scope-safe — the pattern targets the derived slug only; the raw email format `<id>+<handle>@users.noreply.github.com` stays allowed as a git author (Check 3 pins that in Test 22) because GitHub recommends no-reply as the privacy format for commit identity.
33
34
 
34
- Extension mechanism — future pattern classes added to the hook's `IDENTIFIER_PATTERNS` regex + a corresponding Tier 0 test.
35
+ Extension mechanism — future pattern classes added to `standards/identifier-leak-patterns.txt` + a corresponding Tier 0 test. The hook reads the pattern file at run time; adding a line there extends the scrub without a hook edit.
35
36
 
36
37
  ## Anti-patterns
37
38
 
@@ -36,6 +36,29 @@ When a PR changes the manifest schema OR output shape:
36
36
  3. **Downstream cure named** — the entry names what adopters need to do. If nothing (backward-compatible add), say so explicitly. If code change (like the v0.36.0 `upstream_commit` removal), name the pattern.
37
37
  4. **Release script pre-flight** — before every `/release`, the script reads the ledger and confirms an entry exists for the outgoing `manifest_version`. Missing entry BLOCKs the cascade.
38
38
 
39
+ ## Ledger-edit-then-regen order (bassclef-upstream#2110)
40
+
41
+ The ledger file at `standards/lite-manifest-schema-changes.md` is ITSELF a `tier: lite` artifact. Its content_hash lives inside the manifest. So the regen must capture the FINAL ledger state. Order matters:
42
+
43
+ 1. **Edit ledger first.** Write the entry explaining the schema change.
44
+ 2. **Regen second.** Run `scripts/generate-lite-manifest.sh`.
45
+
46
+ Natural authoring instinct is "regen first (see the hashes), then explain." That order ships a stale ledger hash in the manifest. CI drift check fails. A second regen commit is then needed.
47
+
48
+ **Preferred cure — use the wrapper.** `scripts/update-lite-manifest.sh` enforces the order:
49
+
50
+ ```bash
51
+ # Append a pre-written entry
52
+ scripts/update-lite-manifest.sh --entry-file path/to/entry.md
53
+
54
+ # Open $EDITOR on the ledger
55
+ scripts/update-lite-manifest.sh --edit
56
+ ```
57
+
58
+ Both modes hash the ledger before and after. The wrapper refuses to regen when the ledger is unchanged. Then it dispatches `scripts/generate-lite-manifest.sh` + the renderer when present.
59
+
60
+ Per `.claude/rules/bootstrap-pair-discipline.md` — the wrapper ships with Tier 0 tests at `scripts/tests/update-lite-manifest.test.sh`.
61
+
39
62
  ## Retirements
40
63
 
41
64
  Retirement-class changes (content-remove: a file previously shipped in the lite manifest gets deleted intentionally) ship with the same discipline shape as additions. Three paired artifacts, same PR:
@@ -0,0 +1,95 @@
1
+ ---
2
+ tier: lite
3
+ description: Any session that ships source code (files under src/, apps/, packages/, lib/, app/) MUST invoke Reviewer responsibilities before the session ends.
4
+ ---
5
+
6
+ # Reviewer Dispatch
7
+
8
+ Any session that ships source code (files under `src/`, `apps/`, `packages/`, `lib/`, `app/`) MUST invoke Reviewer responsibilities before the session ends. Mode-agnostic — orchestrator-gated mode dispatches the Reviewer agent; sequential mode self-invokes Reviewer responsibilities (read against spec, check tests, validate ADR compliance, flag TDD test-after patterns per Kent Beck).
9
+
10
+ The discipline lives at the rule layer (this file) plus the mechanical layer (`.claude/hooks/reviewer-dispatch-check.sh` — Stop hook). Both required for adopter code quality.
11
+
12
+ ## When this rule fires
13
+
14
+ At session end (Stop event), the hook:
15
+
16
+ 1. Counts source files changed since session-start (heuristic: `git diff` against `origin/main`; uncommitted + committed changes both count)
17
+ 2. Path matchers for "source": `src/`, `apps/`, `packages/`, `lib/`, `app/`
18
+ 3. When source count > 0, the hook checks for any marker at `state/markers/reviewer/*.md`
19
+ 4. When no marker exists, the hook BLOCKs with reviewer-checklist remediation
20
+
21
+ ## Reviewer marker contract
22
+
23
+ The marker captures the Reviewer pass per the agent definition (`~/.claude/agents/reviewer.md`):
24
+
25
+ ```markdown
26
+ # Reviewer pass — <session-id>
27
+
28
+ mode: sequential | orchestrator-gated
29
+ reviewed_at: <ISO-8601>
30
+ source_files_changed: <count>
31
+ wu_id: <e.g., bet-2026-06-25k WU-3> # bet 25k WU-5 extension
32
+ iteration_count: <N> # bet 25k WU-5 extension
33
+ lens_used: [<luminary-slug>, ...] # bet 25k WU-5 extension
34
+
35
+ ## Checks
36
+ - spec acceptance criteria: PASS | FAIL | N/A
37
+ - tests present + green: PASS | FAIL
38
+ - test-after pattern check: NONE FOUND | FOUND (list)
39
+ - ADR compliance: PASS | N/A
40
+ - Designer sign-off: PRESENT | N/A
41
+ - architect-review: INVOKED | N/A
42
+
43
+ ## Findings
44
+ - <severity>: <description> # BLOCK / WARN / NOTE
45
+
46
+ acknowledged: true
47
+ ```
48
+
49
+ ### Field extensions per bassclef-upstream#366 (bet 25k WU-5)
50
+
51
+ Three fields extend the prior contract to make the /loop discipline auditable per step:
52
+
53
+ - **`wu_id`** — goal identifier + WU number (e.g., `bet-2026-06-25k WU-3`). Lets future audits grep markers by WU. Required when the marker is written under /longrun dispatch; optional for ad-hoc reviews.
54
+ - **`iteration_count`** — number of /loop iterations the step ran before landing GREEN. Iteration 1 GREEN is the cleanest path; counts above 1 record rework cycles (RED → fix → GREEN). Required under /longrun dispatch.
55
+ - **`lens_used`** — YAML list of luminary slugs that drove the review. Mirrors the goal doc's `authoring_luminaries` for the relevant WU. Required under /longrun dispatch.
56
+
57
+ The three fields together let future operators answer questions like "which WUs in bet 25k ran more than one /loop iteration" or "which luminary lens caught the most rework" by greppable markers, not by chronicle archaeology. The mechanical layer (greppable fields) compounds across every goal; the bar (`## /temperance + /luminary + /loop discipline` section in the PR body) gives the immediate operator-visible read.
58
+
59
+ Forward-only enforcement. Markers written before WU-5 stay as-is; markers written from WU-5 onwards carry the three new fields under /longrun dispatch.
60
+
61
+ ## Mode behavior
62
+
63
+ **Orchestrator-gated mode** — orchestrator dispatches Reviewer agent at code-shipping handoffs. Reviewer agent reads against spec, runs checks, writes the marker per the contract above, returns PASS/FAIL/WARN findings to orchestrator.
64
+
65
+ **Sequential mode** — agent self-invokes Reviewer responsibilities. Read the Reviewer agent definition. Apply checks against the session's source diff. Write the marker. The self-invocation is honor-system at the methodology layer; the marker is the mechanical evidence the hook reads.
66
+
67
+ ## Override path
68
+
69
+ `SKIP_REVIEWER_CHECK=1 <command>` — logged via trace-helper. Use rarely:
70
+
71
+ - Documentation-only session that accidentally touched a comment in src/ (operator confirms the source touch is non-behavioral)
72
+ - Emergency rescue when the hook itself misclassifies (file substrate-defect)
73
+ - One-shot migration script that ships hundreds of source files (operator has reviewed externally)
74
+ - Bulk dependency-bump where the package-manager touched lockfile entries only
75
+
76
+ INSTEAD of overriding for routine work: write the marker. The Reviewer-self-invocation cost is small (~5 minutes); the audit trail compounds across every adopter session.
77
+
78
+ ## What the rule does NOT cover
79
+
80
+ - Code review process content (covered in `~/.claude/agents/reviewer.md`)
81
+ - TDD test-list discipline (covered in `.claude/rules/test-list-discipline.md`)
82
+ - Sufficiency rubric for tests (covered in `.claude/rules/test-sufficiency.md`)
83
+ - ADR discipline (covered in `.claude/rules/adr-discipline.md`)
84
+ - Architect review timing (covered in `.claude/skills/architect-review/SKILL.md`)
85
+
86
+ This rule is the gate. Sister rules + skills carry the content.
87
+
88
+ ## Composition with other rules
89
+
90
+ - `.claude/rules/testing-tier-config.md` — Tier 0 strict-TDD on hook + tests (this hook + its test file land Tier 0)
91
+ - `.claude/rules/blocked-items.md` — BLOCK protocol the hook fires
92
+ - `.claude/rules/test-sufficiency.md` — what Reviewer checks for under "tests present + green"
93
+ - `.claude/rules/test-list-discipline.md` — what Reviewer checks for under "test-after pattern check" (Beck's test-list-as-planning-artifact)
94
+ - `.claude/rules/adr-discipline.md` — what Reviewer checks for under "ADR compliance"
95
+ - `~/.claude/agents/reviewer.md` — the full Reviewer responsibilities definition
@@ -52,6 +52,11 @@
52
52
  "type": "command",
53
53
  "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/git-stash-u-guard.sh",
54
54
  "timeout": 5
55
+ },
56
+ {
57
+ "type": "command",
58
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-push-shellcheck.sh",
59
+ "timeout": 60
55
60
  }
56
61
  ]
57
62
  },
@@ -167,6 +172,11 @@
167
172
  "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/turn-prose-grade-measure.sh",
168
173
  "timeout": 10
169
174
  },
175
+ {
176
+ "type": "command",
177
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/turn-prose-density-measure.sh",
178
+ "timeout": 10
179
+ },
170
180
  {
171
181
  "type": "command",
172
182
  "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/longrun-prep-compounding-sequence-check.sh",
@@ -214,13 +214,28 @@ if echo "$@" | grep -q -- '--explain'; then
214
214
  EXPLAIN_MODE=true
215
215
  fi
216
216
 
217
- # Resolve goal via accessor
217
+ # Resolve goal via accessor. #2132 — capture stderr so parse errors surface
218
+ # instead of masquerading as "not found". Map accessor return codes:
219
+ # 1 → goal file not found; 3 → goal file could not be parsed.
218
220
  source lib/state.sh
219
221
  BET_SLUG="$1"
220
- BET_JSON=$(state_iteration_bet_get "$BET_SLUG" 2>/dev/null) || {
221
- echo "/build: goal '$BET_SLUG' not found via state_iteration_bet_get" >&2
222
- exit 1
223
- }
222
+ BET_JSON=$(state_iteration_bet_get "$BET_SLUG" 2>&1)
223
+ BET_RC=$?
224
+ if [ "$BET_RC" -ne 0 ]; then
225
+ case "$BET_RC" in
226
+ 1)
227
+ echo "/build: goal file not found: $BET_SLUG" >&2
228
+ ;;
229
+ 3)
230
+ echo "/build: goal file could not be parsed: $BET_SLUG" >&2
231
+ ;;
232
+ *)
233
+ echo "/build: goal '$BET_SLUG' lookup failed (rc=$BET_RC)" >&2
234
+ ;;
235
+ esac
236
+ echo "$BET_JSON" >&2
237
+ exit "$BET_RC"
238
+ fi
224
239
 
225
240
  # Check status active
226
241
  STATUS=$(echo "$BET_JSON" | jq -r '.status // empty')
@@ -234,6 +249,15 @@ SPEC_PATH=$(echo "$BET_JSON" | jq -r '.references[]? | select(.type == "spec") |
234
249
  DECOMP_PATH=$(echo "$BET_JSON" | jq -r '.references[]? | select(.type == "decomposition") | .id // empty')
235
250
  UX_MIG_PATH=$(echo "$BET_JSON" | jq -r '.references[]? | select(.type == "ux-migration") | .id // empty')
236
251
 
252
+ # #2132 — goal MUST declare a spec reference. Empty SPEC_PATH means Phase 1
253
+ # would awk against docs/specs/.md and silently find nothing; cure refuses
254
+ # at Phase 0 instead with a clear error.
255
+ if [ -z "$SPEC_PATH" ]; then
256
+ echo "/build: goal '$BET_SLUG' has no spec reference — refusing" >&2
257
+ echo "Add a {type: spec, id: <spec-slug>} entry to goal frontmatter references[]." >&2
258
+ exit 4
259
+ fi
260
+
237
261
  # Validate references exist
238
262
  for ref_type in spec decomposition; do
239
263
  ref_id=$(echo "$BET_JSON" | jq -r --arg t "$ref_type" '.references[]? | select(.type == $t) | .id // empty')
@@ -349,6 +373,16 @@ WU_LIST=$(awk '
349
373
 
350
374
  # Each step should reference at least one story (US-NNN)
351
375
  WU_COUNT=$(echo "$WU_LIST" | grep -cE '^### (Step|WU)-' || echo 0)
376
+
377
+ # #2132 — refuse zero-step specs. The Phase 2 orphan check does not fire
378
+ # on empty WU_LIST (no steps → no orphans → silent pass). Phase 1 refuses
379
+ # so adopters see the real problem.
380
+ if [ "$WU_COUNT" = "0" ]; then
381
+ echo "/build: spec has zero steps — refusing" >&2
382
+ echo "Spec file: $SPEC_FILE" >&2
383
+ echo "Need a Steps enumerated section with Step-N or WU-N entries." >&2
384
+ exit 4
385
+ fi
352
386
  ```
353
387
 
354
388
  Build structured object: `{ bet, spec_path, decomp_path, ux_migration_path, wus[] }`.
@@ -483,6 +517,23 @@ Operator can force adopter-mode via `--adopter-mode` flag (V2). Operator can for
483
517
 
484
518
  ### Phase 3 — DryRunPlanner
485
519
 
520
+ **Read the pre-mortem marker first (per bassclef-upstream#2175 Batch B — F5).** Before enumerating steps, read `state/markers/pre-mortem/<branch-slug>.marker` if present. The marker names the risk ledger file plus top-5 risks. Surface the ledger path + top-5 risks in the dry-run output. Builder dispatches in future LIVE mode read the ledger before writing code.
521
+
522
+ <!-- FKGL-EXEMPT: bash command block; shell commands are not graded prose. -->
523
+
524
+ ```bash
525
+ BRANCH_SLUG=$(git branch --show-current | tr '/' '-')
526
+ MARKER="state/markers/pre-mortem/${BRANCH_SLUG}.marker"
527
+ if [ -f "$MARKER" ]; then
528
+ echo "Pre-mortem ledger — reading top-5 risks:"
529
+ cat "$MARKER"
530
+ fi
531
+ ```
532
+
533
+ <!-- /FKGL-EXEMPT -->
534
+
535
+ When the marker is absent, `/build` emits a one-line note that no pre-mortem fired for this branch and proceeds. `/pre-mortem` is mandatory on always-fire domains per its SKILL body; Phase 3 does not re-enforce that gate.
536
+
486
537
  Walk step enumeration; for each, plan what would happen in live mode:
487
538
 
488
539
  ```
@@ -534,6 +585,8 @@ TODO: live execution — operator-supervised first run only.
534
585
  - Operator-tap moments: 1 (prod deploy approval via GitHub Environment)
535
586
  ```
536
587
 
588
+ <!-- FKGL-EXEMPT: Phase 4 stub — technical dispatch TODO; OOAD artifact names + branch slug template resist short SVO rewrites. Per bassclef-upstream#2175 Batch A — slug canon matches Phase 3. -->
589
+
537
590
  ### Phase 4 — BuilderDispatcher (LIVE — STUBBED)
538
591
 
539
592
  **Amendment per #1171 — Builder inputs name OOAD artifacts.**
@@ -545,7 +598,7 @@ TODO: live execution — operator-supervised first run only. Would:
545
598
  dispatch Builder against step <slug> with context from spec section
546
599
  <X> + decomp matrix entry <Y> + paired OOAD artifacts (decomposition
547
600
  + interaction-design + use-case per goal doc references). Branch:
548
- feature/<goal-slug>-stack-<N>.
601
+ feature/<goal-slug>-stack-<N>-<wu-slug>.
549
602
  ```
550
603
 
551
604
  The string `TODO: live execution` is grep-able for audit. Live mode
@@ -556,6 +609,10 @@ in the goal doc `references` array — decomposition (grep for
556
609
  `type: interaction-design`), use-case (grep for `type: use-case`).
557
610
  Per #1171 — every OOAD artifact consulted for any engineering change.
558
611
 
612
+ <!-- /FKGL-EXEMPT -->
613
+
614
+ <!-- FKGL-EXEMPT: Phase 5 stub + Phase 5a Reviewer dispatch — technical TODO blocks for live-mode scaffolding. F10 cure per bassclef-upstream#2175 Batch D. -->
615
+
559
616
  ### Phase 5 — VerifyOrchestrator (LIVE — STUBBED)
560
617
 
561
618
  Tonight emits literal:
@@ -566,6 +623,24 @@ Match change type (UI smoke / unit / integration) and BLOCK on
566
623
  failure with line-numbered output.
567
624
  ```
568
625
 
626
+ ### Phase 5a — Reviewer dispatch (per bassclef-upstream#2175 Batch D — F10)
627
+
628
+ After `/verify` passes, Phase 5 adds a Reviewer dispatch step. Running `/verify` alone catches test failures. The Reviewer agent (`.claude/agents/reviewer.md`) catches spec-acceptance drift, missing-test patterns, ADR deviations, and friction the Builder did not surface. One review step per PR before merge.
629
+
630
+ Tonight emits literal:
631
+
632
+ ```
633
+ TODO: live execution — would dispatch Reviewer agent against branch <name>.
634
+ Reviewer reads: spec acceptance criteria, decomposition matrix entry, PR
635
+ body friction report. Reviewer writes: state/markers/reviewer/<branch>.md
636
+ with PASS / FAIL / NOTE findings. BLOCK merge on FAIL; NOTE findings file
637
+ as follow-on tickets.
638
+ ```
639
+
640
+ The Reviewer dispatch is MANDATORY per `.claude/rules/reviewer-dispatch.md` for any session shipping source code. Phase 5a is the Phase-level wiring. Live mode consumes the Reviewer marker before Phase 6 opens the PR.
641
+
642
+ <!-- /FKGL-EXEMPT -->
643
+
569
644
  ### Phase 5.5 — Amendment B2: /pattern-review trigger reads decomposition (per #1150 + #1167)
570
645
 
571
646
  When the goal references a decomposition doc AND that doc contains `@pattern` references, dispatch `/pattern-review` with the decomposition path as input. Skip when the decomposition names no `@pattern`. Contract per `.claude/rules/pattern-annotation.md` L57-62.
@@ -151,6 +151,32 @@ Individual items: ...
151
151
  - #127 — title — no path to advance the goal — CLOSE
152
152
  ```
153
153
 
154
+ ### Step 4b — Emit the hard stack rank
155
+
156
+ After the per-quadrant tables, emit one final table. Columns fixed:
157
+
158
+ 1. **Rank** — 1 to N, cross-repo, top-down
159
+ 2. **Ticket** — `<repo>#<number>` form
160
+ 3. **Repo** — the owning repo. Bold the repo name when work ships somewhere other than the filed repo.
161
+ 4. **Why this rank** — one plain-English sentence. Grade-8 target. Cite the Depen. tie or class fit.
162
+
163
+ Row order:
164
+
165
+ - Q1 first, then Q2, then Q3, then Q4
166
+ - Within quadrant, sort by Depen. first (independents at top), then class, then ticket number
167
+ - Flag cross-repo work in the Repo column
168
+
169
+ Example row shape:
170
+
171
+ ```
172
+ | Rank | Ticket | Repo | Why this rank |
173
+ |------|--------|-------------------|--------------------------------------|
174
+ | 1 | #2008 | **bassclef-web** | Q1 lead. Live Sam bounce. |
175
+ | 2 | #2011 | **bassclef-web** | Q1 rider. Same PR as #2008. |
176
+ ```
177
+
178
+ Per bassclef-upstream#2096 amendment 3.
179
+
154
180
  ### Step 5 — Optional follow-on
155
181
 
156
182
  If Q3 surfaces 5+ items in the same shape, the skill offers to draft one `/promote` ticket. That ticket proposes automating the class. You accept, edit, or skip.
@@ -264,6 +264,8 @@ fi
264
264
 
265
265
  **Local mode (`--local`) — bassclef#923 A2:**
266
266
 
267
+ **Issue-per-branch exemption (per bassclef-upstream#2175 Batch A — F2).** `/launch --local` creates adopter prototype branches that are exempt from the `.claude/rules/branching.md` "every branch ties to a GitHub Issue" rule. Local prototype mode exists for adopters who do not yet have a GitHub repo. No issue is required for `--local` branches. All other `/launch` modes (full, scratch, operator-mode) still require an issue.
268
+
267
269
  `/launch --local` prints a banner first. The banner shows which picker is active:
268
270
 
269
271
  ```
@@ -546,6 +548,43 @@ The gallery is THE cameo-review surface. Luminary perspectives
546
548
  agent runtime per roadmap Stage 3) consume the gallery + spec + decomp
547
549
  to produce reviews per perspective. Reviews surface in PR body.
548
550
 
551
+ ### Phase 10a — Stack-choice ask (per bassclef-upstream#2175 Batch D — F13)
552
+
553
+ <!-- FKGL-EXEMPT: Phase 10a — technical ask step with config-key contract. Short SVO loses the stack-key list. Fires before Phase 11 /ux-migration so the migration plan picks the right adapter. -->
554
+
555
+ Before Phase 11 runs, ask the operator which frontend stack the migration targets. Today `/launch` silently assumes Next.js — nine SKILL bodies lean on Next.js examples. Non-React adopters hit the silent pick at Phase 11 and must rework. The ask fires once per `/launch` dispatch; the answer writes to `substrate.config.md` key `frontend_stack` for Phase 11 to read.
556
+
557
+ Read `substrate.config.md` for `frontend_stack`. If set, skip the ask. If unset, ask:
558
+
559
+ ```
560
+ Which stack is this app?
561
+
562
+ a. Next.js — React + App Router (bassclef default; nine SKILL bodies lean on Next.js examples)
563
+ b. Vite + React — React + Vite dev server
564
+ c. Remix — React + Remix
565
+ d. SvelteKit — Svelte + Kit
566
+ e. SolidStart — Solid + Start
567
+ f. none — static HTML + CSS, no framework
568
+ g. other — operator names it
569
+
570
+ [a-g]
571
+ ```
572
+
573
+ Write the answer to `substrate.config.md` `frontend_stack`:
574
+ - `a` → `frontend_stack: nextjs`
575
+ - `b` → `frontend_stack: vite-react`
576
+ - `c` → `frontend_stack: remix`
577
+ - `d` → `frontend_stack: sveltekit`
578
+ - `e` → `frontend_stack: solidstart`
579
+ - `f` → `frontend_stack: none`
580
+ - `g` → `frontend_stack: <operator-named>`
581
+
582
+ Default on Enter → `a` (Next.js) to preserve the historical silent pick. Operator can edit `substrate.config.md` directly at any time.
583
+
584
+ Phase 11 reads `frontend_stack` and picks the matching migration adapter. When unset at Phase 11 fire, Phase 11 BLOCKs with a one-line note citing this ask.
585
+
586
+ <!-- /FKGL-EXEMPT -->
587
+
549
588
  ### Phase 11 — `/ux-migration` (chosen variant → production migration plan)
550
589
 
551
590
  ```bash
@@ -699,8 +738,8 @@ Two templates — adopter-mode and operator-mode. Default is adopter when `--loc
699
738
  After Phase 12-alt (`local-serve.sh` running) + Phase 13 (preview-state.yml on disk), emit this template directly to the adopter — no YAML edits, no frontmatter flip, no command-soup:
700
739
 
701
740
  ```
702
- I made you N directions for your family recipe wiki. Walk through them on
703
- your phone or laptop at <LOCAL_URL> and tell me which one feels right.
741
+ I made you N directions for your family recipe wiki. Walk through them
742
+ on this computer at <LOCAL_URL> and tell me which one feels right.
704
743
 
705
744
  When you've picked, just tell me — "I like variant 2" or "#3 looks
706
745
  great".
@@ -740,8 +779,12 @@ None of them feel right? Tell me what's off and I'll generate new
740
779
  directions for you. (See /launch --refine, bassclef#942.)
741
780
  ```
742
781
 
782
+ <!-- FKGL-EXEMPT: substitution instructions name three distinct placeholders; short SVO loses the mapping precision -->
743
783
  Substitute `<LOCAL_URL>` with the URL returned by Phase 12-alt. Substitute the actual variant count (`I made you N directions...`). Substitute the actual app description (`your family recipe wiki` etc.) from the InputArtifact's `intent` field.
744
784
 
785
+ **Phone or iPad reach — opt-in with `--lan`.** The default URL binds `127.0.0.1`. Only this computer can open it. The adopter may want the gallery on a phone or iPad on the same Wi-Fi. Re-run Phase 12-alt with `--lan`: `bash scripts/launch/local-serve.sh --lan <gallery-dir>`. The script binds `0.0.0.0` and prints a one-line warning. Any device on the Wi-Fi can then reach the URL. Swap the handoff line to "on your phone, laptop, or iPad at <LOCAL_URL>" when `--lan` fires.
786
+ <!-- /FKGL-EXEMPT -->
787
+
745
788
  The adopter-mode template **surfaces** (per bassclef#969):
746
789
  - The formal artifacts the chain will produce post-pick, in plain English (use case, user stories, IA, interaction design, responsibility map, build spec, migration plan)
747
790
  - The chain-ordering rationale (variants are cheap shared data; formal artifacts are expensive per-direction; cheap first → commit → expensive once)