@thebassclef/lite 1.9.8 → 1.9.10

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 (44) hide show
  1. package/README.md +6 -2
  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/hooks/git-stash-u-guard.sh +4 -1
  6. package/dist/lite/.claude/hooks/post-skill-friction-check.sh +6 -1
  7. package/dist/lite/.claude/hooks/pre-commit-gate.sh +49 -6
  8. package/dist/lite/.claude/hooks/pre-commit-identifier-leak-scrub.sh +68 -8
  9. package/dist/lite/.claude/hooks/pre-commit-manifest-autoregen.sh +4 -2
  10. package/dist/lite/.claude/hooks/save-state.sh +24 -0
  11. package/dist/lite/.claude/hooks/session-reflection.d/83-install-written-paths-rebuild.sh +111 -0
  12. package/dist/lite/.claude/hooks/state-validate.sh +8 -0
  13. package/dist/lite/.claude/hooks/trace-helper.sh +29 -0
  14. package/dist/lite/.claude/rules/install-written-paths-mediation.md +74 -0
  15. package/dist/lite/.claude/rules/lite-manifest-schema-change-discipline.md +30 -0
  16. package/dist/lite/.claude/skills/autonomous/SKILL.md +53 -7
  17. package/dist/lite/.claude/skills/build/SKILL.md +53 -11
  18. package/dist/lite/.claude/skills/deploy-prod/SKILL.md +10 -0
  19. package/dist/lite/.claude/skills/launch/SKILL.md +26 -0
  20. package/dist/lite/.claude/skills/onboard-repo/SKILL.md +59 -18
  21. package/dist/lite/.claude/skills/personas/SKILL.md +1 -1
  22. package/dist/lite/.claude/skills/release-notes/SKILL.md +24 -0
  23. package/dist/lite/bassclef-version.json +5 -5
  24. package/dist/lite/gitignore +8 -0
  25. package/dist/lite/lib/capability-probe.sh +39 -1
  26. package/dist/lite/lib/composer-preflight.sh +8 -4
  27. package/dist/lite/lib/deferred-actions.sh +4 -4
  28. package/dist/lite/lib/fixture-builder.sh +9 -9
  29. package/dist/lite/lib/identifier-leak-check.sh +85 -0
  30. package/dist/lite/lib/install-written-paths.sh +369 -0
  31. package/dist/lite/lib/luminary-pick.sh +35 -13
  32. package/dist/lite/lib/preview-state.sh +20 -20
  33. package/dist/lite/lib/skip-env-inline-check.sh +61 -0
  34. package/dist/lite/lib/state.sh +102 -39
  35. package/dist/lite/lib/tier-check.sh +9 -9
  36. package/dist/lite/lib/tier-dependency-audit.sh +23 -23
  37. package/dist/lite/presence/install/schedule-auto-save.taskscheduler.md +1 -1
  38. package/dist/lite/standards/lite-manifest-schema-changes.md +83 -0
  39. package/dist/lite/standards/lite-manifest.json +70 -47
  40. package/dist/lite/standards/state-spine.md +1 -0
  41. package/dist/lite/templates/spec-template.md +10 -6
  42. package/package.json +1 -1
  43. package/dist/lite/.claude/hooks/longrun-prep-compounding-axis-check.sh +0 -492
  44. package/dist/lite/.claude/rules/compounding-axis-fresh-analysis.md +0 -173
@@ -15,15 +15,19 @@ requires:
15
15
 
16
16
  # Autonomous Development
17
17
 
18
- Entry point for autonomous and semi-autonomous development sessions.
19
- Invoke at any point in any repo that inherits bassclef.
18
+ <!-- FKGL-EXEMPT: cli#331 cure — orientation block names skill scope + tier split. Short SVO loses the tier contract. -->
19
+ Entry point for autonomous SDLC sessions. Use in any repo inheriting bassclef.
20
20
 
21
- This file is a thin dispatcher. Deep procedural detail lives in:
21
+ **Lite adopters:** this file is self-contained. The `## Lite procedure` block below has the minimum steps to run `/autonomous start`. No `strategy/` file is needed at lite tier.
22
22
 
23
- - `strategy/autonomous-dry-run.md` — read-only boot-sequence preview
24
- - `strategy/autonomous-boot-sequence.md` — `/autonomous start` scaffold +
25
- activate, including Step 0–9, Construction automation A–E, pre-build
26
- gates, branch stacking, context management, transition hierarchy
23
+ **Standard and ultra adopters:** deeper procedure lives in these standard-tier files:
24
+
25
+ - `strategy/autonomous-dry-run.md` — read-only boot preview
26
+ - `strategy/autonomous-boot-sequence.md` — full `/autonomous start` steps 0-9 + Construction automation A-E
27
+ - `strategy/agent-choreography.md` — multi-agent operational guide
28
+
29
+ Per cli#331 — closes the "lite skill points at files lite doesn't ship" gap.
30
+ <!-- /FKGL-EXEMPT -->
27
31
 
