@thebassclef/lite 1.9.9 → 1.9.11

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 (39) 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/hooks/artifact-ingestion-gate.sh +9 -3
  6. package/dist/lite/.claude/hooks/pre-commit-gate.sh +31 -0
  7. package/dist/lite/.claude/hooks/pre-commit-identifier-leak-scrub.sh +58 -2
  8. package/dist/lite/.claude/hooks/save-state.sh +6 -1
  9. package/dist/lite/.claude/hooks/session-reflection.d/83-install-written-paths-rebuild.sh +111 -0
  10. package/dist/lite/.claude/hooks/session-reflection.d/97-last-block-count.sh +53 -0
  11. package/dist/lite/.claude/hooks/session-reflection.d/tests/97-last-block-count.test.sh +131 -0
  12. package/dist/lite/.claude/hooks/session-reflection.sh +22 -0
  13. package/dist/lite/.claude/hooks/state-validate.sh +8 -0
  14. package/dist/lite/.claude/rules/install-written-paths-mediation.md +74 -0
  15. package/dist/lite/.claude/rules/last-block-marker.md +79 -0
  16. package/dist/lite/.claude/skills/autonomous/SKILL.md +53 -7
  17. package/dist/lite/.claude/skills/build/SKILL.md +41 -5
  18. package/dist/lite/.claude/skills/deploy-prod/SKILL.md +10 -0
  19. package/dist/lite/.claude/skills/ia-model/SKILL.md +8 -0
  20. package/dist/lite/.claude/skills/interaction-design/SKILL.md +8 -0
  21. package/dist/lite/.claude/skills/jtbd-tasks/SKILL.md +9 -1
  22. package/dist/lite/.claude/skills/last-block/SKILL.md +99 -0
  23. package/dist/lite/.claude/skills/launch/SKILL.md +44 -12
  24. package/dist/lite/.claude/skills/objectory-decompose/SKILL.md +5 -17
  25. package/dist/lite/.claude/skills/onboard-repo/SKILL.md +18 -18
  26. package/dist/lite/.claude/skills/personas/SKILL.md +1 -1
  27. package/dist/lite/.claude/skills/use-case/SKILL.md +5 -4
  28. package/dist/lite/bassclef-version.json +5 -5
  29. package/dist/lite/lib/capability-probe.sh +39 -1
  30. package/dist/lite/lib/composer-preflight.sh +20 -4
  31. package/dist/lite/lib/install-written-paths.sh +369 -0
  32. package/dist/lite/lib/last-block.sh +153 -0
  33. package/dist/lite/lib/luminary-pick.sh +35 -13
  34. package/dist/lite/lib/state.sh +82 -11
  35. package/dist/lite/scripts/interpret-input.sh +19 -4
  36. package/dist/lite/standards/lite-manifest-schema-changes.md +115 -0
  37. package/dist/lite/standards/lite-manifest.json +80 -26
  38. package/dist/lite/standards/state-spine.md +1 -0
  39. package/package.json +2 -2
