@thebassclef/lite 1.9.16 → 1.9.19
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.
- package/README.md +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lite/.claude/agents/builder.md +7 -4
- package/dist/lite/.claude/bassclef-orientation.md +22 -0
- package/dist/lite/.claude/hooks/artifact-ingestion-gate.sh +11 -3
- package/dist/lite/.claude/hooks/assert-verify-steering.sh +1 -19
- package/dist/lite/.claude/hooks/plain-english-steering.sh +86 -41
- package/dist/lite/.claude/hooks/pre-commit-gate.sh +10 -2
- package/dist/lite/.claude/hooks/pre-push-shellcheck.sh +196 -0
- package/dist/lite/.claude/hooks/reviewer-dispatch-check.sh +153 -0
- package/dist/lite/.claude/hooks/session-reflection.d/83-install-written-paths-rebuild.sh +15 -32
- package/dist/lite/.claude/hooks/turn-prose-density-measure.sh +109 -0
- package/dist/lite/.claude/hooks/turn-prose-kiss-check.sh +28 -0
- package/dist/lite/.claude/rules/branching.md +2 -0
- package/dist/lite/.claude/rules/identifier-leak-prevention.md +2 -1
- package/dist/lite/.claude/rules/install-written-paths-mediation.md +7 -0
- package/dist/lite/.claude/rules/lite-manifest-schema-change-discipline.md +23 -0
- package/dist/lite/.claude/rules/reviewer-dispatch.md +95 -0
- package/dist/lite/.claude/settings.json +10 -0
- package/dist/lite/.claude/skills/build/SKILL.md +81 -6
- package/dist/lite/.claude/skills/eisenhower/SKILL.md +26 -0
- package/dist/lite/.claude/skills/launch/SKILL.md +45 -2
- package/dist/lite/.claude/skills/onboard-repo/SKILL.md +208 -40
- package/dist/lite/.claude/skills/onboard-repo/claude-oauth-probe.sh +60 -0
- package/dist/lite/.claude/skills/onboard-repo/concise-prompt.sh +73 -0
- package/dist/lite/.claude/skills/onboard-repo/write-state-marker.sh +81 -0
- package/dist/lite/.claude/skills/pre-mortem/SKILL.md +56 -0
- package/dist/lite/.claude/skills/sprint/SKILL.md +24 -3
- package/dist/lite/.claude/skills/temperance/SKILL.md +15 -4
- package/dist/lite/README.md +2 -0
- package/dist/lite/bassclef-version.json +5 -5
- package/dist/lite/lib/install-written-paths.sh +250 -3
- package/dist/lite/lib/prose-marker.sh +203 -0
- package/dist/lite/lib/tier-dependency-audit.sh +52 -25
- package/dist/lite/presence/install/bassclef-sync.template.sh +86 -8
- package/dist/lite/presence/install/schedule-auto-save.cron.sh +30 -2
- package/dist/lite/scripts/generate-lite-manifest.sh +15 -0
- package/dist/lite/scripts/launch/local-serve.sh +149 -8
- package/dist/lite/standards/bassclef-wiring-manifest.json +12 -0
- package/dist/lite/standards/lite-manifest-schema-changes.md +84 -0
- package/dist/lite/standards/lite-manifest.json +100 -29
- package/dist/lite/standards/prose-marker-format.md +105 -0
- package/package.json +1 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# install-class: project
|
|
4
|
+
# concise-prompt.sh — handle Y/n/skip for Anthropic Concise output style.
|
|
5
|
+
# Called by /onboard-repo SKILL Phase 2.7. Writes operator ~/.claude/settings.json
|
|
6
|
+
# atomically via jq read-modify-write so every pre-existing field survives.
|
|
7
|
+
#
|
|
8
|
+
# Per Longrun 2 Step 2g (bassclef-upstream#2197).
|
|
9
|
+
# ADR-049 opt-in default off — adopter picks Y explicitly.
|
|
10
|
+
#
|
|
11
|
+
# Env: HOME_OVERRIDE points at a fixture home for testing.
|
|
12
|
+
# Returns:
|
|
13
|
+
# 0 — path applied (Y wrote field, n wrote declined sentinel, skip wrote skip sentinel)
|
|
14
|
+
# 1 — jq missing OR write failed (fail-soft; operator sees warning)
|
|
15
|
+
# 2 — invalid choice
|
|
16
|
+
|
|
17
|
+
set +e
|
|
18
|
+
|
|
19
|
+
concise_prompt_apply() {
|
|
20
|
+
local choice="${1:-}"
|
|
21
|
+
local home="${HOME_OVERRIDE:-$HOME}"
|
|
22
|
+
local settings="$home/.claude/settings.json"
|
|
23
|
+
local state_dir="$home/.claude/state"
|
|
24
|
+
mkdir -p "$home/.claude" "$state_dir" 2>/dev/null
|
|
25
|
+
|
|
26
|
+
case "$choice" in
|
|
27
|
+
Y|y)
|
|
28
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
29
|
+
echo "concise-prompt: jq not installed; skipping outputStyle write" >&2
|
|
30
|
+
return 1
|
|
31
|
+
fi
|
|
32
|
+
[ -f "$settings" ] || echo '{}' > "$settings"
|
|
33
|
+
local tmp
|
|
34
|
+
tmp=$(mktemp 2>/dev/null) || return 1
|
|
35
|
+
if jq '. + {"outputStyle": "Concise"}' "$settings" > "$tmp" 2>/dev/null; then
|
|
36
|
+
mv "$tmp" "$settings"
|
|
37
|
+
else
|
|
38
|
+
rm -f "$tmp" 2>/dev/null
|
|
39
|
+
return 1
|
|
40
|
+
fi
|
|
41
|
+
rm -f "$state_dir/concise-prompt-skipped" "$state_dir/concise-prompt-declined" 2>/dev/null
|
|
42
|
+
return 0
|
|
43
|
+
;;
|
|
44
|
+
n|N)
|
|
45
|
+
touch "$state_dir/concise-prompt-declined" 2>/dev/null || return 1
|
|
46
|
+
return 0
|
|
47
|
+
;;
|
|
48
|
+
skip|SKIP|s|S)
|
|
49
|
+
touch "$state_dir/concise-prompt-skipped" 2>/dev/null || return 1
|
|
50
|
+
return 0
|
|
51
|
+
;;
|
|
52
|
+
*)
|
|
53
|
+
return 2
|
|
54
|
+
;;
|
|
55
|
+
esac
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
concise_prompt_should_fire() {
|
|
59
|
+
local home="${HOME_OVERRIDE:-$HOME}"
|
|
60
|
+
local state_dir="$home/.claude/state"
|
|
61
|
+
local settings="$home/.claude/settings.json"
|
|
62
|
+
[ -f "$state_dir/concise-prompt-declined" ] && return 1
|
|
63
|
+
if [ -f "$settings" ] && command -v jq >/dev/null 2>&1; then
|
|
64
|
+
if jq -e '.outputStyle == "Concise"' "$settings" >/dev/null 2>&1; then
|
|
65
|
+
return 1
|
|
66
|
+
fi
|
|
67
|
+
fi
|
|
68
|
+
return 0
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
|
|
72
|
+
concise_prompt_apply "${1:-}"
|
|
73
|
+
fi
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# write-state-marker.sh — Phase 2.3.9 helper for /onboard-repo (bassclef-upstream#664).
|
|
4
|
+
# Retagged standard→lite per bassclef-upstream#2140 (Cure 1) so lite adopters
|
|
5
|
+
# running /onboard-repo at Phase 2.3.9 capture the state marker cleanly.
|
|
6
|
+
# Script shape is small (≤80 lines, no bash gotchas); belongs in the lite bundle.
|
|
7
|
+
#
|
|
8
|
+
# Writes .claude/state/onboard-repo.state.json capturing which mode ran, when,
|
|
9
|
+
# and which files landed. Re-runs append to history[] instead of clobbering.
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# bash .claude/skills/onboard-repo/write-state-marker.sh <mode>
|
|
13
|
+
#
|
|
14
|
+
# Env:
|
|
15
|
+
# ONBOARD_TARGET_DIR root of the adopter repo (defaults to $PWD)
|
|
16
|
+
#
|
|
17
|
+
# Composes with:
|
|
18
|
+
# - Configs UI first-run overlay (bassclef/configs/, bet 2026-07-08a WU-7)
|
|
19
|
+
# - @luminary john-ousterhout — deep module — one script, one job
|
|
20
|
+
|
|
21
|
+
set -eu
|
|
22
|
+
|
|
23
|
+
MODE="${1:-default}"
|
|
24
|
+
TARGET="${ONBOARD_TARGET_DIR:-$PWD}"
|
|
25
|
+
|
|
26
|
+
mkdir -p "$TARGET/.claude/state"
|
|
27
|
+
MARKER="$TARGET/.claude/state/onboard-repo.state.json"
|
|
28
|
+
NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
29
|
+
|
|
30
|
+
# Collect paths written to the adopter repo — check for existence.
|
|
31
|
+
PATHS_JSON='['
|
|
32
|
+
sep=""
|
|
33
|
+
for p in \
|
|
34
|
+
".claude/bassclef-configs.jsonc" \
|
|
35
|
+
".bassclef-source.json" \
|
|
36
|
+
".claude/CLAUDE.md" \
|
|
37
|
+
"docs/whereami.md"; do
|
|
38
|
+
if [ -f "$TARGET/$p" ]; then
|
|
39
|
+
PATHS_JSON="$PATHS_JSON$sep\"$p\""
|
|
40
|
+
sep=","
|
|
41
|
+
fi
|
|
42
|
+
done
|
|
43
|
+
PATHS_JSON="$PATHS_JSON]"
|
|
44
|
+
|
|
45
|
+
if [ -f "$MARKER" ]; then
|
|
46
|
+
# Existing marker — append prior run to history[]; keep top-level as this run.
|
|
47
|
+
python3 - "$MARKER" "$MODE" "$NOW" "$PATHS_JSON" <<'PY'
|
|
48
|
+
import json, sys
|
|
49
|
+
marker_path = sys.argv[1]
|
|
50
|
+
mode = sys.argv[2]
|
|
51
|
+
now = sys.argv[3]
|
|
52
|
+
paths_written = json.loads(sys.argv[4])
|
|
53
|
+
with open(marker_path) as f:
|
|
54
|
+
data = json.load(f)
|
|
55
|
+
history = data.get("history", [])
|
|
56
|
+
history.append({
|
|
57
|
+
"mode": data.get("mode", "unknown"),
|
|
58
|
+
"timestamp_iso": data.get("timestamp_iso", "unknown"),
|
|
59
|
+
"paths_written": data.get("paths_written", []),
|
|
60
|
+
})
|
|
61
|
+
data["mode"] = mode
|
|
62
|
+
data["timestamp_iso"] = now
|
|
63
|
+
data["paths_written"] = paths_written
|
|
64
|
+
data["history"] = history
|
|
65
|
+
data["skill_version"] = data.get("skill_version", "1.0")
|
|
66
|
+
with open(marker_path, "w") as f:
|
|
67
|
+
json.dump(data, f, indent=2)
|
|
68
|
+
PY
|
|
69
|
+
else
|
|
70
|
+
cat > "$MARKER" <<JSON
|
|
71
|
+
{
|
|
72
|
+
"mode": "$MODE",
|
|
73
|
+
"timestamp_iso": "$NOW",
|
|
74
|
+
"paths_written": $PATHS_JSON,
|
|
75
|
+
"history": [],
|
|
76
|
+
"skill_version": "1.0"
|
|
77
|
+
}
|
|
78
|
+
JSON
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
echo "wrote onboard-repo state marker at $MARKER (mode: $MODE)"
|
|
@@ -111,13 +111,69 @@ For non-always-fire scope, `/pre-mortem light` picks 3 lenses appropriate to the
|
|
|
111
111
|
- Per lens: 5-8 imagined failures written by operator; Claude collates without ranking during the pass
|
|
112
112
|
- Follow-up pass: pick top 5 risks total (across all lenses); name owner + mitigation per risk
|
|
113
113
|
- Output at `docs/risk-ledgers/<slug>.md` — short-form ledger
|
|
114
|
+
- **Write marker at `state/markers/pre-mortem/<branch-slug>.marker`** — downstream skills (`/build` Phase 3, Reviewer) read it. See § Marker contract below.
|
|
114
115
|
|
|
115
116
|
### Full mode (90 min)
|
|
116
117
|
|
|
118
|
+
<!-- FKGL-EXEMPT: full-mode bullets inherit light-mode shape with denser per-risk fields (severity + trigger + mitigation); dense bullet reference list, not prose. -->
|
|
117
119
|
- 5-8 rotating lenses per domain map
|
|
118
120
|
- Per lens: 10-15 imagined failures; per-risk severity (high/medium/low); per-risk trigger signal (what would tell operator the risk materialized); per-risk mitigation
|
|
119
121
|
- Follow-up pass: rank all risks by severity × probability; name owner + mitigation trigger + monitoring signal per top-10 risks
|
|
120
122
|
- Output at `docs/risk-ledgers/<slug>.md` — research-grade ledger
|
|
123
|
+
- **Write marker at `state/markers/pre-mortem/<branch-slug>.marker`** — downstream skills read it. See § Marker contract below.
|
|
124
|
+
<!-- /FKGL-EXEMPT -->
|
|
125
|
+
|
|
126
|
+
<!-- FKGL-EXEMPT: § Marker contract — technical reference with bash fenced blocks and marker body template; not prose. Per bassclef-upstream#2175 Batch B — F4. -->
|
|
127
|
+
|
|
128
|
+
## Marker contract (per bassclef-upstream#2175 Batch B — F4)
|
|
129
|
+
|
|
130
|
+
After the risk ledger lands, write a marker at `state/markers/pre-mortem/<branch-slug>.marker`. Downstream skills read it. `/build` Phase 3 surfaces the top risks. Reviewer reads it during review.
|
|
131
|
+
|
|
132
|
+
Marker body shape:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
# Pre-mortem marker — <scope>
|
|
136
|
+
- date: YYYY-MM-DD
|
|
137
|
+
- branch: <branch name>
|
|
138
|
+
- ledger: docs/risk-ledgers/<slug>.md
|
|
139
|
+
- mode: light | full
|
|
140
|
+
- lenses: lead-slug + supporting slugs (comma list)
|
|
141
|
+
- top-5 risks: one line per risk — <severity glyph> <one-sentence summary> (<lens slug>)
|
|
142
|
+
- disposition: fold | monitor | scope-cut per risk
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The ledger path is the canonical reference. The top-5 risks give a scan summary. Lenses pin the authoring luminaries.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
BRANCH=$(git branch --show-current)
|
|
149
|
+
BRANCH_SLUG=$(echo "$BRANCH" | tr '/' '-')
|
|
150
|
+
mkdir -p state/markers/pre-mortem
|
|
151
|
+
cat > "state/markers/pre-mortem/${BRANCH_SLUG}.marker" <<EOF
|
|
152
|
+
# Pre-mortem marker — <scope>
|
|
153
|
+
- date: $(date -u +%Y-%m-%d)
|
|
154
|
+
- branch: $BRANCH
|
|
155
|
+
- ledger: docs/risk-ledgers/<slug>.md
|
|
156
|
+
- mode: light
|
|
157
|
+
- lenses: <lead-slug>, <supporting-slug-1>, <supporting-slug-2>
|
|
158
|
+
- top-5 risks:
|
|
159
|
+
- 🔴 <risk one summary> (<lens-slug>)
|
|
160
|
+
- 🟡 <risk two summary> (<lens-slug>)
|
|
161
|
+
- 🟢 <risk three summary> (<lens-slug>)
|
|
162
|
+
- 🟡 <risk four summary> (<lens-slug>)
|
|
163
|
+
- 🔴 <risk five summary> (<lens-slug>)
|
|
164
|
+
- disposition: fold per risk (name mitigation inline)
|
|
165
|
+
EOF
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Guard the git-stage with a check-ignore test — adopter repos vary on whether `state/markers/` is tracked:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
if ! git check-ignore -q "state/markers/pre-mortem/${BRANCH_SLUG}.marker" 2>/dev/null; then
|
|
172
|
+
git add "state/markers/pre-mortem/${BRANCH_SLUG}.marker"
|
|
173
|
+
fi
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
<!-- /FKGL-EXEMPT -->
|
|
121
177
|
|
|
122
178
|
## Output
|
|
123
179
|
|
|
@@ -100,6 +100,21 @@ Option labels are typeable letters per memory `feedback_keyboard_typeable_option
|
|
|
100
100
|
|
|
101
101
|
**Step 0** — Lead with a `/value-prop tweet` claim (≤180 chars) plus a `/kiss words` grade-10 summary (2-3 sentences). This is what the operator reads first on mobile. Tables come after.
|
|
102
102
|
|
|
103
|
+
**Step 0.5 — First-run detection (per bassclef-upstream#2009; Sam-A).** Before any thread walk fires, check whether the repo is cold. Cold means:
|
|
104
|
+
|
|
105
|
+
- `docs/whereami.md` is missing OR has no `iteration_bet:` + no `next_bet:` field, AND
|
|
106
|
+
- `docs/canvases/` is empty or missing, AND
|
|
107
|
+
- `docs/roadmaps/` is empty or missing, AND
|
|
108
|
+
- `state/markers/temperance/` is empty or missing
|
|
109
|
+
|
|
110
|
+
When cold, jump straight to Step 7d. Skip Steps 1-6. The cold-repo output uses plain adopter vocabulary — no "parent-goal thread", no "orientation gates", no "roadmap reconcile", no "tier taxonomies". A cold adopter has no model for those words yet.
|
|
111
|
+
|
|
112
|
+
Routing hint for cold adopters points at the build path:
|
|
113
|
+
|
|
114
|
+
> New here? Try `/stage` for a clickable mock of your idea, `/launch` for a full variant gallery, or `/build` to ship against an existing spec. These let you see something real on your screen before learning the deeper surface area.
|
|
115
|
+
|
|
116
|
+
When warm (any of the four signals present), continue to Step 1.
|
|
117
|
+
|
|
103
118
|
**Step 1** — Orientation gates. Check `state/markers/orientation-gate/<branch-slug>.marker`. Check `state/markers/roadmap-reconcile-gate/<branch-slug>.marker`. When either is missing on a feature branch, refuse Step 7. Point at the BLOCKED block. On main this step is silent.
|
|
104
119
|
|
|
105
120
|
**Step 2** — Walk the parent chain before reading leaf state. Read the active goal's parent_drivers via `state_iteration_bet_active`. Walk up to parent_bet when present. Walk further until parent_goal is null. Read parent_roadmap when any goal in the chain has one. Fetch its `last_event` and `next_pending` via `state_roadmap_get`. Surface active goal plus parent goal plus parent roadmap plus combined parent_drivers.
|
|
@@ -177,11 +192,17 @@ Instead of refusing to render, `/sprint` reads three fallback sources:
|
|
|
177
192
|
|
|
178
193
|
From these sources, propose 3 candidate options in the same card + scan-table shape (Step 7c). Cite the source per option — for example, "From your open issues #NNN + recent commits on `<area>`". The value-prop per option comes from what the source itself says, not from a strategic doc.
|
|
179
194
|
|
|
180
|
-
End with
|
|
195
|
+
End with two plain-English hints. The first points the adopter at the build path — the fastest route from idea to something on screen:
|
|
196
|
+
|
|
197
|
+
> New here? Try `/stage` for a clickable mock, `/launch` for a full variant gallery, or `/build` to ship against an existing spec. These let you see something real on your screen before learning the deeper surface area.
|
|
198
|
+
|
|
199
|
+
The second hint points at ways to sharpen future runs:
|
|
200
|
+
|
|
201
|
+
> Want sharper option proposals from `/sprint` later? Author strategic docs first — `/lean-canvas` (business model), `/spec` (buildable spec), or `/user-stories` (backlog shape). These feed `/sprint` on future calls.
|
|
181
202
|
|
|
182
|
-
|
|
203
|
+
Both hints are opt-in. The adopter does NOT need to author strategic docs to get value from this call. This mirrors `/onboard-repo` Path B "greenfield-from-intent" — the skill produces useful output on its first fire, and hints at deeper flows without gating on them.
|
|
183
204
|
|
|
184
|
-
|
|
205
|
+
Note per Sam PDF cure (bassclef-upstream#2009): the first hint takes precedence over the second. Sam's bounce rule is that unfamiliar vocabulary at first mention causes exit. The build path uses adopter-vocab (`/stage`, `/launch`, `/build`); the strategic-docs path uses deeper vocab (`/lean-canvas`, `/user-stories`). First hint fires first.
|
|
185
206
|
|
|
186
207
|
Note per risk ledger R2: production fixture harness for this fallback path uses a brownfield fixture (no canvas, no whereami) so the fallback runs under test. Adding that fixture ships as a follow-on when the fallback surface gets its first Tier 0 test.
|
|
187
208
|
|
|
@@ -82,7 +82,9 @@ State these five lines after the checklist:
|
|
|
82
82
|
- **What you are NOT doing** — the temptations you resist.
|
|
83
83
|
- **Verification plan** — how you will know it works.
|
|
84
84
|
|
|
85
|
-
Then write the gate marker. BOTH the ephemeral flag AND the durable
|
|
85
|
+
Then write the gate marker. BOTH the ephemeral flag AND the durable marker. The pre-build-gate hook accepts either. The durable marker lets the 5-session compliance counter see mobile firings that would evaporate at sandbox teardown.
|
|
86
|
+
|
|
87
|
+
<!-- FKGL-EXEMPT: bash command block; shell commands are not graded prose. F6 cure per bassclef-upstream#2175 Batch B wraps git add in check-ignore guard. -->
|
|
86
88
|
|
|
87
89
|
```bash
|
|
88
90
|
BRANCH=$(git branch --show-current)
|
|
@@ -93,13 +95,22 @@ SESSION_ID=${CLAUDE_SESSION_ID:-$$}
|
|
|
93
95
|
# Ephemeral flag (existing pre-build-gate contract)
|
|
94
96
|
touch "/tmp/claude-temperance-done-${SESSION_ID}-${BRANCH_HASH}"
|
|
95
97
|
|
|
96
|
-
# Durable
|
|
98
|
+
# Durable marker (gitignore-aware per F6 cure)
|
|
97
99
|
mkdir -p state/markers/temperance
|
|
98
100
|
date -u +%Y-%m-%dT%H:%M:%SZ > "state/markers/temperance/${BRANCH_SLUG}.marker"
|
|
99
|
-
|
|
101
|
+
|
|
102
|
+
# F6 cure (bassclef-upstream#2175 Batch B) — skip git add when the path is
|
|
103
|
+
# ignored. Adopter repos vary on whether state/markers/ is tracked. The marker
|
|
104
|
+
# write stays unconditional (local file always). The stage runs only when the
|
|
105
|
+
# path is tracked.
|
|
106
|
+
if ! git check-ignore -q "state/markers/temperance/${BRANCH_SLUG}.marker" 2>/dev/null; then
|
|
107
|
+
git add "state/markers/temperance/${BRANCH_SLUG}.marker"
|
|
108
|
+
fi
|
|
100
109
|
```
|
|
101
110
|
|
|
102
|
-
|
|
111
|
+
<!-- /FKGL-EXEMPT -->
|
|
112
|
+
|
|
113
|
+
The durable marker always lands as a local file. The git stage runs only when the adopter repo tracks `state/markers/`. Per `.claude/rules/mobile-ephemeral-session.md` section 2 and `.claude/rules/session-artifacts.md` posture note.
|
|
103
114
|
|
|
104
115
|
Then proceed with the work.
|
|
105
116
|
|
package/dist/lite/README.md
CHANGED
|
@@ -13,6 +13,8 @@ description: A shared substrate for working with AI coding agents.
|
|
|
13
13
|
|
|
14
14
|
**A shared substrate for working with AI coding agents.** Install it once into your repo, and every Claude Code session starts loaded: skills you invoke, rules the agent reads, specialist agents you dispatch, practitioner profiles it designs against, hooks that catch mistakes, plus the standards and decision records that wire them together.
|
|
15
15
|
|
|
16
|
+
**What lands on disk.** Bassclef installs its working files under `.claude/`, plus one project-state document at `docs/whereami.md`, a config at `substrate.config.md`, and a `CLAUDE.md` that loads them. Most of these are bassclef's own files — you normally don't edit them. Your own code is untouched.
|
|
17
|
+
|
|
16
18
|
Run `/skills` or `/luminary` in any session to see exactly what your install carries. The counts differ by tier, so this README does not print them.
|
|
17
19
|
|
|
18
20
|
The agent reads your rules, knows the catalog, handles failure cleanly, and ships with discipline. You stop re-explaining your conventions every session — the difference between an assistant you brief and a band that already knows the set.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "v1.
|
|
3
|
-
"marker_tag": "release-2026-10-
|
|
4
|
-
"release_date": "2026-10-
|
|
5
|
-
"release_sha": "
|
|
6
|
-
"release_tag": "v1.
|
|
2
|
+
"version": "v1.11.1",
|
|
3
|
+
"marker_tag": "release-2026-10-18-3b0203d5",
|
|
4
|
+
"release_date": "2026-10-18T14:59:01Z",
|
|
5
|
+
"release_sha": "3b0203d5",
|
|
6
|
+
"release_tag": "v1.11.1"
|
|
7
7
|
}
|
|
@@ -112,6 +112,100 @@ _iwp_manifest_valid() {
|
|
|
112
112
|
return 0
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
# ---- Ajv-runtime schema validator (bassclef-upstream#2157 step 6) ----
|
|
116
|
+
#
|
|
117
|
+
# Wraps the install-written-paths manifest in ajv-based schema validation.
|
|
118
|
+
# Called from install_written_paths_verify (read-side gate) + from
|
|
119
|
+
# install_written_paths_register (post-write gate).
|
|
120
|
+
#
|
|
121
|
+
# Reads via is_install_written_path stay cheap (no ajv — hot path).
|
|
122
|
+
# Writes are rare, so post-write ajv cost is acceptable.
|
|
123
|
+
#
|
|
124
|
+
# Fail-soft on ajv missing (adopter machine without ajv-cli): returns 0.
|
|
125
|
+
# Mirrors the step 2 validator pattern at scripts/validate-cross-repo-contracts.sh:139-144.
|
|
126
|
+
# This keeps lite-tier adopters working when ajv is not installed; the step 3
|
|
127
|
+
# CI gate catches drift at every PR regardless of adopter ajv presence.
|
|
128
|
+
#
|
|
129
|
+
# @pattern patterns/code/ousterhout/deep-module.md — one wrapper, many callers
|
|
130
|
+
# @pattern patterns/code/vernon/anticorruption-layer.md — ajv at the write boundary
|
|
131
|
+
|
|
132
|
+
_iwp_schema_path() {
|
|
133
|
+
local root="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
134
|
+
echo "$root/standards/state-spine/schemas/install-written-paths.schema.json"
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# _iwp_ajv_validate <manifest-path>
|
|
138
|
+
# Returns 0 when ajv absent (fail-soft) OR when ajv confirms the manifest
|
|
139
|
+
# conforms to standards/state-spine/schemas/install-written-paths.schema.json.
|
|
140
|
+
# Returns 1 when ajv is present AND the manifest fails schema validation.
|
|
141
|
+
_iwp_ajv_validate() {
|
|
142
|
+
local manifest_file="${1:-}"
|
|
143
|
+
[ -n "$manifest_file" ] || return 1
|
|
144
|
+
[ -f "$manifest_file" ] || return 1
|
|
145
|
+
|
|
146
|
+
if ! command -v ajv >/dev/null 2>&1; then
|
|
147
|
+
_iwp_log "ajv-validate skip reason=ajv-missing manifest=$manifest_file"
|
|
148
|
+
return 0
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
local schema
|
|
152
|
+
schema=$(_iwp_schema_path)
|
|
153
|
+
if [ ! -f "$schema" ]; then
|
|
154
|
+
_iwp_log "ajv-validate skip reason=schema-missing path=$schema"
|
|
155
|
+
return 0
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
if ajv validate -s "$schema" -d "$manifest_file" --spec=draft2020 -c ajv-formats >/dev/null 2>&1; then
|
|
159
|
+
_iwp_log "ajv-validate pass manifest=$manifest_file"
|
|
160
|
+
return 0
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
_iwp_log "ajv-validate fail manifest=$manifest_file"
|
|
164
|
+
return 1
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# ---- CLI init.manifest.json fallback (bassclef-upstream#2138) ----
|
|
168
|
+
#
|
|
169
|
+
# bassclef-cli's `bassclef init` writes `.bassclef/init.manifest.json`
|
|
170
|
+
# with its own schema (per bassclef-cli/src/lib/manifest-types.ts):
|
|
171
|
+
# { schema_version: 2|3, entries: [{path, template, outcome, ...}] }
|
|
172
|
+
#
|
|
173
|
+
# When upstream's state/install-written-paths.json doesn't have an
|
|
174
|
+
# entry for a path, fall back to this CLI manifest. Treat outcome in
|
|
175
|
+
# {created, updated, unchanged} as install-written. Treat refused |
|
|
176
|
+
# error as NOT install-written (the write didn't actually land, so
|
|
177
|
+
# discipline should still fire).
|
|
178
|
+
#
|
|
179
|
+
# No hash verification against the CLI manifest. The CLI schema uses
|
|
180
|
+
# `content_hash_sha256` without the `sha256:` prefix; shape-mismatch
|
|
181
|
+
# is tolerated because the fallback is a presence + outcome check,
|
|
182
|
+
# not an integrity check. The upstream manifest keeps strict hash
|
|
183
|
+
# verification per Hoare.
|
|
184
|
+
_iwp_init_manifest_path() {
|
|
185
|
+
local root="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
186
|
+
echo "$root/.bassclef/init.manifest.json"
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
# Returns 0 when the path is present in the CLI init manifest with
|
|
190
|
+
# outcome in {created, updated, unchanged}. Returns 1 otherwise
|
|
191
|
+
# (missing file, malformed JSON, path absent, or outcome refused/error).
|
|
192
|
+
_iwp_init_manifest_has_path() {
|
|
193
|
+
local tgt="${1:-}"
|
|
194
|
+
[ -n "$tgt" ] || return 1
|
|
195
|
+
local init_manifest
|
|
196
|
+
init_manifest=$(_iwp_init_manifest_path)
|
|
197
|
+
[ -f "$init_manifest" ] || return 1
|
|
198
|
+
command -v jq >/dev/null 2>&1 || return 1
|
|
199
|
+
# Guard against malformed JSON: jq -e . exits non-zero on parse failure.
|
|
200
|
+
jq -e . "$init_manifest" >/dev/null 2>&1 || return 1
|
|
201
|
+
local outcome
|
|
202
|
+
outcome=$(jq -r --arg path "$tgt" '.entries[]? | select(.path == $path) | .outcome' "$init_manifest" 2>/dev/null)
|
|
203
|
+
case "$outcome" in
|
|
204
|
+
created|updated|unchanged) return 0 ;;
|
|
205
|
+
*) return 1 ;;
|
|
206
|
+
esac
|
|
207
|
+
}
|
|
208
|
+
|
|
115
209
|
# ---- Public API ----
|
|
116
210
|
|
|
117
211
|
# install_written_paths_register <path> <writer>
|
|
@@ -182,7 +276,19 @@ install_written_paths_register() {
|
|
|
182
276
|
fi
|
|
183
277
|
|
|
184
278
|
rm -f "$tmp" 2>/dev/null || true
|
|
185
|
-
|
|
279
|
+
|
|
280
|
+
# Post-write ajv check (step 6 — bassclef-upstream#2157). On fail, surface
|
|
281
|
+
# a warning but keep the write — fail-soft so adopters never lose data
|
|
282
|
+
# over schema drift. The step 2 validator + step 3 CI gate + step 5 release
|
|
283
|
+
# gate catch drift at later layers; this layer is advisory at write-time.
|
|
284
|
+
if ! _iwp_ajv_validate "$mpath"; then
|
|
285
|
+
echo "install-written-paths: WARNING — manifest at $mpath fails ajv schema validation after register" >&2
|
|
286
|
+
echo "install-written-paths: entry kept; run 'install_written_paths_verify' for details" >&2
|
|
287
|
+
_iwp_log "register path=$tgt writer=$writer post-write-ajv=fail"
|
|
288
|
+
else
|
|
289
|
+
_iwp_log "register path=$tgt writer=$writer post-write-ajv=pass"
|
|
290
|
+
fi
|
|
291
|
+
|
|
186
292
|
return 0
|
|
187
293
|
}
|
|
188
294
|
|
|
@@ -204,10 +310,17 @@ is_install_written_path() {
|
|
|
204
310
|
local mpath
|
|
205
311
|
mpath=$(_iwp_manifest_path)
|
|
206
312
|
|
|
207
|
-
# Fail-soft: manifest missing or malformed
|
|
313
|
+
# Fail-soft: upstream manifest missing or malformed. Before giving
|
|
314
|
+
# up, consult the CLI init manifest (bassclef-upstream#2138). Cold
|
|
315
|
+
# adopters that ran only `bassclef init` have no upstream manifest
|
|
316
|
+
# yet; the CLI manifest is their only install-written source.
|
|
208
317
|
if ! _iwp_manifest_valid; then
|
|
209
|
-
_iwp_log "check path=$tgt result=false reason=no-manifest-or-malformed"
|
|
210
318
|
[ -f "$mpath" ] && echo "install-written-paths: manifest malformed; treating as not-install-written" >&2
|
|
319
|
+
if _iwp_init_manifest_has_path "$tgt"; then
|
|
320
|
+
_iwp_log "check path=$tgt result=true reason=cli-init-manifest-fallback-no-upstream-manifest"
|
|
321
|
+
return 0
|
|
322
|
+
fi
|
|
323
|
+
_iwp_log "check path=$tgt result=false reason=no-manifest-or-malformed"
|
|
211
324
|
return 1
|
|
212
325
|
fi
|
|
213
326
|
|
|
@@ -220,6 +333,14 @@ is_install_written_path() {
|
|
|
220
333
|
local recorded_hash
|
|
221
334
|
recorded_hash=$(jq -r --arg path "$tgt" '.entries[] | select(.path == $path) | .content_hash' "$mpath" 2>/dev/null)
|
|
222
335
|
if [ -z "$recorded_hash" ] || [ "$recorded_hash" = "null" ]; then
|
|
336
|
+
# Fallback per bassclef-upstream#2138 — the CLI's `bassclef init`
|
|
337
|
+
# writes `.bassclef/init.manifest.json` with its own shape. Lite
|
|
338
|
+
# adopters hit the lead-lens 3-marker gate because the two manifests
|
|
339
|
+
# never agreed. Consult the CLI manifest before giving up.
|
|
340
|
+
if _iwp_init_manifest_has_path "$tgt"; then
|
|
341
|
+
_iwp_log "check path=$tgt result=true reason=cli-init-manifest-fallback"
|
|
342
|
+
return 0
|
|
343
|
+
fi
|
|
223
344
|
_iwp_log "check path=$tgt result=false reason=not-in-manifest"
|
|
224
345
|
return 1
|
|
225
346
|
fi
|
|
@@ -335,6 +456,125 @@ install_written_paths_rebuild() {
|
|
|
335
456
|
return 0
|
|
336
457
|
}
|
|
337
458
|
|
|
459
|
+
# install_written_paths_rebuild_from_lite_manifest <lite-manifest-path>
|
|
460
|
+
#
|
|
461
|
+
# Bulk-rebuild cure per bassclef-upstream#2200.
|
|
462
|
+
#
|
|
463
|
+
# Replaces the fragment's 451 serial `install_written_paths_register` loop
|
|
464
|
+
# with one atomic rebuild. Reads lite-manifest.json, computes SHA256 per
|
|
465
|
+
# path that exists on disk, writes the full entries array in one jq pass
|
|
466
|
+
# to a tmp file, runs ajv once, then atomically moves tmp over manifest.
|
|
467
|
+
#
|
|
468
|
+
# Deep module per @luminary john-ousterhout — one verb hides the whole
|
|
469
|
+
# rebuild path. Caller stays dumb. Register's per-path semantics stay
|
|
470
|
+
# untouched for ad-hoc callers (hook-connect, migrations) that need
|
|
471
|
+
# per-call ajv (PR #2174 intent preserved).
|
|
472
|
+
#
|
|
473
|
+
# Postcondition (per @luminary tony-hoare):
|
|
474
|
+
# On return 0: manifest at _iwp_manifest_path() carries one entry per
|
|
475
|
+
# lite-manifest.json path that passes _iwp_validate_path AND exists on
|
|
476
|
+
# disk; writer=bassclef-sync-template; content_hash=sha256:<hex>;
|
|
477
|
+
# written_at=current UTC timestamp. Schema-valid per ajv when ajv present.
|
|
478
|
+
# On return 1: lite-manifest missing OR malformed; existing manifest
|
|
479
|
+
# untouched (atomic mv never ran).
|
|
480
|
+
#
|
|
481
|
+
# Returns:
|
|
482
|
+
# 0 success (manifest written)
|
|
483
|
+
# 1 lite-manifest missing | malformed | jq missing
|
|
484
|
+
install_written_paths_rebuild_from_lite_manifest() {
|
|
485
|
+
local lite_manifest="${1:-}"
|
|
486
|
+
|
|
487
|
+
if [ -z "$lite_manifest" ] || [ ! -f "$lite_manifest" ]; then
|
|
488
|
+
_iwp_log "bulk-rebuild fail reason=lite-manifest-missing path=$lite_manifest"
|
|
489
|
+
return 1
|
|
490
|
+
fi
|
|
491
|
+
|
|
492
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
493
|
+
echo "install-written-paths: jq not available; cannot bulk-rebuild" >&2
|
|
494
|
+
return 1
|
|
495
|
+
fi
|
|
496
|
+
|
|
497
|
+
# Precondition: lite-manifest.json is valid JSON
|
|
498
|
+
if ! jq -e . "$lite_manifest" >/dev/null 2>&1; then
|
|
499
|
+
_iwp_log "bulk-rebuild fail reason=lite-manifest-malformed path=$lite_manifest"
|
|
500
|
+
return 1
|
|
501
|
+
fi
|
|
502
|
+
|
|
503
|
+
local mpath
|
|
504
|
+
mpath=$(_iwp_manifest_path)
|
|
505
|
+
mkdir -p "$(dirname "$mpath")"
|
|
506
|
+
|
|
507
|
+
local root="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
508
|
+
local now
|
|
509
|
+
now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
510
|
+
|
|
511
|
+
# Build entries array in a tmp jq-input file: one JSON object per line.
|
|
512
|
+
# Each line is a {path, writer, written_at, content_hash} record.
|
|
513
|
+
# Skip entries that fail path validation OR have no file on disk.
|
|
514
|
+
local jq_input
|
|
515
|
+
jq_input=$(mktemp)
|
|
516
|
+
# shellcheck disable=SC2064
|
|
517
|
+
trap "rm -f \"$jq_input\"" RETURN
|
|
518
|
+
|
|
519
|
+
local registered=0
|
|
520
|
+
local skipped=0
|
|
521
|
+
while IFS= read -r rel_path; do
|
|
522
|
+
[ -z "$rel_path" ] && continue
|
|
523
|
+
if ! _iwp_validate_path "$rel_path"; then
|
|
524
|
+
skipped=$((skipped + 1))
|
|
525
|
+
continue
|
|
526
|
+
fi
|
|
527
|
+
local full_path
|
|
528
|
+
case "$rel_path" in
|
|
529
|
+
/*) full_path="$rel_path" ;;
|
|
530
|
+
*) full_path="$root/$rel_path" ;;
|
|
531
|
+
esac
|
|
532
|
+
if [ ! -f "$full_path" ]; then
|
|
533
|
+
skipped=$((skipped + 1))
|
|
534
|
+
continue
|
|
535
|
+
fi
|
|
536
|
+
local hash
|
|
537
|
+
hash=$(_iwp_sha256 "$full_path")
|
|
538
|
+
if [ -z "$hash" ]; then
|
|
539
|
+
skipped=$((skipped + 1))
|
|
540
|
+
continue
|
|
541
|
+
fi
|
|
542
|
+
jq -cn --arg p "$rel_path" --arg w "bassclef-sync-template" --arg ts "$now" --arg h "$hash" \
|
|
543
|
+
'{path:$p, writer:$w, written_at:$ts, content_hash:$h}' >> "$jq_input"
|
|
544
|
+
registered=$((registered + 1))
|
|
545
|
+
done < <(jq -r '.entries[]?.path // empty' "$lite_manifest")
|
|
546
|
+
|
|
547
|
+
# Build the manifest from the per-line records using jq --slurp.
|
|
548
|
+
# Tmp filename MUST end in .json so ajv's auto-detect reads it as JSON
|
|
549
|
+
# instead of trying to require() it as a module (macOS mktemp without
|
|
550
|
+
# --suffix produces extensionless files that fail ajv's parse).
|
|
551
|
+
local tmp
|
|
552
|
+
tmp="$mpath.rebuild.$$.json"
|
|
553
|
+
if ! jq -s --arg sv "$_IWP_SCHEMA_VERSION" \
|
|
554
|
+
'{schema_version:$sv, entries:.}' "$jq_input" > "$tmp"; then
|
|
555
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
556
|
+
_iwp_log "bulk-rebuild fail reason=jq-slurp-failed registered=$registered"
|
|
557
|
+
return 1
|
|
558
|
+
fi
|
|
559
|
+
|
|
560
|
+
# Post-write ajv validate; on fail, keep old manifest (atomic safety per Nygard)
|
|
561
|
+
if ! _iwp_ajv_validate "$tmp"; then
|
|
562
|
+
echo "install-written-paths: WARNING — bulk-rebuild output fails ajv; keeping prior manifest" >&2
|
|
563
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
564
|
+
_iwp_log "bulk-rebuild fail reason=ajv-validate registered=$registered"
|
|
565
|
+
return 1
|
|
566
|
+
fi
|
|
567
|
+
|
|
568
|
+
if ! mv "$tmp" "$mpath"; then
|
|
569
|
+
rm -f "$tmp" 2>/dev/null || true
|
|
570
|
+
_iwp_log "bulk-rebuild fail reason=mv-failed registered=$registered"
|
|
571
|
+
return 1
|
|
572
|
+
fi
|
|
573
|
+
|
|
574
|
+
_iwp_log "bulk-rebuild ok registered=$registered skipped=$skipped mpath=$mpath"
|
|
575
|
+
return 0
|
|
576
|
+
}
|
|
577
|
+
|
|
338
578
|
# install_written_paths_verify
|
|
339
579
|
# Returns 0 on well-formed manifest, 1 on schema violation.
|
|
340
580
|
install_written_paths_verify() {
|
|
@@ -365,5 +605,12 @@ install_written_paths_verify() {
|
|
|
365
605
|
return 1
|
|
366
606
|
fi
|
|
367
607
|
|
|
608
|
+
# Ajv-runtime schema check (step 6 — bassclef-upstream#2157).
|
|
609
|
+
# Fail-soft on ajv missing (returns 0); strict reject on schema failure.
|
|
610
|
+
if ! _iwp_ajv_validate "$mpath"; then
|
|
611
|
+
echo "install-written-paths: manifest fails ajv schema validation" >&2
|
|
612
|
+
return 1
|
|
613
|
+
fi
|
|
614
|
+
|
|
368
615
|
return 0
|
|
369
616
|
}
|