28
32
  ## Usage
29
33
 
@@ -263,6 +267,48 @@ silently proceed with defaults.
263
267
 
264
268
  ---
265
269
 
270
+ ## Lite procedure — self-contained minimum (cli#331)
271
+
272
+ <!-- FKGL-EXEMPT: cli#331 cure — procedure block for lite adopters. Short SVO loses step contract. -->
273
+ Lite adopters run `/autonomous start` with this block only. No `strategy/` file needed.
274
+
275
+ **Step 0 — validate args.** Accept `operator-gated` or `orchestrator-gated` for gating. Accept `sequential` or `parallel` for mode. Reject anything else with a helpful error.
276
+
277
+ **Step 1 — ask the operator two choices.** Gating (default: operator-gated). Mode (default: sequential). Use AskUserQuestion.
278
+
279
+ **Step 2 — check readiness.** Run through this checklist. Scaffold missing items as you go:
280
+ - `substrate.config.md` present → if missing, dispatch `/onboard-repo` first
281
+ - `docs/whereami.md` present → if missing, dispatch `/onboard-repo` first
282
+ - Current iteration goal doc at `docs/iteration-bets/<slug>.md` → if missing, ask operator for the goal; create a minimal doc
283
+ - Session lock free → if active and >30 min old, clear with announcement
284
+ - Chronicle fresh (last 24h) → if stale, note "will write at session-end"
285
+
286
+ **Step 3 — risk register (optional on lite).** `docs/risk-register.md` is optional at lite tier. Create one only when operator asks or when mode is orchestrator-gated. Skip the check otherwise.
287
+
288
+ **Step 4 — budget ceiling (optional on lite).** Ask the operator "how many turns are you OK spending on this session?" Default to "proceed until operator says stop" when operator skips. Record whatever they say in session memory.
289
+
290
+ **Step 5 — confirm scope.** Echo the goal, the two choices, and the ceiling (or "no ceiling"). Ask: "Ready to start?" Wait for yes.
291
+
292
+ **Step 6 — execute the SDLC chain.** Fire the agents in order per gating choice:
293
+ - **operator-gated:** pause between each agent for operator approval. Agents: Shaper → PM → Architect → Designer → Builder → Reviewer → Deployer → Closer. Operator picks which fire.
294
+ - **orchestrator-gated:** chain the agents without pausing. Each writes a handoff doc. Builder commits code. Reviewer runs `/verify`. PRs open for operator merge (or admin-merge within scope).
295
+
296
+ **Step 7 — handle Construction.** For code work:
297
+ - Parse scope into GitHub issues (one issue per discrete change)
298
+ - Write stack manifest at `docs/stacks/<slug>.md` if more than one branch
299
+ - Create branches with issue numbers in name (not placeholder — per bassclef#295)
300
+ - Dispatch Builder per branch; fire `/temperance` + `/diagnose` + `/verify` per branch
301
+ - Open PR per branch; squash merge after Reviewer PASS
302
+
303
+ **Step 8 — close the session.** Dispatch `/session-end` which handles chronicle write, whereami bump, journal entry.
304
+
305
+ Lite adopters at any point can escalate to operator-gated mode mid-session. Say "pause for me to review" and the next agent handoff waits.
306
+
307
+ **Deep procedure for standard+ adopters.** Standard+ and ultra adopters have the strategy/ files above. Those cover Construction automation A-E, pre-build gates, branch stacking, context management, transition hierarchy, and the full choreography rules. Read them for the complete design.
308
+ <!-- /FKGL-EXEMPT -->
309
+
310
+ ---
311
+
266
312
  ## Rules
267
313
 
268
314
  - Never start work without operator confirmation
@@ -255,7 +255,7 @@ FLOOR_PATTERN='src/lib/auth/|src/middleware\.ts|prisma/schema\.prisma|prisma/mig
255
255
  ACCEPTANCE_CONTENT=$(awk '
256
256
  /^```/{in_code = !in_code; next}
257
257
  in_code{next}
258
- /^### WU-/{in_wu=1; next}
258
+ /^### (Step|WU)-/{in_wu=1; next}
259
259
  /^## /{in_wu=0}
260
260
  in_wu && /^- \[ \]/ {print}
261
261
  ' "docs/specs/${SPEC_PATH}.md" "docs/decompositions/${DECOMP_PATH}.md" 2>/dev/null)
@@ -273,8 +273,8 @@ fi
273
273
  ```
274
274
 
275
275
  **Why this is precise (per bassclef#366):** the scan only looks at
276
- step acceptance criteria (lines starting `- [ ]` under `### WU-N`
277
- headings, outside code blocks) plus file-path-shaped references in
276
+ step acceptance criteria (lines starting `- [ ]` under `### Step-N`
277
+ or `### WU-N` headings, outside code blocks) plus file-path-shaped references in
278
278
  the goal's frontmatter. Prose paragraphs that DESCRIBE the floor (like
279
279
  this section) don't trigger refusal. Code blocks that document the
280
280
  floor don't trigger refusal. Only structured ACTIONS the agent would
@@ -330,14 +330,25 @@ Parse the validated goal's structure:
330
330
  ```bash
331
331
  # Already have BET_JSON, SPEC_PATH, DECOMP_PATH, UX_MIG_PATH from Phase 0
332
332
  # Read spec body for step enumeration. Per bassclef#367, spec MUST have
333
- # the exact heading `## Steps enumerated` (pinned in /spec SKILL.md
334
- # rules + templates/spec-template.md). /launch's Phase 9
335
- # emits this heading by contract. Specs that lack the section → exit 4.
333
+ # the heading `## Steps enumerated` (pinned in /spec SKILL.md rules +
334
+ # templates/spec-template.md). /launch's Phase 9 emits this heading by
335
+ # contract. Specs that lack the section → exit 4.
336
+ #
337
+ # Finding #2 + #3 cure (#2036): the previous awk range
338
+ # `/^## Steps enumerated/,/^## /` closed on its own start line because
339
+ # `^## ` matches `^## Steps enumerated`. Flag-based extraction fixes it.
340
+ # Grace window per ADR-040 D1 (through 2026-10-31): accept both the new
341
+ # "Steps enumerated" / "### Step-N" and the historical "Workunits
342
+ # enumerated" / "### WU-N" headings so legacy specs still build.
336
343
  SPEC_FILE="docs/specs/${SPEC_PATH}.md"
337
- WU_LIST=$(awk '/^## Steps enumerated/,/^## /' "$SPEC_FILE" | grep -E '^### WU-')
344
+ WU_LIST=$(awk '
345
+ /^## (Steps|Workunits) enumerated/ { in_section=1; next }
346
+ /^## / { in_section=0 }
347
+ in_section
348
+ ' "$SPEC_FILE" | grep -E '^### (Step|WU)-')
338
349
 
339
- # Each WU should reference at least one story (US-NNN)
340
- WU_COUNT=$(echo "$WU_LIST" | grep -c '^### WU-' || echo 0)
350
+ # Each step should reference at least one story (US-NNN)
351
+ WU_COUNT=$(echo "$WU_LIST" | grep -cE '^### (Step|WU)-' || echo 0)
341
352
  ```
342
353
 
343
354
  Build structured object: `{ bet, spec_path, decomp_path, ux_migration_path, wus[] }`.
@@ -348,7 +359,8 @@ Verify each step traces to at least one story. Refuse if any step lacks
348
359
  story traceability.
349
360
 
350
361
  ```bash
351
- ORPHAN_WUS=$(echo "$WU_LIST" | grep -v 'US-' || true)
362
+ # Keep only step headings; drop body lines before the US-NNN check.
363
+ ORPHAN_WUS=$(echo "$WU_LIST" | grep -E '^### (Step|WU)-' | grep -v 'US-' || true)
352
364
  if [ -n "$ORPHAN_WUS" ]; then
353
365
  echo "/build: step enumeration lacks story traceability (bassclef#358 violation)" >&2
354
366
  echo "Orphan steps (no US-NNN ref):" >&2
@@ -578,7 +590,26 @@ already open. Operator merges inline.
578
590
 
579
591
  ### Phase 7 — ApprovalGateLink (LIVE — STUBBED)
580
592
 
581
- Tonight emits literal:
593
+ <!-- FKGL-EXEMPT: cli#332 cure — Phase 7 branches on hosting_platform value. Three code-shape outputs. Technical stub-shape dispatch pattern. -->
594
+
595
+ Phase 7 branches on `hosting_platform` read from `substrate.config.md`:
596
+
597
+ **When `hosting_platform: none`** (lite adopters, operator-only tools, local-first apps) — emit:
598
+
599
+ ```
600
+ TODO: live execution — built, not deployed. hosting_platform=none in
601
+ substrate.config.md. The code is reviewed and committed. Running it is
602
+ your choice.
603
+
604
+ To add hosting later:
605
+ - Edit substrate.config.md: set hosting_platform to one of
606
+ github-pages | netlify | cloudflare-pages | vercel | amplify
607
+ - Run /onboard-repo --with-deploy-host to wire the host
608
+ - Then /build Phase 7 will dispatch /deploy-prod via GitHub Environment
609
+ + required-reviewer rail per manual-prod-approval.md.
610
+ ```
611
+
612
+ **When `hosting_platform` is set to a known value** — emit:
582
613
 
583
614
  ```
584
615
  TODO: live execution — when all steps merged + CI green, would dispatch
@@ -587,6 +618,17 @@ required-reviewer rail per manual-prod-approval.md. Operator approves
587
618
  prod deploy via GitHub UI from phone. /build NEVER bypasses this rail.
588
619
  ```
589
620
 
621
+ **When `hosting_platform` is unset or unrecognized** — emit:
622
+
623
+ ```
624
+ TODO: live execution — hosting_platform not set in substrate.config.md.
625
+ Can't deploy without a target. Set hosting_platform to one of none |
626
+ github-pages | netlify | cloudflare-pages | vercel | amplify, then re-run.
627
+ ```
628
+
629
+ Per cli#332 — closes the "lite promises deployed app without a deploy path" honesty gap.
630
+ <!-- /FKGL-EXEMPT -->
631
+
590
632
  <!-- FKGL-EXEMPT: Phase 7.5 references paths + skip criteria per architect-review SKILL; technical dispatch stub matches sibling STUB pattern -->
591
633
  ### Phase 7.5 — /architect-review auto-dispatch (LIVE — STUBBED, per #1437 Step 2 wiring)
592
634
 
@@ -17,8 +17,18 @@ requires:
17
17
 
18
18
  # Deploy to Production
19
19
 
20
+ <!-- FKGL-EXEMPT: cli#332 cure — tier contract block names scope + override path. Short SVO loses host-override contract. -->
20
21
  Standard deployment procedure. Run after code is reviewed and ready to ship.
21
22
 
23
+ **Tier contract (per cli#332):**
24
+
25
+ - This skill ships at `tier: lite` as a generic 5-step template: test → build → push → CI → verify.
26
+ - The template body is host-agnostic. Host-specific commands (AWS, Vercel, Netlify, Fly, Railway, GitHub Pages) are not shipped here.
27
+ - When `substrate.config.md` sets `hosting_platform` to a known value, standard-tier adopters add a host-specific wrapper that calls this skill AND runs the host commands. Lite adopters without `hosting_platform` run the template steps + stop.
28
+ - `/build` Phase 7 dispatches this skill only when `hosting_platform` is set to a known value — see /build Phase 7 branching logic per cli#332.
29
+ - `manual-prod-approval.md` applies in all tiers: GitHub Environment `prod` with required reviewer MUST be configured before any prod deploy fires.
30
+ <!-- /FKGL-EXEMPT -->
31
+
22
32
  ## Procedure
23
33
 
24
34
  ### Step 1: Pre-flight
@@ -309,9 +309,35 @@ Per `standards/voice-input-pattern.md` + Phase V Hey-Alexa MVD goal (`docs/itera
309
309
  - Existing text-mode + brownfield-mode behavior unchanged when flag absent
310
310
  - Composes with `--brownfield` (rare; voice-driven brownfield refactor is plausible but not the MVD path)
311
311
 
312
+ ### Phase 0b — hosting_platform ask (per cli#332)
313
+
314
+ <!-- FKGL-EXEMPT: cli#332 cure — hosting_platform ask step. Short SVO loses the config-key contract. -->
315
+ Read `substrate.config.md` for the `hosting_platform` key. If unset or missing, ask the operator once:
316
+
317
+ ```
318
+ Where should this run?
319
+ a. local only (default — develop + preview on your machine)
320
+ b. a static host (GitHub Pages / Netlify / Cloudflare Pages / Vercel)
321
+ c. a server host (Amplify / Fly.io / Railway)
322
+ d. I'll deploy myself — just build the plan
323
+ ```
324
+
325
+ Write the answer to `substrate.config.md` `hosting_platform`:
326
+ - `a` → `hosting_platform: none`
327
+ - `b` → prompt for which static host; write that value (`github-pages` / `netlify` / `cloudflare-pages` / `vercel`)
328
+ - `c` → prompt for which server host; write that value (`amplify` / `fly` / `railway`)
329
+ - `d` → `hosting_platform: operator-managed`
330
+
331
+ If already set, skip the question. The answer flows to /build Phase 7 which branches on this value per cli#332.
332
+
333
+ Per cli#332 — closes "Nothing asks where the app will run" at the earliest surface in the chain.
334
+ <!-- /FKGL-EXEMPT -->
335
+
312
336
  ### Phase 1 — `/interpret-input`
313
337
 
338
+ <!-- FKGL-EXEMPT: pre-existing text-mode + brownfield-mode description — technical dispatch contract per bassclef#459 sub-goal 1. -->
314
339
  Dispatches `/interpret-input` with the type detected by Phase 0. Text mode preserves the existing text-mode behavior; brownfield mode invokes the Phase C extractor (LR2 WU-1, bassclef#459 sub-goal 1) which walks the repo and populates structural hints (entities, actors, routes, README excerpt).
340
+ <!-- /FKGL-EXEMPT -->
315
341
 
316
342
  ```bash
317
343
  # Generate sprint slug + InputArtifact path
@@ -433,25 +433,25 @@ Idempotent API calls against the target repo via `gh`.
433
433
  # Read from git remote — don't infer from cwd basename (cwd can be renamed)
434
434
  REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
435
435
 
436
- declare -A LABELS=(
437
- [bassclef-evolution]="A6E0F2:Candidate for promotion to bassclef"
438
- [agent-user-proposed]="D4A5F5:Proposed by agent in an app repo"
439
- [bassclef-sync]="8FD4A4:Related to bassclef substrate syncing"
440
- [review-required]="F5B94A:Blocks — human review required"
441
- [bug]="E4443A:Defect"
442
- [enhancement]="A2EEEF:New or improved functionality"
443
- [security]="FF0000:Security-relevant"
444
- [infra]="C9D1D9:Infrastructure / workflows / tooling"
445
- [priority-high]="E4443A:High priority"
446
- [priority-medium]="F5B94A:Medium priority"
447
- [question]="D876E3:Needs operator input or design discussion"
448
- )
449
-
450
- for NAME in "${!LABELS[@]}"; do
451
- COLOR="${LABELS[$NAME]%%:*}"
452
- DESC="${LABELS[$NAME]#*:}"
436
+ # Per cli#306 — use while-read loop over pipe-delimited heredoc instead
437
+ # of `declare -A`. macOS ships bash 3.2 which has no associative arrays.
438
+ # Format per line: NAME|COLOR|DESCRIPTION
439
+ while IFS='|' read -r NAME COLOR DESC; do
440
+ [ -z "$NAME" ] && continue
453
441
  gh label create "$NAME" --repo "$REPO" --color "$COLOR" --description "$DESC" --force >/dev/null
454
- done
442
+ done <<'EOF'
443
+ bassclef-evolution|A6E0F2|Candidate for promotion to bassclef
444
+ agent-user-proposed|D4A5F5|Proposed by agent in an app repo
445
+ bassclef-sync|8FD4A4|Related to bassclef substrate syncing
446
+ review-required|F5B94A|Blocks — human review required
447
+ bug|E4443A|Defect
448
+ enhancement|A2EEEF|New or improved functionality
449
+ security|FF0000|Security-relevant
450
+ infra|C9D1D9|Infrastructure / workflows / tooling
451
+ priority-high|E4443A|High priority
452
+ priority-medium|F5B94A|Medium priority
453
+ question|D876E3|Needs operator input or design discussion
454
+ EOF
455
455
  ```
456
456
 
457
457
  Uses `--force` so the call is idempotent (updates existing label or
@@ -462,9 +462,28 @@ immediately without waiting for a sync cycle.
462
462
 
463
463
  ##### 1.2 Protect main from force-push and deletion
464
464
 
465
+ **Heads up — GitHub free-tier adopters on PRIVATE repos:** the API call
466
+ below returns HTTP 403 ("Upgrade to GitHub Pro or make this repository
467
+ public") on free-tier accounts, because branch protection is a paid
468
+ feature for private repos. Public repos get branch protection on every
469
+ plan, free included. Pick a path below BEFORE running the call.
470
+
471
+ Three ways to clear this step:
472
+
473
+ 1. **Make the repo public** — simplest; free plan includes branch
474
+ protection on public repos. Good fit when the code is open source
475
+ or ships to adopters anyway.
476
+ 2. **Upgrade the GitHub plan** — Pro / Team / Enterprise includes
477
+ branch protection on private repos.
478
+ 3. **Skip the API call** — rely on local safety (no `git push --force`
479
+ habits, PR review, pre-commit hooks). Mark the repo config so later
480
+ phases know branch protection is operator-managed, not substrate-
481
+ managed. See the fallback block below.
482
+
465
483
  ```bash
466
484
  DEFAULT_BRANCH=$(gh repo view "$REPO" --json defaultBranchRef -q '.defaultBranchRef.name')
467
485
 
486
+ # Pro / public-repo path — run the API call.
468
487
  gh api "repos/$REPO/branches/$DEFAULT_BRANCH/protection" \
469
488
  --method PUT \
470
489
  --input - <<EOF
@@ -484,6 +503,28 @@ required here — agent-created PRs (bassclef-sync) need to merge
484
503
  without human intervention for the mobile-sync story. Add stricter
485
504
  rules per-repo in `.github/branch-protection.yml` if the repo warrants.
486
505
 
506
+ **Free-tier private-repo fallback (path 3 above).** If the API
507
+ returned 403 and the operator picked "skip and manage locally":
508
+
509
+ ```bash
510
+ # Record the choice so later audits know main is not API-protected.
511
+ mkdir -p .claude
512
+ cat >> .claude/onboard-notes.md <<'NOTE'
513
+ ## Branch protection — operator-managed
514
+
515
+ GitHub free-tier does not include branch protection on private repos.
516
+ This repo relies on:
517
+ - Local discipline: no `git push --force` to main.
518
+ - PR review before merge.
519
+ - Pre-commit hooks shipped by bassclef.
520
+
521
+ Revisit if the repo moves to public, or the plan upgrades to Pro+.
522
+ NOTE
523
+ ```
524
+
525
+ The adopter can revisit this step after switching to public OR
526
+ upgrading the plan.
527
+
487
528
  ##### 1.3 Add the production approval gate (if this repo deploys to prod)
488
529
 
489
530
  If the repo will deploy to production, per `manual-prod-approval.md`:
@@ -87,7 +87,7 @@ When no persona files exist in the repo, auto-populate a minimal persona file fo
87
87
 
88
88
  - `CLAUDE.md` — read the "What this repo is" / "Active initiative" sections for goal context
89
89
  - `substrate.config.md` — read `agents:` and `deploy_targets:` for role context
90
- - If a git `user.email` exists, use it to derive a rough slug (`operator-[local-part]`), otherwise `operator`
90
+ - Default slug: `operator`. Do not derive it from `user.email`. GitHub no-reply addresses carry the account ID and handle. The slug then leaks into committed docs (cli#320). Ask for a display name in-turn if the operator wants one.
91
91
 
92
92
  Emit the operator default to the conversation (not to disk — operator decides whether to persist it):
93
93
 
@@ -108,6 +108,30 @@ Rules for external notes:
108
108
  - No feature names that aren't visible to users
109
109
  - Test: could someone who's never seen the codebase understand this?
110
110
 
111
+ ### 3.5. Run `/ogilvy-writing-audit` on each body
112
+
113
+ Before saving the drafted bodies to disk, run `/ogilvy-writing-audit`
114
+ on each one. The skill scans against Ogilvy + Roman-Raphaelson's
115
+ rules (see `standards/ogilvy-writing-rules.md`) and returns
116
+ severity-tagged findings.
117
+
118
+ ```bash
119
+ /ogilvy-writing-audit docs/release-notes/YYYY-MM-DD-internal.md
120
+ /ogilvy-writing-audit docs/release-notes/YYYY-MM-DD-external.md
121
+ ```
122
+
123
+ Fix every 🔴 and 🟡 finding before step 4. When a 🟡 ships without
124
+ a fix, cite the audit in the commit body and name why.
125
+
126
+ Why this step. Release notes land in reader surfaces that must read
127
+ clean — the GitHub Release artifact plus the Google Doc the team
128
+ reviews. Jargon and grade-13 prose creep in without a mechanical
129
+ check. PR #408 ran this discipline by hand across 8 entries, and
130
+ PR #412 formalizes it so every future entry inherits the floor.
131
+
132
+ Cross-ref: `.claude/rules/session-artifacts.md` §Release notes names
133
+ this step as the discipline anchor.
134
+
111
135
  ### 4. Save locally
112
136
 
113
137
  ```
@@ -1,7 +1,7 @@
1
1
  {
2
- "version": "v1.6.4",
3
- "marker_tag": "release-2026-09-28-21212e17",
4
- "release_date": "2026-09-28T01:33:41Z",
5
- "release_sha": "21212e17",
6
- "release_tag": "release-2026-09-28-21212e17"
2
+ "version": "v1.6.6",
3
+ "marker_tag": "release-2026-10-05-484c6f3e",
4
+ "release_date": "2026-10-05T03:42:33Z",
5
+ "release_sha": "484c6f3e",
6
+ "release_tag": "release-2026-10-05-484c6f3e"
7
7
  }
@@ -31,6 +31,14 @@ state/luminary-implementations/
31
31
  .claude/hooks/logs/
32
32
  .claude/hooks/*.trace
33
33
 
34
+ # SDLC traces (operator-private; runtime artifacts carry operator paths)
35
+ # Per #2036 Finding #1 (adopter report 2026-10-02) — auto-save was
36
+ # committing /Users/<name>/ paths to adopter git every fire. Shipping this
37
+ # line stops future adopter installs from leaking. Existing installs run
38
+ # `git rm --cached -r docs/sdlc-traces/` one time to untrack files
39
+ # already in history.
40
+ docs/sdlc-traces/
41
+
34
42
  # Build output
35
43
  dist/
36
44
 
@@ -44,8 +44,46 @@ probe_gh_auth() {
44
44
  }
45
45
 
46
46
  probe_mcp_playwright() {
47
- # V1 heuristic — env var contract. V2 may read tool inventory.
47
+ # Fast path — explicit opt-in via env var. Adopters who want to force the
48
+ # probe TRUE set CLAUDE_MCP_PLAYWRIGHT=1 in their shell init. Documented at
49
+ # docs/use-cases/UC-lib-capability-probe-mcp-fallback.md.
48
50
  [ -n "${CLAUDE_MCP_PLAYWRIGHT:-}" ] && return 0
51
+
52
+ # Fallback — look for a Playwright MCP server in known MCP config files.
53
+ # jq is required for parsing. When absent, the probe fails soft to 1.
54
+ if ! command -v jq >/dev/null 2>&1; then
55
+ echo "probe_mcp_playwright: jq not found; env-var fast path only" >&2
56
+ return 1
57
+ fi
58
+
59
+ local project_dir="${CLAUDE_PROJECT_DIR:-.}"
60
+ local home_dir="${HOME:-}"
61
+
62
+ # Check paths in precedence order. First hit wins.
63
+ _probe_mcp_json_has_playwright "${project_dir}/.mcp.json" && return 0
64
+ _probe_mcp_json_has_playwright "${project_dir}/.claude/settings.json" && return 0
65
+ if [ -n "$home_dir" ]; then
66
+ _probe_mcp_json_has_playwright "${home_dir}/.claude/settings.json" && return 0
67
+ _probe_mcp_json_has_playwright "${home_dir}/.claude.json" && return 0
68
+ fi
69
+ return 1
70
+ }
71
+
72
+ # _probe_mcp_json_has_playwright <path>
73
+ # Returns 0 when the JSON file names a Playwright MCP server. Checks two
74
+ # shapes:
75
+ # 1. Top-level "playwright" key (used by `.mcp.json` on some adopters)
76
+ # 2. Nested "mcpServers.playwright" (used by `settings.json` + `~/.claude.json`)
77
+ # Fail-soft on missing file + malformed JSON — returns 1 silently.
78
+ # Per bassclef-upstream Finding #6 (#2036): `local path` is a zsh-magic variable
79
+ # name — ties to $PATH when sourced from zsh. Renamed to `local p`.
80
+ _probe_mcp_json_has_playwright() {
81
+ local p="$1"
82
+ [ -f "$p" ] || return 1
83
+ # Shape 1 — top-level playwright
84
+ jq -e '.playwright // empty' "$p" >/dev/null 2>&1 && return 0
85
+ # Shape 2 — nested mcpServers.playwright
86
+ jq -e '.mcpServers.playwright // empty' "$p" >/dev/null 2>&1 && return 0
49
87
  return 1
50
88
  }
51
89
 
@@ -178,10 +178,14 @@ composer_preflight_check() {
178
178
  echo "check_5_recommendation="
179
179
  pass=$((pass + 1))
180
180
  elif [ -f "$repo_skill" ] || [ -L "$repo_skill" ]; then
181
- echo "check_5_status=warn"
182
- echo "check_5_message=Skill at repo-level only ($repo_skill); user-level symlink missing — harness may not pick up (bassclef#271)"
183
- echo "check_5_recommendation=Run bassclef-sync.sh to create user-level symlink OR restart Claude session"
184
- warn=$((warn + 1))
181
+ # Per cli#321 — Claude Code's harness loads project .claude/skills/
182
+ # directly; user-level symlinks are not required. Repo-level IS a
183
+ # valid discovery path. Previously flagged as WARN, trained adopters
184
+ # to ignore warnings. Now PASS with informational message.
185
+ echo "check_5_status=pass"
186
+ echo "check_5_message=Skill resolves at repo-level: $repo_skill (harness loads project .claude/skills directly)"
187
+ echo "check_5_recommendation="
188
+ pass=$((pass + 1))
185
189
  elif [ -n "${BASSCLEF_DIR:-}" ] && [ -f "$bassclef_skill" ]; then
186
190
  echo "check_5_status=warn"
187
191
  echo "check_5_message=Skill at bassclef only; consumer-side links missing — substrate sync incomplete"
@@ -72,9 +72,9 @@ _da_resolve_path() {
72
72
  da_get_field() {
73
73
  local id="$1"
74
74
  local field="$2"
75
- local path
76
- path="$(_da_resolve_path "$id")"
77
- [[ -f "$path" ]] || { echo "not-found: $path" >&2; return 1; }
75
+ local p
76
+ p="$(_da_resolve_path "$id")"
77
+ [[ -f "$p" ]] || { echo "not-found: $p" >&2; return 1; }
78
78
  # Read frontmatter only (between two `---` lines)
79
79
  awk -v f="$field" '
80
80
  BEGIN { in_fm = 0; seen_open = 0 }
@@ -89,7 +89,7 @@ da_get_field() {
89
89
  print
90
90
  exit
91
91
  }
92
- ' "$path"
92
+ ' "$p"
93
93
  }
94
94
 
95
95
  da_resolve() {
@@ -82,16 +82,16 @@ fixture_cleanup() {
82
82
  if [ "${SKIP_FIXTURE_BUILDER:-0}" = "1" ]; then
83
83
  return 0
84
84
  fi
85
- local path="${1:?fixture_cleanup requires path}"
86
- if [ ! -e "$path" ]; then
85
+ local p="${1:?fixture_cleanup requires path}"
86
+ if [ ! -e "$p" ]; then
87
87
  # Idempotent — nothing to do
88
88
  return 0
89
89
  fi
90
- if [ ! -f "$path/$_FIXTURE_SENTINEL" ]; then
91
- echo "fixture_cleanup: refusing to remove path without sentinel: $path" >&2
90
+ if [ ! -f "$p/$_FIXTURE_SENTINEL" ]; then
91
+ echo "fixture_cleanup: refusing to remove path without sentinel: $p" >&2
92
92
  return 1
93
93
  fi
94
- rm -rf "$path"
94
+ rm -rf "$p"
95
95
  return 0
96
96
  }
97
97
 
@@ -105,10 +105,10 @@ fixture_cleanup_all() {
105
105
  return 0
106
106
  fi
107
107
  local IFS=$'\n'
108
- local path
109
- for path in $_FIXTURE_REGISTRY; do
110
- [ -n "$path" ] || continue
111
- fixture_cleanup "$path" 2>/dev/null || true
108
+ local p
109
+ for p in $_FIXTURE_REGISTRY; do
110
+ [ -n "$p" ] || continue
111
+ fixture_cleanup "$p" 2>/dev/null || true
112
112
  done
113
113
  _FIXTURE_REGISTRY=""
114
114
  }
@@ -0,0 +1,85 @@
1
+ #!/bin/bash
2
+ # tier: lite
3
+ # Operator-identifier leak check for staged git content.
4
+ #
5
+ # Shared across:
6
+ # - .claude/hooks/save-state.sh (inline before `git commit`;
7
+ # bypasses pre-commit-gate.sh because save-state runs git
8
+ # directly, not through the agent's PreToolUse path)
9
+ # - future: .claude/hooks/pre-commit-gate.sh CCF-3 (unification
10
+ # deferred to a follow-on; shape intentionally matches CCF-3
11
+ # today so the two can merge without semantic drift)
12
+ #
13
+ # Rejects staged content carrying absolute operator paths matching
14
+ # /Users/<name>/ or /home/<name>/. Mirrors pre-commit-gate.sh:50-53
15
+ # exemptions so policy docs that quote the pattern don't trip.
16
+ #
17
+ # Per #2036 Finding #1 (adopter report 2026-10-02).
18
+ # Per @luminary nygard — stability pattern (circuit breaker on commit).
19
+ # Per @luminary hoare — precondition contract before `git commit`.
20
+
21
+ # check_staged_for_leaks — scan git-staged content for operator paths.
22
+ #
23
+ # Returns 0 when staged content is clean OR when every leaking file
24
+ # sits in an exempt path (policy docs, rules, luminaries).
25
+ # Returns 1 when staged content outside exempt paths carries a leak.
26
+ # On exit 1, every leaking file is named on stderr with line number
27
+ # and the matched pattern.
28
+ check_staged_for_leaks() {
29
+ # Staged non-exempt files. Match pre-commit-gate.sh:46-53 exemption shape
30
+ # so the two gates agree on which paths carry policy docs legitimately
31
+ # quoting operator paths.
32
+ local staged
33
+ staged=$(git diff --cached --name-only --diff-filter=AM 2>/dev/null | \
34
+ grep -vE '^\.claude/(rules|hooks|skills|luminaries|agents)/' | \
35
+ grep -vE '^standards/' | \
36
+ grep -vE '^architecture/' | \
37
+ grep -vE '^design/' | \
38
+ grep -vE '^docs/standards/' | \
39
+ grep -vE '^docs/rules/' | \
40
+ grep -vE '^chronicle/' | \
41
+ grep -vE '^strategy/' | \
42
+ grep -vE '^scripts/tests/' || true)
43
+
44
+ [ -z "$staged" ] && return 0
45
+
46
+ local leaked=""
47
+ while IFS= read -r f; do
48
+ [ -z "$f" ] && continue
49
+ [ ! -f "$f" ] && continue
50
+ local matches
51
+ matches=$(grep -nE '/Users/[A-Za-z0-9_.-]+|/home/[A-Za-z0-9_.-]+' "$f" 2>/dev/null || true)
52
+ if [ -n "$matches" ]; then
53
+ leaked="${leaked}
54
+ ${f}:
55
+ ${matches}
56
+ "
57
+ fi
58
+ done <<< "$staged"
59
+
60
+ if [ -n "$leaked" ]; then
61
+ {
62
+ echo ""
63
+ echo "============================================"
64
+ echo "BLOCKED: operator path in staged content (#2036 Finding #1)"
65
+ echo "============================================"
66
+ echo "$leaked"
67
+ echo ""
68
+ echo "Operator-specific paths (/Users/<name>, /home/<name>) must NOT"
69
+ echo "ship in adopter git. Common cause: docs/sdlc-traces/*.log"
70
+ echo "carrying absolute paths from PreToolUse hook callers."
71
+ echo ""
72
+ echo "Fixes:"
73
+ echo " 1. Delete the staged file if it's a trace log auto-generated"
74
+ echo " (gitignored going forward per #2036 Finding #1 cure)"
75
+ echo " 2. Rewrite the path as repo-relative"
76
+ echo " 3. Move policy docs under docs/standards/ or docs/rules/ (exempt)"
77
+ echo ""
78
+ echo "Policy: standards/two-layer-config.md (bassclef#740)"
79
+ echo "============================================"
80
+ } >&2
81
+ return 1
82
+ fi
83
+
84
+ return 0
85
+ }