@@ -40,8 +40,9 @@ __state_check_binary_deps() {
40
40
  if [ "${__STATE_DEPS_CHECKED:-}" = "1" ]; then
41
41
  return 0
42
42
  fi
43
+ # Baseline deps — awk + jq required unconditionally
43
44
  local missing=""
44
- for bin in awk python3 jq; do
45
+ for bin in awk jq; do
45
46
  command -v "$bin" >/dev/null 2>&1 || missing="$missing $bin"
46
47
  done
47
48
  if [ -n "$missing" ]; then
@@ -49,21 +50,63 @@ __state_check_binary_deps() {
49
50
  STATE ACCESSOR FAILURE
50
51
  Required binary missing from PATH:$missing
51
52
 
52
- These binaries are load-bearing for lib/state.sh accessors (Shape B
53
- YAML frontmatter extraction + JSON tooling).
53
+ awk + jq are load-bearing for lib/state.sh accessors.
54
54
 
55
55
  Resolution:
56
- macOS: brew install gawk python3 jq
57
- Debian: sudo apt-get install gawk python3 jq
58
-
59
- Closes the bassclef#598 silent-failure class.
56
+ macOS: brew install gawk jq
57
+ Debian: sudo apt-get install gawk jq
60
58
  EOF
61
59
  return 4
62
60
  fi
61
+
62
+ # Per cli#308: YAML parser needs EITHER yq OR python3+PyYAML.
63
+ # Stock macOS has python3 but not PyYAML. Pre-cli#308, that mismatch
64
+ # caused silent failures where __state_check_binary_deps passed but
65
+ # the actual yaml parse failed with ModuleNotFoundError. Now we check
66
+ # that at least one working parser exists.
67
+ __state_check_yaml_parser || return 6
63
68
  export __STATE_DEPS_CHECKED=1
64
69
  return 0
65
70
  }
66
71
 
72
+ # YAML parser check (per cli#308). Returns 0 if yq available OR
73
+ # python3+yaml-module importable. Returns 6 if neither.
74
+ __state_check_yaml_parser() {
75
+ # Cache the result per shell process.
76
+ if [ "${__STATE_YAML_PARSER:-}" = "yq" ] || [ "${__STATE_YAML_PARSER:-}" = "python3" ]; then
77
+ return 0
78
+ fi
79
+ # Prefer yq (faster + simpler contract).
80
+ if command -v yq >/dev/null 2>&1; then
81
+ export __STATE_YAML_PARSER=yq
82
+ return 0
83
+ fi
84
+ # Fall back to python3+PyYAML.
85
+ if command -v python3 >/dev/null 2>&1 && python3 -c "import yaml" >/dev/null 2>&1; then
86
+ export __STATE_YAML_PARSER=python3
87
+ return 0
88
+ fi
89
+ cat >&2 <<EOF
90
+ STATE ACCESSOR FAILURE — no YAML parser available (per cli#308)
91
+
92
+ lib/state.sh needs ONE of:
93
+ - yq (preferred — faster, no Python module needed)
94
+ - python3 + PyYAML module
95
+
96
+ Both are missing on this machine.
97
+
98
+ Resolution:
99
+ macOS (preferred): brew install yq
100
+ macOS (alt): python3 -m pip install pyyaml
101
+ Debian: sudo apt-get install yq OR python3 -m pip install pyyaml
102
+
103
+ Per cli#308 — this check closes the silent-failure class where
104
+ python3 existed but PyYAML did not, and callers reported "goal not
105
+ found" (wrong diagnosis).
106
+ EOF
107
+ return 6
108
+ }
109
+
67
110
  # yq2json — extract YAML frontmatter from markdown file, emit JSON.
68
111
  # Used by Shape B (markdown + YAML frontmatter + body) entity accessors.
69
112
  #
@@ -86,7 +129,7 @@ EOF
86
129
  # on invalid input) + @luminary michael-feathers (characterization
87
130
  # tests RED-first via fixtures/2026-09-06-silent-empty/).
88
131
  __state_yq2json() {
89
- __state_check_binary_deps || return 4
132
+ __state_check_binary_deps || return $?
90
133
  local file="$1"
91
134
  if [ ! -f "$file" ]; then
92
135
  return 1
@@ -127,7 +170,23 @@ __state_yq2json() {
127
170
 
128
171
  local out
129
172
  local ec
130
- out=$(awk '/^---$/{c++; next} c==1' "$file" | python3 -c "
173
+ # Per cli#308 — prefer yq when available, fall back to python3+PyYAML.
174
+ # __state_check_binary_deps (called at top of this function via return 4
175
+ # path) already resolved __STATE_YAML_PARSER to one of the two.
176
+ if [ "${__STATE_YAML_PARSER:-}" = "yq" ]; then
177
+ out=$(awk '/^---$/{c++; next} c==1' "$file" | yq -o=json '.' 2>&1)
178
+ ec=$?
179
+ if [ "$ec" -ne 0 ]; then
180
+ printf 'yaml parse failed (yq): %s\n' "$out" >&2
181
+ return 3
182
+ fi
183
+ # yq returns `null` for empty input; coerce to `{}` for consistency
184
+ # with python3 path.
185
+ if [ "$out" = "null" ]; then
186
+ out="{}"
187
+ fi
188
+ else
189
+ out=$(awk '/^---$/{c++; next} c==1' "$file" | python3 -c "
131
190
  import sys, yaml, json
132
191
  try:
133
192
  data = yaml.safe_load(sys.stdin) or {}
@@ -136,7 +195,8 @@ except Exception as e:
136
195
  print(f'yaml parse failed: {e}', file=sys.stderr)
137
196
  sys.exit(3)
138
197
  ")
139
- ec=$?
198
+ ec=$?
199
+ fi
140
200
  # Instrument real-parse count for #1519 tests + observability.
141
201
  __STATE_YQ2JSON_PARSE_COUNT=$(( ${__STATE_YQ2JSON_PARSE_COUNT:-0} + 1 ))
142
202
  if [ "$ec" -ne 0 ]; then
@@ -185,7 +245,7 @@ __state_atomic_write() {
185
245
  # output) + @luminary michael-nygard (silent-fail wrap; write does not
186
246
  # crash caller).
187
247
  __state_yaml_write() {
188
- __state_check_binary_deps || return 4
248
+ __state_check_binary_deps || return $?
189
249
  # Finding #6 (#2036): `local path` is a zsh-magic variable name — ties to
190
250
  # $PATH when sourced from zsh. Renamed to `local p` throughout this file.
191
251
  local p="$1"
@@ -1030,6 +1090,17 @@ state_reference_get() {
1030
1090
  marker)
1031
1091
  state_marker_get "$id"
1032
1092
  ;;
1093
+ input-artifact)
1094
+ # InputArtifact — Shape C (pure JSON) per state-spine.md L262.
1095
+ # Added per sunj-labs/bassclef-cli#315: /objectory-decompose Step 1
1096
+ # reads input-artifact via this accessor; without a case the call
1097
+ # exited 5 and the SKILL could not read the artifact in lite.
1098
+ if [ -f "docs/input-artifacts/${id}.json" ]; then
1099
+ cat "docs/input-artifacts/${id}.json"
1100
+ else
1101
+ return 1
1102
+ fi
1103
+ ;;
1033
1104
  gate)
1034
1105
  # Gates derive from Markers; not addressable directly in v0
1035
1106
  echo "state_reference_get: gate type derives from Marker; not directly addressable in v0" >&2
@@ -52,12 +52,14 @@ SOURCE=""
52
52
  TYPE="auto"
53
53
  PERSIST=0
54
54
  SLUG=""
55
+ INTENT="" # cli#319 cure — operator intent text; defaults to SOURCE in text mode
55
56
 
56
57
  while [ $# -gt 0 ]; do
57
58
  case "$1" in
58
59
  --type=*) TYPE="${1#--type=}"; shift ;;
59
60
  --persist) PERSIST=1; shift ;;
60
61
  --slug=*) SLUG="${1#--slug=}"; shift ;;
62
+ --intent=*) INTENT="${1#--intent=}"; shift ;; # cli#319
61
63
  --help|-h) usage; exit 0 ;;
62
64
  --*) echo "interpret-input: unknown flag '$1'" >&2; usage >&2; exit 2 ;;
63
65
  *)
