@thebassclef/lite 1.7.0 → 1.9.0
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/hooks/bassclef-sync.sh +43 -6
- package/dist/lite/.claude/hooks/git-stash-u-guard.sh +84 -0
- package/dist/lite/.claude/hooks/session-reflection.d/01-adopter-message-channel.sh +49 -0
- package/dist/lite/.claude/hooks/session-reflection.d/80-hook-heartbeat-check.sh +47 -8
- package/dist/lite/.claude/hooks/session-reflection.d/tests/fresh-install-state-gate.test.sh +242 -0
- package/dist/lite/.claude/settings.json +5 -0
- package/dist/lite/.claude/skills/build/SKILL.md +57 -1
- package/dist/lite/.claude/skills/launch/SKILL.md +43 -1
- package/dist/lite/.claude/skills/riff/SKILL.md +55 -13
- package/dist/lite/bassclef-version.json +5 -5
- package/dist/lite/lib/capability-probe.sh +140 -0
- package/dist/lite/presence/install/bassclef-sync.dispatcher.template.sh +54 -0
- package/dist/lite/presence/install/bassclef-sync.template.sh +51 -0
- package/dist/lite/standards/bassclef-wiring-manifest.json +6 -0
- package/dist/lite/standards/lite-manifest.json +37 -13
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
tier: lite
|
|
3
3
|
name: launch
|
|
4
|
-
description: "Compare variants of an idea.
|
|
4
|
+
description: "Compare variants of an idea. Get a mock gallery. Pick a direction. Get a spec, user stories, and a plan to build. Two sizes \u2014 medium and full. Three modes \u2014 full, local, scratch."
|
|
5
5
|
problem: "An idea is a doc. A working feature is code. The gap in between is where projects stall."
|
|
6
6
|
value: "Turns an idea into a buildable plan \u2014 mock gallery and spec and INVEST stories. Ready for /build."
|
|
7
7
|
inputs: [A rough idea or ticket or canvas]
|
|
@@ -9,6 +9,7 @@ outputs: [Clickable mock gallery, Buildable spec, INVEST user stories, GRASP res
|
|
|
9
9
|
user_invocable: true
|
|
10
10
|
disable_model_invocation: false
|
|
11
11
|
sizes: [medium, full]
|
|
12
|
+
modes: [full, local, scratch]
|
|
12
13
|
composes_baseline: anthropic-frontend-design
|
|
13
14
|
---
|
|
14
15
|
|
|
@@ -97,8 +98,49 @@ Per `.claude/rules/adr-discipline.md`. Mechanism: `adr-discipline-check.sh` fire
|
|
|
97
98
|
|
|
98
99
|
## Procedure
|
|
99
100
|
|
|
101
|
+
### Phase −1a — Capability probe (per bassclef-upstream#1909)
|
|
102
|
+
|
|
103
|
+
Source `lib/capability-probe.sh` and pick a mode. Three modes: **full**, **local**, **scratch**. Modes are orthogonal to the existing `--brownfield`, `--reuse-artifact`, and `--local` deploy flags (RFC H3 folded per `docs/rfcs/2026-09-23-graceful-degrade-adversarial-review.md`).
|
|
104
|
+
|
|
105
|
+
- **full** — git repo + gh auth + Playwright MCP + network. Full chain runs.
|
|
106
|
+
- **local** — git repo present. No PR, no screenshots. Composer-preflight skips the deploy-host check.
|
|
107
|
+
- **scratch** — no git repo. Writes to a tmp dir. Composer-preflight skips entirely; only Phase 1 (interpret-input) and Phase 4 (riff-prototypes) run.
|
|
108
|
+
|
|
109
|
+
The mode picker short-circuits `probe_network` when other probes already downgrade the mode below `full` (RFC C2 folded).
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
source "${BASSCLEF_DIR:-$REPO_ROOT}/lib/capability-probe.sh"
|
|
113
|
+
|
|
114
|
+
probe_git; GIT=$?
|
|
115
|
+
probe_mcp_playwright; MCP=$?
|
|
116
|
+
probe_gh_auth; GH=$?
|
|
117
|
+
|
|
118
|
+
if [ $GIT -eq 0 ] && [ $MCP -eq 0 ] && [ $GH -eq 0 ]; then
|
|
119
|
+
probe_network; NET=$?
|
|
120
|
+
else
|
|
121
|
+
NET=1
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
G=$([ $GIT -eq 0 ] && echo 1 || echo 0)
|
|
125
|
+
M=$([ $MCP -eq 0 ] && echo 1 || echo 0)
|
|
126
|
+
H=$([ $GH -eq 0 ] && echo 1 || echo 0)
|
|
127
|
+
N=$([ $NET -eq 0 ] && echo 1 || echo 0)
|
|
128
|
+
|
|
129
|
+
MODE="${OVERRIDE_MODE:-$(resolve_mode "$G" "$H" "$M" "$N")}"
|
|
130
|
+
validate_mode "$MODE" "$G" "$H" "$M" "$N" || exit 1
|
|
131
|
+
announce_mode "$MODE" "/launch"
|
|
132
|
+
|
|
133
|
+
if [ "$MODE" = "scratch" ]; then
|
|
134
|
+
echo "launch: scratch mode — Phase −1 (composer prerequisites) skipped; no git repo, no deploy chain" >&2
|
|
135
|
+
fi
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Operator override reads `--mode=<name>` from argv. The parser validates the requested mode against the environment before dispatch. Requested `full` without gh auth exits with a plain-language error.
|
|
139
|
+
|
|
100
140
|
### Phase -1 — Pre-flight (per bassclef#408 + bassclef#449/#450)
|
|
101
141
|
|
|
142
|
+
Runs only when Phase −1a picked `full` or `local`. Skipped entirely in `scratch` mode.
|
|
143
|
+
|
|
102
144
|
Source the shared pre-flight helper and surface all 6 prerequisites
|
|
103
145
|
upfront. Pass `--no-launch` if the dispatch is variants-only (no
|
|
104
146
|
`/launch-preview` chain expected); the deploy-host check (Check 6)
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: riff
|
|
3
|
-
description: "Produce 2
|
|
4
|
-
problem: "Design
|
|
5
|
-
value: "Two or three
|
|
3
|
+
description: "Produce 2 or 3 clickable mock variants. Each pins a luminary lens. Three modes — full, local, scratch. Full uses git, gh, and screenshots. Local writes to disk only. Scratch uses a tmp dir with no git."
|
|
4
|
+
problem: "Design work gets one variant. No comparison. The operator picks blind. Rework is heavy."
|
|
5
|
+
value: "Two or three mocks per surface. Each mock pins a luminary lens. Cold adopters see a result in five minutes."
|
|
6
6
|
inputs: [A UI surface intent or design goal]
|
|
7
7
|
outputs: [2-3 clickable mock variants, Luminary lens per variant]
|
|
8
8
|
tier: lite
|
|
9
9
|
user_invocable: true
|
|
10
10
|
disable_model_invocation: false
|
|
11
|
+
modes: [full, local, scratch]
|
|
11
12
|
composes_with: [interpret-input, luminary, visual-review]
|
|
12
13
|
---
|
|
13
14
|
|
|
@@ -70,15 +71,55 @@ Standard adopters can bypass `/riff`. They invoke `/stage` directly for the full
|
|
|
70
71
|
|
|
71
72
|
Per canvas `docs/canvases/2026-08-02-porcelain-plumbing-reframe.md` — 10 of 10 luminaries agreed on the reframe.
|
|
72
73
|
|
|
74
|
+
## Modes (per bassclef-upstream#1909)
|
|
75
|
+
|
|
76
|
+
Three modes let the skill run in any environment:
|
|
77
|
+
|
|
78
|
+
- **full** — git repo + gh auth + Playwright MCP + network. Ships PR + screenshots.
|
|
79
|
+
- **local** — git repo present. No PR. No screenshots. Artifacts land on disk.
|
|
80
|
+
- **scratch** — no git. Writes to a tmp dir. No PR. No screenshots. Fastest path for cold adopters.
|
|
81
|
+
|
|
82
|
+
Cold adopters typing `/riff` on a fresh Saturday hit **local** or **scratch** by default. They see a viewable result. No BLOCK on missing MCP.
|
|
83
|
+
|
|
84
|
+
The mode picker sits at Phase −1. It reads four probes from `lib/capability-probe.sh`. It picks the highest-fidelity mode the environment supports. Operator can override with `--mode=<name>`. The picker validates the requested mode's requirements. If a required capability is missing, the parser exits with a plain-language error.
|
|
85
|
+
|
|
73
86
|
## Procedure
|
|
74
87
|
|
|
88
|
+
0. **Phase −1 — pick the mode.** Source the probe lib. Read four capability probes. Resolve a mode. Announce it. This step short-circuits `probe_network` when the other three probes already downgrade the mode below `full` (RFC C2 folded).
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
source "${BASSCLEF_DIR:-$REPO_ROOT}/lib/capability-probe.sh"
|
|
92
|
+
|
|
93
|
+
probe_git; GIT=$?
|
|
94
|
+
probe_mcp_playwright; MCP=$?
|
|
95
|
+
probe_gh_auth; GH=$?
|
|
96
|
+
|
|
97
|
+
# Short-circuit: only probe network when other capabilities support full mode
|
|
98
|
+
if [ $GIT -eq 0 ] && [ $MCP -eq 0 ] && [ $GH -eq 0 ]; then
|
|
99
|
+
probe_network; NET=$?
|
|
100
|
+
else
|
|
101
|
+
NET=1
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# Convert exit codes to booleans (0 = present)
|
|
105
|
+
G=$([ $GIT -eq 0 ] && echo 1 || echo 0)
|
|
106
|
+
M=$([ $MCP -eq 0 ] && echo 1 || echo 0)
|
|
107
|
+
H=$([ $GH -eq 0 ] && echo 1 || echo 0)
|
|
108
|
+
N=$([ $NET -eq 0 ] && echo 1 || echo 0)
|
|
109
|
+
|
|
110
|
+
# Operator override reads --mode=<name>; parser validates before dispatch
|
|
111
|
+
MODE="${OVERRIDE_MODE:-$(resolve_mode "$G" "$H" "$M" "$N")}"
|
|
112
|
+
validate_mode "$MODE" "$G" "$H" "$M" "$N" || exit 1
|
|
113
|
+
announce_mode "$MODE" "/riff"
|
|
114
|
+
```
|
|
115
|
+
|
|
75
116
|
1. Parse the operator's input via `/interpret-input`. Output: normalized InputArtifact.
|
|
76
117
|
2. Pick 2-3 luminary lenses. Either operator-named or via `/luminary` picker.
|
|
77
|
-
3. For each lens: author a static HTML mock. Path: `docs/prototypes/YYYY-MM-DD-riff-<slug>-variant-N/index.html`. Stubbed data. Include a `<h2>` naming the lens.
|
|
78
|
-
4.
|
|
79
|
-
5. Assemble a gallery page. Link every variant. Show screenshots inline. Show luminary tags.
|
|
80
|
-
6. Run `/visual-review`
|
|
81
|
-
7. Present the gallery URL to the operator.
|
|
118
|
+
3. For each lens: author a static HTML mock. Path in `full` and `local` modes: `docs/prototypes/YYYY-MM-DD-riff-<slug>-variant-N/index.html`. Path in `scratch` mode: `${TMPDIR:-/tmp}/riff-<slug>-variant-N/index.html`. Stubbed data. Include a `<h2>` naming the lens.
|
|
119
|
+
4. **Screenshots — mode-gated.** In `full` mode, screenshot each variant via Playwright MCP and save under `.playwright-mcp/`. In `local` and `scratch` modes, skip this step and note in the gallery output that screenshots are absent because the environment did not have Playwright MCP.
|
|
120
|
+
5. Assemble a gallery page. Link every variant. Show screenshots inline when present. Show luminary tags in every mode.
|
|
121
|
+
6. Run `/visual-review` in `full` mode. In `local` and `scratch` modes, run the self-review checklist against each variant instead. Report findings per variant.
|
|
122
|
+
7. Present the gallery URL to the operator. In `full` mode, open a PR. In `local` mode, print the disk path. In `scratch` mode, print the tmp path and a note that the gallery does not survive reboot. Picked variant flows into `/build` or `/launch` at standard.
|
|
82
123
|
|
|
83
124
|
## Baseline composition
|
|
84
125
|
|
|
@@ -93,11 +134,12 @@ Per canvas `docs/canvases/2026-08-02-porcelain-plumbing-reframe.md` — 10 of 10
|
|
|
93
134
|
|
|
94
135
|
## Rules
|
|
95
136
|
|
|
96
|
-
- MUST produce a PR. Push to a feature branch, not main.
|
|
97
|
-
- MUST run on a feature branch.
|
|
98
|
-
- MUST screenshot every variant via Playwright MCP.
|
|
99
|
-
- MUST tag each variant with its luminary lens in the HTML.
|
|
100
|
-
- MUST report per-variant `/visual-review` findings
|
|
137
|
+
- MUST produce a PR when running in `full` mode. Push to a feature branch, not main.
|
|
138
|
+
- MUST run on a feature branch when git is present. Scratch mode has no branch.
|
|
139
|
+
- MUST screenshot every variant via Playwright MCP when running in `full` mode. In `local` and `scratch` modes, skip screenshots and note the reason in the gallery output. (Per bassclef-upstream#1909 — the prior BLOCK broke cold adopters.)
|
|
140
|
+
- MUST tag each variant with its luminary lens in the HTML in every mode.
|
|
141
|
+
- MUST report per-variant `/visual-review` findings in `full` mode. In `local` and `scratch` modes, run the self-review checklist and report findings the same way.
|
|
142
|
+
- MUST print the mode announcement line at Phase −1. STDOUT stays untouched; the line prints to STDERR so parsers stay clean (RFC R1 folded per `docs/risk-ledgers/2026-09-23-graceful-degrade.md`).
|
|
101
143
|
|
|
102
144
|
## Refs
|
|
103
145
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "v1.
|
|
3
|
-
"marker_tag": "release-2026-09-
|
|
4
|
-
"release_date": "2026-09-
|
|
5
|
-
"release_sha": "
|
|
6
|
-
"release_tag": "release-2026-09-
|
|
2
|
+
"version": "v1.4.0",
|
|
3
|
+
"marker_tag": "release-2026-09-24-f36b8f31",
|
|
4
|
+
"release_date": "2026-09-24T01:12:22Z",
|
|
5
|
+
"release_sha": "f36b8f31",
|
|
6
|
+
"release_tag": "release-2026-09-24-f36b8f31"
|
|
7
7
|
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# lib/capability-probe.sh — capability probe deep module.
|
|
4
|
+
#
|
|
5
|
+
# Per @luminary john-ousterhout deep module: hides several probe strategies
|
|
6
|
+
# behind a narrow interface. Downstream SKILLs source this lib and ask what
|
|
7
|
+
# mode fits the environment. Probe strategies can change without touching
|
|
8
|
+
# any SKILL.
|
|
9
|
+
#
|
|
10
|
+
# Exposed functions:
|
|
11
|
+
# probe_git() — 0 inside git repo, 1 otherwise
|
|
12
|
+
# probe_gh_auth() — 0 when authenticated to GitHub, 1 otherwise
|
|
13
|
+
# probe_mcp_playwright() — 0 when Playwright MCP loaded, 1 otherwise
|
|
14
|
+
# probe_network() — 0 when network reachable, 1 otherwise (or OFFLINE=1)
|
|
15
|
+
# resolve_mode g gh m n — echoes "full" | "local" | "scratch"
|
|
16
|
+
# validate_mode m g gh m n — 0 if mode requirements met, 1 otherwise
|
|
17
|
+
# announce_mode m skill — prints "<skill>: running in <m> mode — <reason>" to stderr
|
|
18
|
+
#
|
|
19
|
+
# Per bassclef-upstream#1909 + spec docs/specs/2026-09-23-graceful-degrade-skills.md.
|
|
20
|
+
# Every probe fail-soft. Never throws.
|
|
21
|
+
|
|
22
|
+
# --- Individual probes -------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
probe_git() {
|
|
25
|
+
# Short-circuit if git binary absent
|
|
26
|
+
command -v git >/dev/null 2>&1 || return 1
|
|
27
|
+
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
28
|
+
return 0
|
|
29
|
+
fi
|
|
30
|
+
return 1
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
probe_gh_auth() {
|
|
34
|
+
# Fast path: token env vars indicate auth intent + presence.
|
|
35
|
+
if [ -n "${GH_TOKEN:-}" ] || [ -n "${GITHUB_TOKEN:-}" ]; then
|
|
36
|
+
return 0
|
|
37
|
+
fi
|
|
38
|
+
# Slow path: gh CLI must be present + authenticated.
|
|
39
|
+
command -v gh >/dev/null 2>&1 || return 1
|
|
40
|
+
if gh auth status >/dev/null 2>&1; then
|
|
41
|
+
return 0
|
|
42
|
+
fi
|
|
43
|
+
return 1
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
probe_mcp_playwright() {
|
|
47
|
+
# V1 heuristic — env var contract. V2 may read tool inventory.
|
|
48
|
+
[ -n "${CLAUDE_MCP_PLAYWRIGHT:-}" ] && return 0
|
|
49
|
+
return 1
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
probe_network() {
|
|
53
|
+
# Short-circuit: OFFLINE=1 skips the wire call entirely.
|
|
54
|
+
[ -n "${OFFLINE:-}" ] && return 1
|
|
55
|
+
command -v curl >/dev/null 2>&1 || return 1
|
|
56
|
+
if curl -sSf --max-time 2 "${BASSCLEF_NETWORK_PROBE_URL:-https://api.github.com/zen}" >/dev/null 2>&1; then
|
|
57
|
+
return 0
|
|
58
|
+
fi
|
|
59
|
+
return 1
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# --- Mode resolution ---------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
# resolve_mode <git> <gh> <mcp> <network>
|
|
65
|
+
# Each arg is "0" or "1" per the probe result convention (1 = capability present).
|
|
66
|
+
# Prints one of: "full" | "local" | "scratch".
|
|
67
|
+
resolve_mode() {
|
|
68
|
+
local git="${1:-0}" gh="${2:-0}" mcp="${3:-0}" net="${4:-0}"
|
|
69
|
+
if [ "$git" = "1" ] && [ "$gh" = "1" ] && [ "$mcp" = "1" ] && [ "$net" = "1" ]; then
|
|
70
|
+
echo "full"
|
|
71
|
+
elif [ "$git" = "1" ]; then
|
|
72
|
+
echo "local"
|
|
73
|
+
else
|
|
74
|
+
echo "scratch"
|
|
75
|
+
fi
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# validate_mode <mode> <git> <gh> <mcp> <net>
|
|
79
|
+
# Returns 0 when the mode's requirements are met, 1 otherwise.
|
|
80
|
+
# Stderr names the missing capability on failure.
|
|
81
|
+
validate_mode() {
|
|
82
|
+
local mode="${1:-}" git="${2:-0}" gh="${3:-0}" mcp="${4:-0}" net="${5:-0}"
|
|
83
|
+
case "$mode" in
|
|
84
|
+
full)
|
|
85
|
+
[ "$git" = "1" ] || { echo "validate_mode: full requires git (not present)" >&2; return 1; }
|
|
86
|
+
[ "$gh" = "1" ] || { echo "validate_mode: full requires gh auth (not present)" >&2; return 1; }
|
|
87
|
+
[ "$mcp" = "1" ] || { echo "validate_mode: full requires MCP (CLAUDE_MCP_PLAYWRIGHT not set)" >&2; return 1; }
|
|
88
|
+
[ "$net" = "1" ] || { echo "validate_mode: full requires network (probe failed)" >&2; return 1; }
|
|
89
|
+
return 0
|
|
90
|
+
;;
|
|
91
|
+
local)
|
|
92
|
+
[ "$git" = "1" ] || { echo "validate_mode: local requires git (not present)" >&2; return 1; }
|
|
93
|
+
return 0
|
|
94
|
+
;;
|
|
95
|
+
scratch)
|
|
96
|
+
return 0
|
|
97
|
+
;;
|
|
98
|
+
*)
|
|
99
|
+
echo "validate_mode: '$mode' is not a valid mode (expected: full | local | scratch)" >&2
|
|
100
|
+
return 1
|
|
101
|
+
;;
|
|
102
|
+
esac
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# --- Announcement ------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
# announce_mode <mode> <skill-name>
|
|
108
|
+
# Prints one line to stderr naming the picked mode and the reason.
|
|
109
|
+
# Honors SKIP_MODE_ANNOUNCE=1 to suppress the line (useful for scripted runs).
|
|
110
|
+
# STDOUT is untouched — parsers relying on STDOUT stay clean (R1 folded).
|
|
111
|
+
announce_mode() {
|
|
112
|
+
[ -n "${SKIP_MODE_ANNOUNCE:-}" ] && return 0
|
|
113
|
+
local mode="${1:-}" skill="${2:-}"
|
|
114
|
+
local reason
|
|
115
|
+
case "$mode" in
|
|
116
|
+
full) reason="all capabilities present" ;;
|
|
117
|
+
local) reason="one or more of gh, MCP, or network missing; disk-only" ;;
|
|
118
|
+
scratch) reason="no git repo detected; using scratch dir" ;;
|
|
119
|
+
*) reason="unknown mode" ;;
|
|
120
|
+
esac
|
|
121
|
+
echo "${skill}: running in \`${mode}\` mode — ${reason}. Say \`--mode=<other>\` to override." >&2
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# --- Lib self-check on source ------------------------------------------------
|
|
125
|
+
# Per R13 folded (Nygard stability): a broken lib should not silently return
|
|
126
|
+
# FALSE for everything. Emit a WARN if any required function is missing.
|
|
127
|
+
__bassclef_capability_probe_self_check() {
|
|
128
|
+
local missing=""
|
|
129
|
+
local f
|
|
130
|
+
for f in probe_git probe_gh_auth probe_mcp_playwright probe_network resolve_mode validate_mode announce_mode; do
|
|
131
|
+
type -t "$f" >/dev/null 2>&1 || missing="$missing $f"
|
|
132
|
+
done
|
|
133
|
+
if [ -n "$missing" ]; then
|
|
134
|
+
echo "WARN: lib/capability-probe.sh is missing functions:$missing" >&2
|
|
135
|
+
return 1
|
|
136
|
+
fi
|
|
137
|
+
return 0
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
__bassclef_capability_probe_self_check || true
|
|
@@ -126,6 +126,45 @@ bassclef_sync_resolve_pointer_file() {
|
|
|
126
126
|
fi
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
# bassclef_sync_bundled_substrate_present <cwd>
|
|
130
|
+
#
|
|
131
|
+
# Returns 0 (yes) when .claude/hooks/, .claude/rules/, .claude/skills/ all
|
|
132
|
+
# exist AND contain at least one file (recursively). Returns 1 (no) when
|
|
133
|
+
# any of those conditions fails.
|
|
134
|
+
#
|
|
135
|
+
# Cures the npm-only cold adopter false-error class per
|
|
136
|
+
# bassclef-upstream#1849. The dispatcher was designed for peer-checkout
|
|
137
|
+
# OR source-config models. Neither works for anonymous adopters against
|
|
138
|
+
# private repos. But the npm bundle already unpacks substrate at install
|
|
139
|
+
# time. The fetch failure is not a real failure; this check catches the
|
|
140
|
+
# steady state and lets the dispatcher exit silently.
|
|
141
|
+
#
|
|
142
|
+
# Composes with:
|
|
143
|
+
# @luminary alan-cooper — Sam persona; silent-on-success matches user model
|
|
144
|
+
# @luminary donald-norman — signifier; false-positive banner erodes real signal
|
|
145
|
+
# @luminary linus-torvalds — adopter contract; npm bundle IS the substrate
|
|
146
|
+
# @luminary michael-feathers — characterization tests over 3 install shapes
|
|
147
|
+
#
|
|
148
|
+
# Tests: .claude/hooks/tests/bassclef-sync-bundled-substrate-check.test.sh
|
|
149
|
+
# Refs: bassclef-upstream#1849
|
|
150
|
+
bassclef_sync_bundled_substrate_present() {
|
|
151
|
+
local cwd="${1:-}"
|
|
152
|
+
if [ -z "$cwd" ] || [ ! -d "$cwd" ]; then
|
|
153
|
+
return 1
|
|
154
|
+
fi
|
|
155
|
+
local _dir
|
|
156
|
+
for _dir in .claude/hooks .claude/rules .claude/skills; do
|
|
157
|
+
if [ ! -d "$cwd/$_dir" ]; then
|
|
158
|
+
return 1
|
|
159
|
+
fi
|
|
160
|
+
# Nonempty check: at least one file anywhere under the dir.
|
|
161
|
+
if [ -z "$(find "$cwd/$_dir" -mindepth 1 -maxdepth 4 -type f -print -quit 2>/dev/null)" ]; then
|
|
162
|
+
return 1
|
|
163
|
+
fi
|
|
164
|
+
done
|
|
165
|
+
return 0
|
|
166
|
+
}
|
|
167
|
+
|
|
129
168
|
bassclef_sync_self_heal_pointer() {
|
|
130
169
|
local cwd="${1:-$CWD}"
|
|
131
170
|
if [ "${SKIP_SELF_HEAL_POINTER:-0}" = "1" ]; then
|
|
@@ -713,6 +752,21 @@ if [ -f "$SIBLING_TEMPLATE" ]; then
|
|
|
713
752
|
exit $?
|
|
714
753
|
fi
|
|
715
754
|
|
|
755
|
+
# === Path 1.5. Bundled-substrate recognition (per #1849) ===
|
|
756
|
+
# npm-only cold adopters carry substrate in the package bundle. The
|
|
757
|
+
# dispatcher was designed for peer-checkout OR source-config models; both
|
|
758
|
+
# fail for anonymous adopters against private repos. Before firing the
|
|
759
|
+
# fetch-strategies path, check whether the substrate is already loaded at
|
|
760
|
+
# CWD. If yes, exit silently — no news is good news (Cooper).
|
|
761
|
+
#
|
|
762
|
+
# Override: BASSCLEF_DISPATCHER_STRICT=1 forces the Path 2 fetch even when
|
|
763
|
+
# bundled substrate is present (operator debugging).
|
|
764
|
+
if [ "${BASSCLEF_DISPATCHER_STRICT:-0}" != "1" ]; then
|
|
765
|
+
if bassclef_sync_bundled_substrate_present "$CWD"; then
|
|
766
|
+
exit 0
|
|
767
|
+
fi
|
|
768
|
+
fi
|
|
769
|
+
|
|
716
770
|
# === Path 2. Curl fallback ===
|
|
717
771
|
# Uses the same resolver as auto_sync pre-check so old-name adopters
|
|
718
772
|
# still get here (self-heal already fired above, but resolve for safety
|
|
@@ -256,6 +256,42 @@ bassclef_sync_parse_whereami_ts() {
|
|
|
256
256
|
return 0
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
# === #1929 — postcondition emitter (RESTORED/DEGRADED + JSON) ===
|
|
260
|
+
# Mirrors .claude/hooks/bassclef-sync.sh L215-243 (idempotent-complete-or-fail-loud
|
|
261
|
+
# pattern shipped under #1894 Cure 1 of #1691). Reads globals INSTALLED_COUNT,
|
|
262
|
+
# FAILED_COUNT, SKIPPED_INVALID, SKIPPED_TOTAL, missing_paths[], missing_scopes[]
|
|
263
|
+
# set by the install loop below. Emits exactly one human line + one JSON status
|
|
264
|
+
# line. Defined ABOVE the BASSCLEF_SYNC_TEST_MODE guard so tests can source and
|
|
265
|
+
# invoke directly.
|
|
266
|
+
#
|
|
267
|
+
# Anchor luminaries — @luminary linus-torvalds (adopter contract), @luminary
|
|
268
|
+
# michael-nygard (fail-loud stability), @luminary jerome-saltzer-and-michael-schroeder
|
|
269
|
+
# (complete mediation across both callers).
|
|
270
|
+
_template_emit_install_postcondition() {
|
|
271
|
+
local installed="${INSTALLED_COUNT:-0}"
|
|
272
|
+
local failed="${FAILED_COUNT:-0}"
|
|
273
|
+
local skipped_invalid="${SKIPPED_INVALID:-0}"
|
|
274
|
+
local skipped_total="${SKIPPED_TOTAL:-0}"
|
|
275
|
+
local total=$((installed + skipped_total + failed))
|
|
276
|
+
local defect_total=$((failed + skipped_invalid))
|
|
277
|
+
|
|
278
|
+
if [ "$defect_total" = "0" ]; then
|
|
279
|
+
# RESTORED — human line + minimal JSON status:complete
|
|
280
|
+
echo "bassclef-sync: RESTORED — $installed of $total hooks resolved at their scoped targets."
|
|
281
|
+
printf 'bassclef-sync: {"status":"complete","installed":%d}\n' "$installed"
|
|
282
|
+
else
|
|
283
|
+
# DEGRADED — human line + JSON with missing[] carrying path + scope + cure per entry
|
|
284
|
+
echo "bassclef-sync: DEGRADED — $defect_total of $total hooks did not resolve after self-heal."
|
|
285
|
+
local i missing_json="["
|
|
286
|
+
for i in "${!missing_paths[@]}"; do
|
|
287
|
+
[ "$i" -gt 0 ] && missing_json="$missing_json,"
|
|
288
|
+
missing_json="$missing_json{\"path\":\"${missing_paths[$i]}\",\"scope\":\"${missing_scopes[$i]}\",\"cure\":\"bassclef init --force\"}"
|
|
289
|
+
done
|
|
290
|
+
missing_json="$missing_json]"
|
|
291
|
+
printf 'bassclef-sync: {"status":"degraded","installed":%d,"missing":%s}\n' "$installed" "$missing_json"
|
|
292
|
+
fi
|
|
293
|
+
}
|
|
294
|
+
|
|
259
295
|
# === Test-mode early return (#897 Cure A) ===
|
|
260
296
|
# Tests source this template to access helper functions (see
|
|
261
297
|
# .claude/hooks/tests/bassclef-sync-*.test.sh). Early return under
|
|
@@ -898,6 +934,9 @@ SKIPPED_NO_HEADER=0
|
|
|
898
934
|
SKIPPED_INVALID=0
|
|
899
935
|
SKIPPED_LIB_MISSING=0
|
|
900
936
|
FAILED_COUNT=0
|
|
937
|
+
# #1929 — missing[] capture for RESTORED/DEGRADED postcondition
|
|
938
|
+
missing_paths=()
|
|
939
|
+
missing_scopes=()
|
|
901
940
|
|
|
902
941
|
mkdir -p "$HOME/.claude/hooks" 2>/dev/null || true
|
|
903
942
|
|
|
@@ -933,6 +972,8 @@ if [ -d "$BASSCLEF_DIR/.claude/hooks" ]; then
|
|
|
933
972
|
if [ -n "$_WALKER_MISS" ]; then
|
|
934
973
|
echo "$(date -u +%FT%TZ) install-fail-missing-dep $HOOK_NAME" >> "$BASSCLEF_TRACE_LOG"
|
|
935
974
|
FAILED_COUNT=$((FAILED_COUNT + 1))
|
|
975
|
+
missing_paths+=(".claude/hooks/$HOOK_NAME")
|
|
976
|
+
missing_scopes+=("operator")
|
|
936
977
|
continue
|
|
937
978
|
fi
|
|
938
979
|
|
|
@@ -947,6 +988,8 @@ if [ -d "$BASSCLEF_DIR/.claude/hooks" ]; then
|
|
|
947
988
|
chmod +x "$TARGET_USER" 2>/dev/null || true
|
|
948
989
|
echo "$(date -u +%FT%TZ) dispatch-fail $HOOK_NAME" >> "$BASSCLEF_TRACE_LOG"
|
|
949
990
|
FAILED_COUNT=$((FAILED_COUNT + 1))
|
|
991
|
+
missing_paths+=(".claude/hooks/$HOOK_NAME")
|
|
992
|
+
missing_scopes+=("operator")
|
|
950
993
|
fi
|
|
951
994
|
elif [ "$HAS_HEADER" = "1" ] && [ "$DISPATCH_AVAILABLE" = "0" ]; then
|
|
952
995
|
# S-F1 MED — dispatch lib missing; trace explicitly (not silent)
|
|
@@ -965,6 +1008,8 @@ if [ -d "$BASSCLEF_DIR/.claude/hooks" ]; then
|
|
|
965
1008
|
else
|
|
966
1009
|
echo "$(date -u +%FT%TZ) skipped-invalid-header $HOOK_NAME" >> "$BASSCLEF_TRACE_LOG"
|
|
967
1010
|
SKIPPED_INVALID=$((SKIPPED_INVALID + 1))
|
|
1011
|
+
missing_paths+=(".claude/hooks/$HOOK_NAME")
|
|
1012
|
+
missing_scopes+=("operator")
|
|
968
1013
|
fi
|
|
969
1014
|
fi
|
|
970
1015
|
done
|
|
@@ -1005,6 +1050,12 @@ fi
|
|
|
1005
1050
|
|
|
1006
1051
|
# N-F1 MED — install summary with reason breakdown when non-zero
|
|
1007
1052
|
SKIPPED_TOTAL=$((SKIPPED_NO_HEADER + SKIPPED_INVALID + SKIPPED_LIB_MISSING))
|
|
1053
|
+
|
|
1054
|
+
# #1929 — RESTORED/DEGRADED + JSON postcondition (mirrors operator hook L215-243).
|
|
1055
|
+
# Retains the legacy breakdown line for adopters parsing the old shape.
|
|
1056
|
+
_template_emit_install_postcondition
|
|
1057
|
+
|
|
1058
|
+
# RFC-0008 F7 legacy — plain-English breakdown line preserved for parser contract.
|
|
1008
1059
|
if [ "$SKIPPED_TOTAL" = "0" ] && [ "$FAILED_COUNT" = "0" ]; then
|
|
1009
1060
|
echo "bassclef-sync: installed $INSTALLED_COUNT hooks; skipped 0; failed 0"
|
|
1010
1061
|
else
|
|
@@ -165,6 +165,12 @@
|
|
|
165
165
|
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-commit-identifier-leak-scrub.sh",
|
|
166
166
|
"timeout": 10,
|
|
167
167
|
"tier": "lite"
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"type": "command",
|
|
171
|
+
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/git-stash-u-guard.sh",
|
|
172
|
+
"timeout": 5,
|
|
173
|
+
"tier": "lite"
|
|
168
174
|
}
|
|
169
175
|
]
|
|
170
176
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"tier": "lite",
|
|
3
|
-
"manifest_version": "1.
|
|
4
|
-
"generated_at": "2026-09-
|
|
3
|
+
"manifest_version": "1.11.0",
|
|
4
|
+
"generated_at": "2026-09-24T00:06:17Z",
|
|
5
5
|
"entries": [
|
|
6
6
|
{
|
|
7
7
|
"slug": "029-release-pipeline",
|
|
@@ -91,6 +91,14 @@
|
|
|
91
91
|
"content_hash": "sha256:732f8c5a9c2e206f55246aafbdd7fd5c2fa2a154b37b7281b238f0b1175e1ba0",
|
|
92
92
|
"description": "00-bassclef-hook-connect.sh — Auto-connect bassclef hooks to adopter settings.json."
|
|
93
93
|
},
|
|
94
|
+
{
|
|
95
|
+
"slug": "01-adopter-message-channel",
|
|
96
|
+
"type": "hook",
|
|
97
|
+
"path": ".claude/hooks/session-reflection.d/01-adopter-message-channel.sh",
|
|
98
|
+
"tier": "lite",
|
|
99
|
+
"content_hash": "sha256:c2831a9400ef8cacc4d79ed4bb3c8696a1b89826c7a9ece52e373a2c30f36e8c",
|
|
100
|
+
"description": "SessionStart fragment — adopter message channel (bassclef-upstream#1926)"
|
|
101
|
+
},
|
|
94
102
|
{
|
|
95
103
|
"slug": "05-active-bet",
|
|
96
104
|
"type": "hook",
|
|
@@ -232,7 +240,7 @@
|
|
|
232
240
|
"type": "hook",
|
|
233
241
|
"path": ".claude/hooks/session-reflection.d/80-hook-heartbeat-check.sh",
|
|
234
242
|
"tier": "lite",
|
|
235
|
-
"content_hash": "sha256:
|
|
243
|
+
"content_hash": "sha256:0955f4045ae979d6d11102b6aa711f74c3bf2683754beb1d2587e9debcdef30c",
|
|
236
244
|
"description": "Hook heartbeat check — reads standards/hook-cadence.json, calls"
|
|
237
245
|
},
|
|
238
246
|
{
|
|
@@ -387,6 +395,14 @@
|
|
|
387
395
|
"content_hash": "sha256:c923c8081ce4055060ac9d3e2f0a6a7f245087b96098a5c610196d74fc5ba97b",
|
|
388
396
|
"description": "file-rename-discipline-check — PreToolUse hook that blocks `git commit`"
|
|
389
397
|
},
|
|
398
|
+
{
|
|
399
|
+
"slug": "git-stash-u-guard",
|
|
400
|
+
"type": "hook",
|
|
401
|
+
"path": ".claude/hooks/git-stash-u-guard.sh",
|
|
402
|
+
"tier": "lite",
|
|
403
|
+
"content_hash": "sha256:23e45017aa058d6110a327e5e74a70233333b64a0d9c09c5f03549fb58c42add",
|
|
404
|
+
"description": "PreToolUse: Bash"
|
|
405
|
+
},
|
|
390
406
|
{
|
|
391
407
|
"slug": "hook-classifier-canary",
|
|
392
408
|
"type": "hook",
|
|
@@ -723,6 +739,14 @@
|
|
|
723
739
|
"content_hash": "sha256:10d42c54a64415f7e9b4f2a6701d57ffc89cc9d59ec0fec9ddc2af3c178855a2",
|
|
724
740
|
"description": "bassclef-dir-resolver.sh — resolve BASSCLEF_DIR across install layouts."
|
|
725
741
|
},
|
|
742
|
+
{
|
|
743
|
+
"slug": "capability-probe",
|
|
744
|
+
"type": "lib",
|
|
745
|
+
"path": "lib/capability-probe.sh",
|
|
746
|
+
"tier": "lite",
|
|
747
|
+
"content_hash": "sha256:12d4d7cfa78bd07e1f67a405a88b960ac62ac637297698d8ca43f217b74f4de0",
|
|
748
|
+
"description": "lib/capability-probe.sh — capability probe deep module."
|
|
749
|
+
},
|
|
726
750
|
{
|
|
727
751
|
"slug": "clean-artifacts-sweep",
|
|
728
752
|
"type": "lib",
|
|
@@ -1287,7 +1311,7 @@
|
|
|
1287
1311
|
"type": "presence-template",
|
|
1288
1312
|
"path": "presence/install/bassclef-sync.dispatcher.template.sh",
|
|
1289
1313
|
"tier": "lite",
|
|
1290
|
-
"content_hash": "sha256:
|
|
1314
|
+
"content_hash": "sha256:0c5eb10d58174652549d403cec382fc801a3fa7f7b3e4496dbf2eb5dcfffc2ac",
|
|
1291
1315
|
"description": "description: Retagged standard → lite per #1301 concentric rule (ADR-052 D1) —"
|
|
1292
1316
|
},
|
|
1293
1317
|
{
|
|
@@ -1295,7 +1319,7 @@
|
|
|
1295
1319
|
"type": "presence-template",
|
|
1296
1320
|
"path": "presence/install/bassclef-sync.template.sh",
|
|
1297
1321
|
"tier": "lite",
|
|
1298
|
-
"content_hash": "sha256:
|
|
1322
|
+
"content_hash": "sha256:a122102a034d07ec9a7c4b23e078707160b58aa5409a05eb6e30290fd8c27fb0",
|
|
1299
1323
|
"description": "description: BASSCLEF_SYNC_VERSION=2026-09-06-1496-sessionstart-heal-v1"
|
|
1300
1324
|
},
|
|
1301
1325
|
{
|
|
@@ -2146,8 +2170,8 @@
|
|
|
2146
2170
|
"type": "skill",
|
|
2147
2171
|
"path": ".claude/skills/build/SKILL.md",
|
|
2148
2172
|
"tier": "lite",
|
|
2149
|
-
"content_hash": "sha256:
|
|
2150
|
-
"description": "Take a
|
|
2173
|
+
"content_hash": "sha256:ef8f6b7872a29f74e8a9c181ab0b7c24f7c93bfc76b05baaf4cee4703318b10c",
|
|
2174
|
+
"description": "Take a plan from /launch and ship it. One builder per user story. Runs /verify. Opens PRs. Gates prod on a human. Refuses auth, schema, security, and tenant work. Two modes — full and degrade-with-note.",
|
|
2151
2175
|
"inputs": [
|
|
2152
2176
|
"A goal slug pointing to a spec on disk"
|
|
2153
2177
|
],
|
|
@@ -2534,8 +2558,8 @@
|
|
|
2534
2558
|
"type": "skill",
|
|
2535
2559
|
"path": ".claude/skills/launch/SKILL.md",
|
|
2536
2560
|
"tier": "lite",
|
|
2537
|
-
"content_hash": "sha256:
|
|
2538
|
-
"description": "Compare variants of an idea.
|
|
2561
|
+
"content_hash": "sha256:35763f40fe046500da02f8d37ff4e6984e18ab08b64ed0eaa3c4095d90a22225",
|
|
2562
|
+
"description": "Compare variants of an idea. Get a mock gallery. Pick a direction. Get a spec, user stories, and a plan to build. Two sizes — medium and full. Three modes — full, local, scratch.",
|
|
2539
2563
|
"inputs": [
|
|
2540
2564
|
"A rough idea or ticket or canvas"
|
|
2541
2565
|
],
|
|
@@ -2910,8 +2934,8 @@
|
|
|
2910
2934
|
"type": "skill",
|
|
2911
2935
|
"path": ".claude/skills/riff/SKILL.md",
|
|
2912
2936
|
"tier": "lite",
|
|
2913
|
-
"content_hash": "sha256:
|
|
2914
|
-
"description": "Produce 2
|
|
2937
|
+
"content_hash": "sha256:c481ce52be01c6d5e62e8ea07389e7612362b77721228fb59cd1c1c469f327ff",
|
|
2938
|
+
"description": "Produce 2 or 3 clickable mock variants. Each pins a luminary lens. Three modes — full, local, scratch. Full uses git, gh, and screenshots. Local writes to disk only. Scratch uses a tmp dir with no git.",
|
|
2915
2939
|
"composes_with": [
|
|
2916
2940
|
"interpret-input",
|
|
2917
2941
|
"luminary",
|
|
@@ -2924,8 +2948,8 @@
|
|
|
2924
2948
|
"2-3 clickable mock variants",
|
|
2925
2949
|
"Luminary lens per variant"
|
|
2926
2950
|
],
|
|
2927
|
-
"problem": "Design
|
|
2928
|
-
"value": "Two or three
|
|
2951
|
+
"problem": "Design work gets one variant. No comparison. The operator picks blind. Rework is heavy.",
|
|
2952
|
+
"value": "Two or three mocks per surface. Each mock pins a luminary lens. Cold adopters see a result in five minutes."
|
|
2929
2953
|
},
|
|
2930
2954
|
{
|
|
2931
2955
|
"slug": "riff-prototypes",
|