@@ -117,7 +119,15 @@ fi
117
119
 
118
120
  NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
119
121
 
120
- STRUCTURAL_HINTS='{}'
122
+ # cli#319 cure — default INTENT to SOURCE in text mode; empty otherwise.
123
+ # /pick-luminaries matches intent against the luminary catalog; an empty
124
+ # intent is explicitly valid (adopters may inject it later), but text input
125
+ # carries operator intent in SOURCE already — reuse it as a safe default.
126
+ if [ -z "$INTENT" ] && [ "$TYPE" = "text" ]; then
127
+ INTENT="$SOURCE"
128
+ fi
129
+
130
+ STRUCTURAL_HINTS='{"verb_goal_pairs": []}'
121
131
 
122
132
  case "$TYPE" in
123
133
  text)
@@ -149,7 +159,7 @@ case "$TYPE" in
149
159
  if [ -z "$REPO_LOCAL" ]; then
150
160
  # Source can't be resolved; return Phase B-style stub (still valid InputArtifact)
151
161
  EXTRACTED_TEXT="deferred to Phase C"
152
- STRUCTURAL_HINTS='{}'
162
+ STRUCTURAL_HINTS='{"verb_goal_pairs": []}'
153
163
  else
154
164
  EXTRACT_OUT=$(bash "$REPO_ROOT/scripts/interpret-input/extract-repo.sh" "$REPO_LOCAL" 2>/dev/null)
155
165
  EXTRACT_RC=$?
@@ -160,10 +170,13 @@ case "$TYPE" in
160
170
 
161
171
  if [ "$EXTRACT_RC" != "0" ] || [ -z "$EXTRACT_OUT" ]; then
162
172
  EXTRACTED_TEXT="repo extraction returned no signal for $SOURCE"
163
- STRUCTURAL_HINTS='{}'
173
+ STRUCTURAL_HINTS='{"verb_goal_pairs": []}'
164
174
  else
165
175
  EXTRACTED_TEXT=$(echo "$EXTRACT_OUT" | jq -r '.extracted_text')
166
- STRUCTURAL_HINTS=$(echo "$EXTRACT_OUT" | jq -c '.structural_hints')
176
+ # cli#312 cure — merge default verb_goal_pairs: [] so downstream readers
177
+ # (/build, /launch templates) always find the key even when extract
178
+ # doesn't produce it. Extract wins on any key it does emit.
179
+ STRUCTURAL_HINTS=$(echo "$EXTRACT_OUT" | jq -c '{verb_goal_pairs: []} + .structural_hints')
167
180
  fi
168
181
  fi
169
182
  EXTERNAL_URLS='[]'
@@ -179,6 +192,7 @@ ARTIFACT=$(jq -n \
179
192
  --arg type "$TYPE" \
180
193
  --arg source "$SOURCE" \
181
194
  --arg extracted_text "$EXTRACTED_TEXT" \
195
+ --arg intent "$INTENT" \
182
196
  --arg created_at "$NOW" \
183
197
  --argjson external_urls "$EXTERNAL_URLS" \
184
198
  --argjson structural_hints "$STRUCTURAL_HINTS" \
@@ -187,6 +201,7 @@ ARTIFACT=$(jq -n \
187
201
  type: $type,
188
202
  source: $source,
189
203
  extracted_text: $extracted_text,
204
+ intent: $intent,
190
205
  structural_hints: $structural_hints,
191
206
  external_urls: $external_urls,
192
207
  created_at: $created_at
@@ -34,6 +34,121 @@ Every entry stays in this file for the life of the manifest. Never pruned. When
34
34
 
35
35
  ## Entries (newest first)
36
36
 
37
+ ### cli#319 — interpret-input writes intent field (v1.16.9 → v1.16.10, 2026-10-07)
38
+
39
+ - **Change type:** content-change (1 file: `scripts/interpret-input.sh`). Script now emits `intent` field in input-artifact JSON. Prior shape omitted it.
40
+ - **Fields:** 448 entries (unchanged). Content hash shifts on the `interpret-input` script slug.
41
+ - **Version bump:** v1.16.9 → v1.16.10 (patch — content-only change on 1 slug; rides on top of cli#312 v1.16.9 bump).
42
+ - **Rationale:** bassclef-cli#319 — Kunal cold-adopter smoke caught downstream readers (`/launch`, `/build`) needing the intent field + missing on artifact JSON. Per PR #2105.
43
+ - **Downstream cure:** no action for adopters at exact-pin. Downstream readers now get a stable intent key.
44
+ - **Old shape retired:** nothing retired. Additive key.
45
+
46
+
47
+ ### cli#312 — interpret-input emits verb_goal_pairs default (v1.16.8 → v1.16.9, 2026-10-07)
48
+
49
+ - **Change type:** content-change (1 file: `scripts/interpret-input.sh`). Script now writes `structural_hints.verb_goal_pairs` as an empty array when no pairs detected. Prior code omitted the key.
50
+ - **Fields:** 448 entries (unchanged). Content hash shifts on the `interpret-input` script slug.
51
+ - **Version bump:** v1.16.8 → v1.16.9 (patch — content-only change on 1 slug; rides on top of cli#324 v1.16.8 bump).
52
+ - **Rationale:** bassclef-cli#312 — Kunal cold-adopter smoke caught downstream readers tripping on missing key. Default-empty-array is defensive. Per PR #2104.
53
+ - **Downstream cure:** no action for adopters at exact-pin. Downstream readers of `structural_hints.verb_goal_pairs` get a consistent array shape.
54
+ - **Old shape retired:** nothing retired. Behavior is additive — the key always exists now; readers that handled missing-key still work.
55
+
56
+
57
+ ### cli#324 — /launch Phase 11 greenfield partial vs redesign full (v1.16.7 → v1.16.8, 2026-10-07)
58
+
59
+ - **Change type:** content-change (1 file: `.claude/skills/launch/SKILL.md`). Phase 11 now distinguishes greenfield partial-fit from redesign full-fit. Prior behavior conflated both cases.
60
+ - **Fields:** 448 entries (unchanged). Content hash shifts on the `launch` skill slug.
61
+ - **Version bump:** v1.16.7 → v1.16.8 (patch — content-only change on 1 slug; rides on top of cli#322 v1.16.7 bump).
62
+ - **Rationale:** bassclef-cli#324 — Kunal cold-adopter smoke caught `/launch` Phase 11 misreading greenfield intents as redesign full-reshape. Partial-fit path missing. Per PR #2103.
63
+ - **Downstream cure:** no action for adopters at exact-pin. Phase 11 now reads adopter intent + routes to partial OR full. Prior sessions that hit full-reshape see no regression (full stays as-is).
64
+ - **Old shape retired:** nothing retired. Phase 11 branch is additive.
65
+
66
+
67
+ ### cli#322 — /launch Phase 14 jargon gate (v1.16.6 → v1.16.7, 2026-10-07)
68
+
69
+ - **Change type:** content-change (1 file: `.claude/skills/launch/SKILL.md`). Phase 14 template now runs a jargon gate on the output before writing. Prior behavior shipped jargon through.
70
+ - **Fields:** 448 entries (unchanged). Content hash shifts on the `launch` skill slug.
71
+ - **Version bump:** v1.16.6 → v1.16.7 (patch — content-only change on 1 slug; rides on top of cli#309 v1.16.6 bump).
72
+ - **Rationale:** bassclef-cli#322 — Kunal cold-adopter smoke caught `/launch` Phase 14 shipping bassclef-internal jargon into adopter-facing output. Per PR #2100.
73
+ - **Downstream cure:** no action for adopters at exact-pin. Phase 14 now surfaces jargon via gate before write.
74
+ - **Old shape retired:** nothing retired. Gate is additive.
75
+
76
+
77
+ ### cli#309 — /build hosting_platform=none warns (v1.16.5 → v1.16.6, 2026-10-07)
78
+
79
+ - **Change type:** content-change (1 file: `lib/composer-preflight.sh`). Helper now warns + continues when hosting_platform=none instead of refusing. Prior code refused unconditionally.
80
+ - **Fields:** 448 entries (unchanged). Content hash shifts on the `composer-preflight` lib slug.
81
+ - **Version bump:** v1.16.5 → v1.16.6 (patch — content-only change on 1 slug; rides on top of cli#311 v1.16.5 bump).
82
+ - **Rationale:** bassclef-cli#309 — Kunal cold-adopter smoke caught `/build` refusing when adopter opted out of hosting. Local-only builds are a valid shape. Per PR #2101.
83
+ - **Downstream cure:** no action for adopters at exact-pin. Local-only builds (hosting_platform=none) now warn + proceed.
84
+ - **Old shape retired:** nothing retired. Behavior is additive — refuse stays for other invalid states.
85
+
86
+
87
+ ### cli#311 — artifact-ingestion-gate skip when parent_bet is null (v1.16.4 → v1.16.5, 2026-10-07)
88
+
89
+ - **Change type:** content-change (1 file: `.claude/hooks/artifact-ingestion-gate.sh`). Hook now skips the goal-doc parent-walk when the active goal declares `parent_bet: null` in frontmatter. Prior behavior treated null as missing + fired BLOCK on orientation-gate.
90
+ - **Fields:** 448 entries (unchanged). Content hash shifts on the `artifact-ingestion-gate` hook slug.
91
+ - **Version bump:** v1.16.4 → v1.16.5 (patch — content-only change on 1 slug; no entry count change).
92
+ - **Rationale:** bassclef-cli#311 — Kunal cold-adopter smoke caught the orientation-gate firing BLOCK on root-level goals (no parent). Null parent_bet is valid state for arc-root goals; the gate was treating it as unresolved reference. Per PR #2102.
93
+ - **Downstream cure:** no action for adopters at exact-pin. Root goals with `parent_bet: null` stop firing BLOCK at session-start; existing goals with non-null parents still get the walk.
94
+ - **Old shape retired:** nothing retired. Null-parent-bet branch is additive on top of existing walk logic.
95
+
96
+
97
+ ### #2036 Finding #10 — /last-block skill + marker format (v1.15.5 → v1.16.0, 2026-10-06)
98
+
99
+ - **Change type:** content-add (4 new files: `.claude/skills/last-block/SKILL.md`, `lib/last-block.sh`, `.claude/rules/last-block-marker.md`, `.claude/hooks/session-reflection.d/97-last-block-count.sh`). New skill that prints the exact text of a BLOCK or guardrail banner from this session or prior. New marker format at `state/last-block/<slug>.md`.
100
+ - **Fields:** 448 entries (+4 from 444). Four new slugs — skill `last-block`, lib `last-block`, rule `last-block-marker`, hook `97-last-block-count`.
101
+ - **Version bump:** v1.15.5 → v1.16.0 (minor — content-add of 4 new lite slugs). Rides on top of Finding #7 which landed first (v1.15.4 → v1.15.5).
102
+ - **Rationale:** Kunal v1.6.4 adopter run — "I saw a BLOCK banner scroll off the top of the terminal. No way to read it again." Finding #10 adds `/last-block` for recall + a hook that counts markers at session-reflection time. Per bassclef-upstream#2036 Finding #10 + PR #2061.
103
+ - **Downstream cure:** no action for adopters at exact-pin. Four new files land on next sync — all `tier: lite` so every tier picks them up. No shape change on existing slugs.
104
+ - **Old shape retired:** nothing retired (content-add only).
105
+
106
+
107
+ ### cli#305 — identifier-leak scrub self-exclude + commit author check (v1.15.3 → v1.15.4, 2026-10-04)
108
+
109
+ - **Change type:** content-change (1 file: `.claude/hooks/pre-commit-identifier-leak-scrub.sh`). Check 2 now filters out hunks from the hook's own files, its test, the rule, and the pattern source. Check 3 added — scans `git var GIT_AUTHOR_IDENT` for the pattern set plus the `.local` suffix. BLOCK banner adds a resolution path pointing at the GitHub no-reply address.
110
+ - **Fields:** 444 entries (unchanged). Content hash shifts on the `pre-commit-identifier-leak-scrub` hook slug.
111
+ - **Version bump:** v1.15.3 → v1.15.4 (patch — content-only change on 1 slug; no entry count change). Rides on top of the Finding #7 bump (v1.15.3) when PRs land in order.
112
+ - **Rationale:** Kunal cli#305 — first commit after `bassclef init` blocked on the check's own files; author identity leaked silently when `user.email` was unset on Mac. Class A identity leak sister to cli#313 + cli#320 shipped in v1.15.2. Per PR #2062 (planned).
113
+ - **Downstream cure:** no action for adopters at exact-pin. Self-exclude paths are PATH-scoped, not pattern-scoped — leaks moved into non-self files still block. Author check is additive; adopters with clean emails see no change. Adopters whose git author ends in `.local` or carries a hostname pattern see the new BLOCK with a recipe pointing at `<username>@users.noreply.github.com`.
114
+ - **Old shape retired:** nothing retired. Check 1 (message) + Check 2 (staged content, now filtered) + Check 3 (author) chain is additive.
115
+
116
+ ### #2036 Finding #7 — Playwright probe file-based fallback (v1.15.4 → v1.15.5, 2026-10-05)
117
+
118
+ - **Change type:** content-change (1 file: `lib/capability-probe.sh`). `probe_mcp_playwright()` extended with four MCP config file fallback paths after the `CLAUDE_MCP_PLAYWRIGHT` env-var fast path. Fail-soft on missing `jq` + malformed JSON.
119
+ - **Fields:** 444 entries (unchanged). Content hash shifts on the `capability-probe` lib slug.
120
+ - **Version bump:** v1.15.4 → v1.15.5 (patch — content-change on existing lib slug per policy). Rebased on top of cli#305 which landed first.
121
+ - **Rationale:** Kunal v1.6.4 adopter run installed Playwright MCP but did not set `CLAUDE_MCP_PLAYWRIGHT`; probe returned 1; `/riff` + `/build` skipped screenshots. Fallback now reads `.mcp.json`, project + user `.claude/settings.json`, and `~/.claude.json`. Per bassclef-upstream#2036 Finding #7 + PR #2060.
122
+ - **Downstream cure:** nothing — byte-for-byte unchanged when `CLAUDE_MCP_PLAYWRIGHT` is set (fast path first). Adopters without the env var set gain file-based detection on the fly.
123
+ - **Old shape retired:** immediately (new behavior is additive).
124
+
125
+ ### cli#313 + cli#320 — identity-leak sweep in luminary-pick + personas (v1.15.1 → v1.15.2, 2026-10-04)
126
+
127
+ - **Change type:** content-change (2 files: `lib/luminary-pick.sh` + `.claude/skills/personas/SKILL.md`). luminary-pick basedir fallback moves from `$HOME/src/sunj-labs/bassclef` to `$CLAUDE_PROJECT_DIR` → `git rev-parse` → `pwd` chain, plus adds `.claude/luminaries/*.md` catalog fallback for lite tier. personas SKILL default slug changes from email-derived (`operator-[local-part]`) to plain `operator` (never from email).
128
+ - **Fields:** 444 entries (unchanged). Content hash shifts on the `luminary-pick` lib slug + `personas` skill slug.
129
+ - **Version bump:** v1.15.1 → v1.15.2 (patch; content-only on 2 slugs; no schema shape shift; no entry count change).
130
+ - **Rationale:** bassclef-cli#313 (luminary-pick hardcoded eng path; adopters hit "catalog dir missing" every /launch Phase 3.5) + bassclef-cli#320 (operator slug derived from git email leaked GitHub account ID into committed docs). Kunal cold-adopter smoke caught both; shared class is "engineering assumption in cold paths" (per plan doc Slot 3).
131
+ - **Downstream cure:** no action for adopters at exact-pin. Both changes are additive — lite tier adopters now get a working picker catalog; email-derived slugs never fire again. Operators wanting a display name beyond `operator` get an in-turn prompt instead.
132
+ - **Old shape retired:** nothing retired. Both old shapes stay viable (BASSCLEF_DIR env var still honored first; manual slug entry still allowed via operator reply).
133
+
134
+ ### cli#307 — /build Phase 0 awk matches Step-N + WU-N (v1.15.0 → v1.15.1, 2026-10-04)
135
+
136
+ - **Change type:** content-change (1 file: `.claude/skills/build/SKILL.md`). Phase 0 safety floor awk pattern + prose at L276 updated to recognize both `### Step-N` (new vocab per ADR-040 D1) and `### WU-N` (legacy).
137
+ - **Fields:** 444 entries (unchanged). Content hash shifts on the `build` skill slug.
138
+ - **Version bump:** v1.15.0 → v1.15.1 (patch; content-only change on 1 slug; no schema shape shift; no entry count change).
139
+ - **Rationale:** bassclef-cli#307 — Kunal cold-adopter smoke caught the Phase 0 awk reading zero acceptance lines on Step-format specs. Partial-rename regression from ADR-040 D1 (Phase 1 grep got the rename, Phase 0 awk was missed).
140
+ - **Downstream cure:** no action for adopters at exact-pin — the fix is additive (both heading forms match). Step-format specs now trigger the floor as designed. WU-format specs continue to work (characterization).
141
+ - **Old shape retired:** nothing retired. The WU-only pattern leaves. The `(Step|WU)-` pattern stays indefinitely.
142
+
143
+ ### #2036 — install-written-paths manifest + lib + rule (v1.14.4 → v1.15.0, 2026-10-03)
144
+
145
+ - **Change type:** content-add (3 new entries: `lib/install-written-paths.sh`, `.claude/rules/install-written-paths-mediation.md`, `.claude/hooks/session-reflection.d/83-install-written-paths-rebuild.sh`); content-change on `.claude/hooks/pre-commit-gate.sh` (filter block added for Kunal Finding #8 cure).
146
+ - **Fields:** 441 entries → 444 entries (3 adds, 1 content_hash shift).
147
+ - **Version bump:** v1.14.4 → v1.15.0 (content-add MINOR per discipline policy).
148
+ - **Rationale:** bassclef-upstream#2036 Finding #8 structural cure. See `architecture/reviews/2026-10-03.md` + plan doc `docs/next-session-plan-2026-10-04-kunal-slots-5-6.md` Slot 5.
149
+ - **Downstream cure:** no action for adopters at exact-pin — the new entries are additive and the lib's accessor API is new. Adopters pulling at `@thebassclef/lite@^1.15.0` get the cure automatically; the session-start fragment regenerates the install-written-paths manifest on first fresh-install session.
150
+ - **Old shape retired:** nothing retired. All existing entries preserved.
151
+
37
152
  ### #1777 — friction hook pattern adds stage|launch new skill vocabulary (v1.14.2 → v1.14.3, 2026-10-01)
38
153
 
39
154
  - **Change type:** content-change (1 file: `post-skill-friction-check` hook)