@thebassclef/lite 1.9.10 → 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.
- 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/artifact-ingestion-gate.sh +9 -3
- package/dist/lite/.claude/hooks/save-state.sh +6 -1
- package/dist/lite/.claude/hooks/session-reflection.d/97-last-block-count.sh +53 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/97-last-block-count.test.sh +131 -0
- package/dist/lite/.claude/hooks/session-reflection.sh +22 -0
- package/dist/lite/.claude/rules/last-block-marker.md +79 -0
- package/dist/lite/.claude/skills/build/SKILL.md +7 -1
- package/dist/lite/.claude/skills/ia-model/SKILL.md +8 -0
- package/dist/lite/.claude/skills/interaction-design/SKILL.md +8 -0
- package/dist/lite/.claude/skills/jtbd-tasks/SKILL.md +9 -1
- package/dist/lite/.claude/skills/last-block/SKILL.md +99 -0
- package/dist/lite/.claude/skills/launch/SKILL.md +18 -12
- package/dist/lite/.claude/skills/objectory-decompose/SKILL.md +5 -17
- package/dist/lite/.claude/skills/use-case/SKILL.md +5 -4
- package/dist/lite/bassclef-version.json +5 -5
- package/dist/lite/lib/composer-preflight.sh +12 -0
- package/dist/lite/lib/last-block.sh +153 -0
- package/dist/lite/lib/state.sh +11 -0
- package/dist/lite/scripts/interpret-input.sh +19 -4
- package/dist/lite/standards/lite-manifest-schema-changes.md +70 -0
- package/dist/lite/standards/lite-manifest.json +47 -16
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -67,7 +67,7 @@ your browser.
|
|
|
67
67
|
|
|
68
68
|
## Current release
|
|
69
69
|
|
|
70
|
-
<!-- version-start -->1.9.
|
|
70
|
+
<!-- version-start -->1.9.11<!-- version-end -->
|
|
71
71
|
|
|
72
72
|
See [CHANGELOG.md](CHANGELOG.md) for the full changelog. Adopter-facing
|
|
73
73
|
release notes live at [docs.bassclef.dev/docs/whats-new/releases](https://docs.bassclef.dev/docs/whats-new/releases).
|
package/dist/index.cjs
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version: "1.9.
|
|
1
|
+
export declare const version: "1.9.11";
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -143,9 +143,15 @@ if [ "$HAS_SOURCES_READ" -eq 1 ]; then
|
|
|
143
143
|
exit
|
|
144
144
|
}
|
|
145
145
|
')
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
# cli#311 cure — treat null / ~ / empty as "no parent declared" (Peirce three-case).
|
|
147
|
+
# YAML null shorthand is `null`, `~`, or an empty value. Prior code caught empty
|
|
148
|
+
# only; `parent_bet: null` tripped the gate on root goals that genuinely have no
|
|
149
|
+
# parent. Mirrors the #70 cure in bet-doc-gate.sh.
|
|
150
|
+
case "$PARENT_VALUE" in
|
|
151
|
+
"" | "null" | "Null" | "NULL" | "~" | "None")
|
|
152
|
+
continue
|
|
153
|
+
;;
|
|
154
|
+
esac
|
|
149
155
|
|
|
150
156
|
# Check if the parent path (or its basename without extension) appears
|
|
151
157
|
# in the Sources-read section. Accepts either:
|
|
@@ -209,7 +209,12 @@ DIRTY=$(git status --porcelain 2>/dev/null | grep -v '\.DS_Store' | grep -v 'SES
|
|
|
209
209
|
if [ -n "$DIRTY" ]; then
|
|
210
210
|
# Stage tracked modified files + any docs/ or chronicle/ or runtime state
|
|
211
211
|
git add -u 2>/dev/null || true
|
|
212
|
-
|
|
212
|
+
# docs/ except docs/prototypes/ — prototypes are review-gated mocks per
|
|
213
|
+
# .claude/rules/prototype-workflow.md; auto-save must not commit them
|
|
214
|
+
# before the operator confirms the rendered output (cli#323).
|
|
215
|
+
# Same pathspec pattern used for state/markers below.
|
|
216
|
+
git add 'docs/' ':!docs/prototypes' ':!docs/prototypes/**' 2>/dev/null || true
|
|
217
|
+
git add chronicle/ specs/ 2>/dev/null || true
|
|
213
218
|
# state/ except state/markers/ — markers are runtime gate evidence that
|
|
214
219
|
# trigger session-rescue false-positives if committed (bassclef#341 / WU-A2).
|
|
215
220
|
# Negative pathspec syntax requires git >= 2.13 (stable since 2017).
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# install-class: dual
|
|
4
|
+
# Session-reflection fragment — surface last-block marker count.
|
|
5
|
+
#
|
|
6
|
+
# Per @luminary don-norman signifier: when BLOCK banners from prior
|
|
7
|
+
# sessions sit in state/markers/last-block/, this fragment tells the
|
|
8
|
+
# adopter they exist and names /last-block as the retrieval path.
|
|
9
|
+
#
|
|
10
|
+
# Fires once at session-start. Reads marker count. Prints one line to
|
|
11
|
+
# stdout when count > 0. Silent on empty.
|
|
12
|
+
#
|
|
13
|
+
# Composition — sourced by session-reflection.sh under set -e. Must use
|
|
14
|
+
# `return`, not `exit`, else the coordinator dies. Per test-sufficiency
|
|
15
|
+
# criterion 12 (invocation-mode variation).
|
|
16
|
+
#
|
|
17
|
+
# Per bassclef-upstream#2036 Finding #10 + UC-skill-last-block.md.
|
|
18
|
+
|
|
19
|
+
# Resolve directory (same convention as lib/last-block.sh).
|
|
20
|
+
_last_block_count_dir() {
|
|
21
|
+
if [ -n "${LAST_BLOCK_DIR:-}" ]; then
|
|
22
|
+
echo "$LAST_BLOCK_DIR"
|
|
23
|
+
else
|
|
24
|
+
echo "${CLAUDE_PROJECT_DIR:-.}/state/markers/last-block"
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
LBC_DIR="$(_last_block_count_dir)"
|
|
29
|
+
|
|
30
|
+
if [ ! -d "$LBC_DIR" ]; then
|
|
31
|
+
# Fresh install or dir never created. Silent.
|
|
32
|
+
# shellcheck disable=SC2317 # dual-use sourced-or-standalone exit path
|
|
33
|
+
return 0 2>/dev/null || exit 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
LBC_COUNT=$(find "$LBC_DIR" -maxdepth 1 -name '*.marker' -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
37
|
+
|
|
38
|
+
if [ "$LBC_COUNT" -gt 0 ]; then
|
|
39
|
+
if [ "$LBC_COUNT" = "1" ]; then
|
|
40
|
+
echo "### LAST BLOCK"
|
|
41
|
+
echo ""
|
|
42
|
+
echo "1 BLOCK banner stored. Read it with /last-block."
|
|
43
|
+
echo ""
|
|
44
|
+
else
|
|
45
|
+
echo "### LAST BLOCKS"
|
|
46
|
+
echo ""
|
|
47
|
+
echo "${LBC_COUNT} BLOCK banners stored. Read the newest with /last-block, or list all with /last-block --list."
|
|
48
|
+
echo ""
|
|
49
|
+
fi
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# shellcheck disable=SC2317 # dual-use sourced-or-standalone exit path
|
|
53
|
+
return 0 2>/dev/null || exit 0
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# fixtures: bassclef-upstream-self
|
|
4
|
+
# shellcheck disable=SC2034
|
|
5
|
+
# Tier 0 test for .claude/hooks/session-reflection.d/97-last-block-count.sh
|
|
6
|
+
#
|
|
7
|
+
# Covers invocation-mode variation (criterion 12 of test-sufficiency rubric):
|
|
8
|
+
# both standalone and sourced-under-set-e paths.
|
|
9
|
+
#
|
|
10
|
+
# Ticket: bassclef-upstream#2036 Finding #10
|
|
11
|
+
#
|
|
12
|
+
# test-list:
|
|
13
|
+
# [x] T01: standalone + no dir -> silent, exit 0, no return error
|
|
14
|
+
# [x] T02: standalone + empty dir -> silent, exit 0
|
|
15
|
+
# [x] T03: standalone + 1 marker -> prints "1 BLOCK banner stored"
|
|
16
|
+
# [x] T04: standalone + 3 markers -> prints "3 BLOCK banners stored"
|
|
17
|
+
# [x] T05: sourced under set -e -> parent survives (no exit)
|
|
18
|
+
# [x] T06: honors LAST_BLOCK_DIR env override
|
|
19
|
+
|
|
20
|
+
set -euo pipefail
|
|
21
|
+
|
|
22
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
+
FRAG_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
24
|
+
FRAG="$FRAG_DIR/97-last-block-count.sh"
|
|
25
|
+
|
|
26
|
+
if [ ! -f "$FRAG" ]; then
|
|
27
|
+
echo "RED: $FRAG does not exist yet" >&2
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
FIXTURE_ROOT="$(mktemp -d)"
|
|
32
|
+
RESULTS_FILE="$FIXTURE_ROOT/results.log"
|
|
33
|
+
touch "$RESULTS_FILE"
|
|
34
|
+
trap 'rm -rf "$FIXTURE_ROOT"' EXIT
|
|
35
|
+
|
|
36
|
+
pass() { echo "PASS" >> "$RESULTS_FILE"; echo "PASS: $1"; }
|
|
37
|
+
fail() { echo "FAIL" >> "$RESULTS_FILE"; echo "FAIL: $1"; }
|
|
38
|
+
|
|
39
|
+
# T01: standalone + no dir
|
|
40
|
+
( LAST_BLOCK_DIR="$FIXTURE_ROOT/nonexistent"
|
|
41
|
+
export LAST_BLOCK_DIR
|
|
42
|
+
OUT=$(bash "$FRAG" 2>&1)
|
|
43
|
+
RC=$?
|
|
44
|
+
if [ "$RC" = "0" ] && [ -z "$OUT" ]; then
|
|
45
|
+
pass "T01: standalone + no dir -> silent"
|
|
46
|
+
else
|
|
47
|
+
fail "T01: standalone + no dir -> silent (rc=$RC out=$OUT)"
|
|
48
|
+
fi
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# T02: standalone + empty dir
|
|
52
|
+
( LAST_BLOCK_DIR="$(mktemp -d)"
|
|
53
|
+
export LAST_BLOCK_DIR
|
|
54
|
+
OUT=$(bash "$FRAG" 2>&1)
|
|
55
|
+
RC=$?
|
|
56
|
+
if [ "$RC" = "0" ] && [ -z "$OUT" ]; then
|
|
57
|
+
pass "T02: standalone + empty dir -> silent"
|
|
58
|
+
else
|
|
59
|
+
fail "T02: standalone + empty dir -> silent (rc=$RC out=$OUT)"
|
|
60
|
+
fi
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# T03: standalone + 1 marker
|
|
64
|
+
( DIR="$(mktemp -d)"
|
|
65
|
+
LAST_BLOCK_DIR="$DIR"
|
|
66
|
+
export LAST_BLOCK_DIR
|
|
67
|
+
echo "hook: a" > "$DIR/a.marker"
|
|
68
|
+
OUT=$(bash "$FRAG" 2>&1)
|
|
69
|
+
if echo "$OUT" | grep -qF "1 BLOCK banner stored"; then
|
|
70
|
+
pass "T03: 1 marker -> prints singular"
|
|
71
|
+
else
|
|
72
|
+
fail "T03: 1 marker (got: $OUT)"
|
|
73
|
+
fi
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# T04: standalone + 3 markers
|
|
77
|
+
( DIR="$(mktemp -d)"
|
|
78
|
+
LAST_BLOCK_DIR="$DIR"
|
|
79
|
+
export LAST_BLOCK_DIR
|
|
80
|
+
echo "hook: a" > "$DIR/a.marker"
|
|
81
|
+
echo "hook: b" > "$DIR/b.marker"
|
|
82
|
+
echo "hook: c" > "$DIR/c.marker"
|
|
83
|
+
OUT=$(bash "$FRAG" 2>&1)
|
|
84
|
+
if echo "$OUT" | grep -qF "3 BLOCK banners stored"; then
|
|
85
|
+
pass "T04: 3 markers -> prints plural with count"
|
|
86
|
+
else
|
|
87
|
+
fail "T04: 3 markers (got: $OUT)"
|
|
88
|
+
fi
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# T05: sourced under set -e -> parent survives (criterion 12)
|
|
92
|
+
( DIR="$(mktemp -d)"
|
|
93
|
+
LAST_BLOCK_DIR="$DIR"
|
|
94
|
+
export LAST_BLOCK_DIR
|
|
95
|
+
echo "hook: a" > "$DIR/a.marker"
|
|
96
|
+
# Simulate coordinator: set -e wrapper + source
|
|
97
|
+
CANARY_FILE="$FIXTURE_ROOT/canary-t05"
|
|
98
|
+
(
|
|
99
|
+
set -e
|
|
100
|
+
# shellcheck source=/dev/null
|
|
101
|
+
source "$FRAG"
|
|
102
|
+
# If frag used `exit` instead of `return`, this line never runs.
|
|
103
|
+
touch "$CANARY_FILE"
|
|
104
|
+
) >/dev/null 2>&1 || true
|
|
105
|
+
if [ -f "$CANARY_FILE" ]; then
|
|
106
|
+
pass "T05: sourced under set -e survives (canary ran)"
|
|
107
|
+
else
|
|
108
|
+
fail "T05: sourced under set -e survives (canary missing)"
|
|
109
|
+
fi
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# T06: honors LAST_BLOCK_DIR env override
|
|
113
|
+
( DIR="$(mktemp -d)"
|
|
114
|
+
LAST_BLOCK_DIR="$DIR"
|
|
115
|
+
export LAST_BLOCK_DIR
|
|
116
|
+
echo "hook: a" > "$DIR/a.marker"
|
|
117
|
+
OUT=$(bash "$FRAG" 2>&1)
|
|
118
|
+
if echo "$OUT" | grep -qF "1 BLOCK banner stored"; then
|
|
119
|
+
pass "T06: honors LAST_BLOCK_DIR override"
|
|
120
|
+
else
|
|
121
|
+
fail "T06: honors LAST_BLOCK_DIR override (got: $OUT)"
|
|
122
|
+
fi
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
echo
|
|
126
|
+
echo "=== Results ==="
|
|
127
|
+
PASS=$(awk '/^PASS$/ {c++} END {print c+0}' "$RESULTS_FILE")
|
|
128
|
+
FAIL=$(awk '/^FAIL$/ {c++} END {print c+0}' "$RESULTS_FILE")
|
|
129
|
+
echo "PASS: $PASS"
|
|
130
|
+
echo "FAIL: $FAIL"
|
|
131
|
+
if [ "$FAIL" -gt 0 ]; then exit 1; fi
|
|
@@ -74,6 +74,14 @@ else
|
|
|
74
74
|
fi
|
|
75
75
|
export BASSCLEF_DIR
|
|
76
76
|
|
|
77
|
+
# Source lib/last-block.sh so banner calls persist markers. Per Finding
|
|
78
|
+
# #10 — adopters need a first-class way to retrieve BLOCK text they saw.
|
|
79
|
+
# The lib fails soft; missing lib leaves the banners untouched.
|
|
80
|
+
# shellcheck source=/dev/null
|
|
81
|
+
if [ -f "${BASSCLEF_DIR}/lib/last-block.sh" ]; then
|
|
82
|
+
source "${BASSCLEF_DIR}/lib/last-block.sh"
|
|
83
|
+
fi
|
|
84
|
+
|
|
77
85
|
# High-contrast BLOCKED banner; used by every module that raises a gap.
|
|
78
86
|
# RFC-0008 F3 — "Silence is not deferral" is a commitment device. It works
|
|
79
87
|
# because it forecloses the easy out, and it is consumable: spend it on a
|
|
@@ -81,9 +89,13 @@ export BASSCLEF_DIR
|
|
|
81
89
|
# Callers that can prove their own state is benign pass a second argument
|
|
82
90
|
# and get a softer close. Callers that pass one argument behave exactly as
|
|
83
91
|
# before, so the other 27 sites are untouched.
|
|
92
|
+
#
|
|
93
|
+
# Per Finding #10 — each call now also writes a marker so the adopter can
|
|
94
|
+
# retrieve the exact banner text later via /last-block.
|
|
84
95
|
blocked_banner() {
|
|
85
96
|
local msg="$1"
|
|
86
97
|
local severity="${2:-blocking}"
|
|
98
|
+
local hook_name="${BASSCLEF_CALLING_HOOK:-session-reflection}"
|
|
87
99
|
echo ""
|
|
88
100
|
case "$severity" in
|
|
89
101
|
info)
|
|
@@ -106,9 +118,14 @@ blocked_banner() {
|
|
|
106
118
|
echo "────────────────────────────────────────────"
|
|
107
119
|
echo "ACTION: resolve OR explicitly defer (per .claude/rules/blocked-items.md)."
|
|
108
120
|
echo "Silence is not deferral. Propose this as item #1 in your session plan."
|
|
121
|
+
echo "(Retrieve this banner later with: /last-block ${hook_name})"
|
|
109
122
|
;;
|
|
110
123
|
esac
|
|
111
124
|
echo ""
|
|
125
|
+
# Persist marker for /last-block retrieval. Fail-soft.
|
|
126
|
+
if type -t record_last_block >/dev/null 2>&1; then
|
|
127
|
+
record_last_block "$hook_name" "$severity" "$msg" 2>/dev/null || true
|
|
128
|
+
fi
|
|
112
129
|
}
|
|
113
130
|
|
|
114
131
|
# Vendor-swap constant — the AI in the harness running bassclef. Adopter
|
|
@@ -131,7 +148,12 @@ guardrail_banner() {
|
|
|
131
148
|
echo "────────────────────────────────────────────"
|
|
132
149
|
echo "bassclef is steering $AI_NAME to $step — $retry"
|
|
133
150
|
echo "────────────────────────────────────────────"
|
|
151
|
+
echo "(Retrieve this banner later with: /last-block ${hook_name})"
|
|
134
152
|
echo ""
|
|
153
|
+
# Persist marker for /last-block retrieval. Fail-soft.
|
|
154
|
+
if type -t record_last_block >/dev/null 2>&1; then
|
|
155
|
+
record_last_block "$hook_name" "guardrail" "$step — $retry" 2>/dev/null || true
|
|
156
|
+
fi
|
|
135
157
|
}
|
|
136
158
|
|
|
137
159
|
# RFC-0008 F6 — this block is steering aimed at the agent, but the adopter
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
tier: lite
|
|
3
|
+
title: Last-block marker
|
|
4
|
+
anchor_luminaries:
|
|
5
|
+
- john-ousterhout
|
|
6
|
+
- tony-hoare
|
|
7
|
+
- michael-feathers
|
|
8
|
+
- don-norman
|
|
9
|
+
- alan-cooper
|
|
10
|
+
- steve-krug
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Last-block marker
|
|
14
|
+
|
|
15
|
+
Every call to `blocked_banner()` or `guardrail_banner()` in `.claude/hooks/session-reflection.sh` persists a marker at `state/markers/last-block/<hook>.marker`. The marker carries the hook name, severity, timestamp, and verbatim banner text. The `/last-block` skill reads markers back.
|
|
16
|
+
|
|
17
|
+
This rule is the methodology layer. The mechanical layer is `lib/last-block.sh` sourced by `session-reflection.sh`. Both required together to close Kunal Finding #10.
|
|
18
|
+
|
|
19
|
+
## When this rule fires
|
|
20
|
+
|
|
21
|
+
Every call to `blocked_banner()` or `guardrail_banner()` in `.claude/hooks/session-reflection.sh`. The banner helpers already call `record_last_block` from the sourced lib. Hook authors who call these helpers get the marker for free.
|
|
22
|
+
|
|
23
|
+
## Marker format
|
|
24
|
+
|
|
25
|
+
Plaintext layout per `lib/last-block.sh`:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
hook: <hook-name>
|
|
29
|
+
severity: blocking|advisory|info|guardrail
|
|
30
|
+
fired_at: <ISO-8601 UTC>
|
|
31
|
+
session: <CLAUDE_SESSION_ID or "unknown">
|
|
32
|
+
---
|
|
33
|
+
<verbatim banner text, truncated at 8KB with a truncation marker>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Grep-friendly. One marker file per hook. Overwrites on each fire. No rotation in V1.
|
|
37
|
+
|
|
38
|
+
## What the rule requires
|
|
39
|
+
|
|
40
|
+
For hooks that fire BLOCK via `blocked_banner()` or `guardrail_banner()`:
|
|
41
|
+
|
|
42
|
+
- Pass the hook name via the `BASSCLEF_CALLING_HOOK` env var before the call. The default is `session-reflection` when unset.
|
|
43
|
+
- Pass a plain severity (`blocking` / `advisory` / `info`) to `blocked_banner()` as the second arg.
|
|
44
|
+
- The banner helper writes the marker on its own. No extra step.
|
|
45
|
+
|
|
46
|
+
For hooks that fire BLOCK via plain `echo` to stderr (not via the shared helpers):
|
|
47
|
+
|
|
48
|
+
- Follow-on retrofit per `.claude/rules/bootstrap-pair-discipline.md`. Adopt the shared helpers when the hook is next touched.
|
|
49
|
+
|
|
50
|
+
## Anti-patterns
|
|
51
|
+
|
|
52
|
+
**Hook writes a marker directly without using the lib.**
|
|
53
|
+
INSTEAD source `lib/last-block.sh` and call `record_last_block`. The lib owns the format.
|
|
54
|
+
|
|
55
|
+
**Marker format drift.**
|
|
56
|
+
INSTEAD the plaintext layout stays pinned in `lib/last-block.sh`. Change the lib + Tier 0 tests + this rule in one PR.
|
|
57
|
+
|
|
58
|
+
**BLOCK banner emitted directly to stderr without recording.**
|
|
59
|
+
INSTEAD route through `blocked_banner()` so the marker lands with the banner. If a hook has a reason to skip the shared helper, call `record_last_block` directly after the echo.
|
|
60
|
+
|
|
61
|
+
## Override
|
|
62
|
+
|
|
63
|
+
`SKIP_LAST_BLOCK_RECORD=1` bypasses marker recording for one call. Logged via trace-helper. Rare — mostly for test fixtures that need to control marker writing timing.
|
|
64
|
+
|
|
65
|
+
## Composes with
|
|
66
|
+
|
|
67
|
+
- `.claude/rules/blocked-items.md` — sister rule; the two banner shapes live there, and this rule adds the persistence requirement
|
|
68
|
+
- `.claude/rules/bootstrap-pair-discipline.md` — this rule + lib + skill + tests + settings wire ship together
|
|
69
|
+
- `.claude/rules/testing-tier-config.md` — Tier 0 strict TDD on `lib/last-block.sh`
|
|
70
|
+
- `.claude/rules/we-dont-break-adopters.md` — additive change; existing banner callers see no behavior change
|
|
71
|
+
- `.claude/hooks/session-reflection.sh` — the hook that sources the lib + calls record_last_block from both banner helpers
|
|
72
|
+
- `lib/last-block.sh` — the deep module
|
|
73
|
+
- `.claude/skills/last-block/SKILL.md` — the user-invocable surface
|
|
74
|
+
- @luminary john-ousterhout — deep module anchor
|
|
75
|
+
- @luminary tony-hoare — pre/postcondition anchor (write path fail-soft)
|
|
76
|
+
- @luminary michael-feathers — characterization anchor (Tier 0 tests pin every path)
|
|
77
|
+
- @luminary don-norman — signifier (banner footer teaches the recovery path)
|
|
78
|
+
- @luminary alan-cooper — goal-directed (one goal: get banner text back)
|
|
79
|
+
- @luminary steve-krug — discoverability (no-args default)
|
|
@@ -371,7 +371,13 @@ fi
|
|
|
371
371
|
|
|
372
372
|
### Phase 2b — Adopter-mode framing (bassclef#943)
|
|
373
373
|
|
|
374
|
-
Two paths — adopter-mode and operator-mode. Default is adopter
|
|
374
|
+
Two paths — adopter-mode and operator-mode. Default is adopter after a `/launch --local` run. Operator-mode is the default otherwise.
|
|
375
|
+
|
|
376
|
+
Mode detection reads the goal doc's `preview_state:` frontmatter field. /launch writes that field. Open the file it names. When `local_url` is present, /build picks adopter-mode. Default to operator-mode when the field is absent.
|
|
377
|
+
|
|
378
|
+
<!-- FKGL-EXEMPT: ticket reference -->
|
|
379
|
+
Per sunj-labs/bassclef-cli#326.
|
|
380
|
+
<!-- /FKGL-EXEMPT -->
|
|
375
381
|
|
|
376
382
|
#### Adopter-mode pre-dispatch surface
|
|
377
383
|
|
|
@@ -62,6 +62,14 @@ Derive nav from the screen map. Apply Peter Morville's IA framework:
|
|
|
62
62
|
- **C**ategory: by type or classification
|
|
63
63
|
- **H**ierarchy: by importance or score
|
|
64
64
|
|
|
65
|
+
## Output file
|
|
66
|
+
|
|
67
|
+
Write to `docs/ia-models/<slug>.md`. Use the active goal or sprint slug. `/launch` Phase 6 reads from this path. `/build` Phase 0.5 reads from the same path.
|
|
68
|
+
|
|
69
|
+
<!-- FKGL-EXEMPT: ticket reference -->
|
|
70
|
+
Per sunj-labs/bassclef-cli#317.
|
|
71
|
+
<!-- /FKGL-EXEMPT -->
|
|
72
|
+
|
|
65
73
|
## Output Format
|
|
66
74
|
|
|
67
75
|
```markdown
|
|
@@ -68,6 +68,14 @@ BACKSTAGE (system does): Score → Index → Enrich → Notify
|
|
|
68
68
|
SUPPORT PROCESSES: Ingest → Classify → Deduplicate
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
## Output file
|
|
72
|
+
|
|
73
|
+
Write to `docs/interaction-designs/<slug>.md`. Use the active goal or sprint slug. `/build` Phase 0.5 reads from this path. `/launch` Phase 7 reads from the same path.
|
|
74
|
+
|
|
75
|
+
<!-- FKGL-EXEMPT: ticket reference -->
|
|
76
|
+
Per sunj-labs/bassclef-cli#317.
|
|
77
|
+
<!-- /FKGL-EXEMPT -->
|
|
78
|
+
|
|
71
79
|
## Output Format
|
|
72
80
|
|
|
73
81
|
For each interaction flow:
|
|
@@ -188,9 +188,17 @@ For each JTBD, produce:
|
|
|
188
188
|
| 1.1 ... | [job] | [screen or component implied] |
|
|
189
189
|
```
|
|
190
190
|
|
|
191
|
+
## Output file
|
|
192
|
+
|
|
193
|
+
Write to `docs/jtbd/<slug>.md`. Use the active goal or sprint slug. `/build` Phase 0.5 reads from this path. See the ticket noted at the end of this skill body.
|
|
194
|
+
|
|
195
|
+
<!-- FKGL-EXEMPT: ticket reference -->
|
|
196
|
+
Per sunj-labs/bassclef-cli#317.
|
|
197
|
+
<!-- /FKGL-EXEMPT -->
|
|
198
|
+
|
|
191
199
|
## Next Step
|
|
192
200
|
|
|
193
|
-
|
|
201
|
+
The output feeds `/task-scenarios` for user stories. Or it feeds `/ia-model` to drive IA.
|
|
194
202
|
|
|
195
203
|
## References
|
|
196
204
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: last-block
|
|
3
|
+
description: Print the exact text of a BLOCK or guardrail banner you saw this session or prior. No args shows the newest. One hook name shows that hook. --list enumerates every stored banner newest first.
|
|
4
|
+
tier: lite
|
|
5
|
+
user_invocable: true
|
|
6
|
+
disable_model_invocation: false
|
|
7
|
+
authoring_luminaries:
|
|
8
|
+
primary: john-ousterhout
|
|
9
|
+
supporting:
|
|
10
|
+
- tony-hoare
|
|
11
|
+
- michael-feathers
|
|
12
|
+
composes_with: []
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# /last-block
|
|
16
|
+
|
|
17
|
+
## What you get
|
|
18
|
+
|
|
19
|
+
An adopter who sees a BLOCK banner can retrieve the exact text later. No scrolling through the terminal. No grep over trace logs. Type `/last-block`. The banner prints.
|
|
20
|
+
|
|
21
|
+
## When to use it
|
|
22
|
+
|
|
23
|
+
Three cases.
|
|
24
|
+
|
|
25
|
+
- You saw a BLOCK banner, scrolled past it, and want to re-read the recipe or override instruction.
|
|
26
|
+
- You want to file a ticket and need the exact BLOCK text as evidence.
|
|
27
|
+
- You want to see which hooks fired BLOCK during the prior session.
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
/last-block # print the newest stored banner
|
|
33
|
+
/last-block <hook-name> # print the banner stored under <hook-name>
|
|
34
|
+
/last-block --list # list every stored banner newest first
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## How it works
|
|
38
|
+
|
|
39
|
+
Every BLOCK banner printed by `blocked_banner()` or `guardrail_banner()` persists a marker at `state/markers/last-block/<hook>.marker`. Per-hook markers overwrite on each fire. The lib `lib/last-block.sh` reads them back.
|
|
40
|
+
|
|
41
|
+
Marker format is plaintext:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
hook: <hook-name>
|
|
45
|
+
severity: blocking|advisory|info|guardrail
|
|
46
|
+
fired_at: <ISO-8601 UTC>
|
|
47
|
+
session: <session-id or unknown>
|
|
48
|
+
---
|
|
49
|
+
<verbatim banner text, truncated at 8KB>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Procedure
|
|
53
|
+
|
|
54
|
+
1. Source `lib/last-block.sh`.
|
|
55
|
+
2. If `--list` arg, call `list_last_blocks` and print to stdout.
|
|
56
|
+
3. If a hook-name arg, call `read_last_block <hook>` and print.
|
|
57
|
+
4. Else call `read_last_block` and print the newest.
|
|
58
|
+
5. Exit 0 on hit, 1 on miss.
|
|
59
|
+
|
|
60
|
+
## Example
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
$ /last-block
|
|
64
|
+
hook: pre-commit-gate
|
|
65
|
+
severity: blocking
|
|
66
|
+
fired_at: 2026-10-04T15:21:08Z
|
|
67
|
+
session: 2026-10-04-slot-6
|
|
68
|
+
---
|
|
69
|
+
🛑 BLOCKED: path contains operator identifier /Users/<name>/...
|
|
70
|
+
Override: SKIP_OPERATOR_PATHS=1 git commit ...
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## FAQ
|
|
74
|
+
|
|
75
|
+
### What hooks persist markers?
|
|
76
|
+
|
|
77
|
+
Any hook that calls `blocked_banner()` or `guardrail_banner()` from `session-reflection.sh`. ~27 call sites today. Hooks that fire BLOCK via plain `echo` to stderr are a follow-on retrofit.
|
|
78
|
+
|
|
79
|
+
### Does the marker persist across machines?
|
|
80
|
+
|
|
81
|
+
No. Markers live in `state/markers/last-block/` which bassclef-upstream gitignores. The marker survives the session that wrote it; it does not sync to teammates.
|
|
82
|
+
|
|
83
|
+
### Can I clear markers?
|
|
84
|
+
|
|
85
|
+
Yes — delete `state/markers/last-block/<hook>.marker` or the whole directory. Next BLOCK fire re-populates.
|
|
86
|
+
|
|
87
|
+
### What if the lib is missing?
|
|
88
|
+
|
|
89
|
+
The banner helpers detect missing lib and skip the record call. Banners still print. The marker is just not written. `/last-block` reports "no block recorded".
|
|
90
|
+
|
|
91
|
+
## Refs
|
|
92
|
+
|
|
93
|
+
- Ticket: bassclef-upstream#2036 Finding #10
|
|
94
|
+
- UC: `docs/use-cases/UC-skill-last-block.md`
|
|
95
|
+
- Rule: `.claude/rules/last-block-marker.md`
|
|
96
|
+
- Lib: `lib/last-block.sh`
|
|
97
|
+
- Sister rule: `.claude/rules/blocked-items.md`
|
|
98
|
+
- Luminary: `.claude/luminaries/john-ousterhout.md` (deep module)
|
|
99
|
+
- /rfc outside lenses: `.claude/luminaries/don-norman.md` + `.claude/luminaries/steve-krug.md` + `.claude/luminaries/alan-cooper.md`
|
|
@@ -379,7 +379,7 @@ Produces `InputArtifact` at `docs/input-artifacts/<sprint-slug>-input.json`. Dow
|
|
|
379
379
|
### Phase 2 — `/objectory-decompose` (full BCE matrix)
|
|
380
380
|
|
|
381
381
|
Same as `/stage` Phase 2 — Jacobson method via `/objectory-decompose`.
|
|
382
|
-
Output: `docs/decompositions/<slug>-domain.md` with entity inventory
|
|
382
|
+
Output: `docs/decompositions/<sprint-slug>-input-domain.md` with entity inventory
|
|
383
383
|
+ actor list + verb-goal pairs + BCE matrix.
|
|
384
384
|
|
|
385
385
|
**Distinction from `/stage`**: medium + full consume the FULL BCE
|
|
@@ -500,7 +500,7 @@ map derived from BCE entities.
|
|
|
500
500
|
/interaction-design # consumes /ia-model + /use-case
|
|
501
501
|
```
|
|
502
502
|
|
|
503
|
-
Output: state diagrams + sequence diagrams in `docs/interaction-
|
|
503
|
+
Output: state diagrams + sequence diagrams in `docs/interaction-designs/<slug>.md` (per sunj-labs/bassclef-cli#317).
|
|
504
504
|
|
|
505
505
|
### Phase 8 — `/decompose` (GRASP responsibility assignment)
|
|
506
506
|
|
|
@@ -552,12 +552,18 @@ to produce reviews per perspective. Reviews surface in PR body.
|
|
|
552
552
|
/ux-migration <chosen-variant-slug> # if base= provided, use that; else operator chooses post-gallery
|
|
553
553
|
```
|
|
554
554
|
|
|
555
|
-
|
|
556
|
-
prototype-HTML→React mapping table + work packages with dependency-aware
|
|
557
|
-
build order.
|
|
555
|
+
<!-- FKGL-EXEMPT: cli#324 cure — technical precision required naming paired SKILL steps + file paths -->
|
|
558
556
|
|
|
559
|
-
|
|
560
|
-
|
|
557
|
+
Output: `docs/specs/ux-migration-<slug>.md` carries token extraction, prototype-to-React mapping, and work packages.
|
|
558
|
+
|
|
559
|
+
**When to run full `/ux-migration` vs partial (cli#324):**
|
|
560
|
+
|
|
561
|
+
- **Existing UI (redesign).** Run the full skill. The `/ux-migration` SKILL names this a MUST for redesigns.
|
|
562
|
+
- **Greenfield (no existing UI).** Run only Step 3b and Step 3c. Step 3b extracts design tokens. Step 3c maps prototype HTML to React components. The `/ux-migration` SKILL says skip the full migration for greenfield.
|
|
563
|
+
|
|
564
|
+
Pick by project state. Does `src/app/` carry a shipping UI? Yes means redesign. No means greenfield. When unsure, default to greenfield partial. The full gate's output is a component diff. A component diff has no value on an empty tree.
|
|
565
|
+
|
|
566
|
+
<!-- /FKGL-EXEMPT -->
|
|
561
567
|
|
|
562
568
|
### Phase 12 — Deploy or local-serve (mode-gated)
|
|
563
569
|
|
|
@@ -621,9 +627,9 @@ status != build-ready.
|
|
|
621
627
|
### Phase 14 — Scaffold Construction iteration-goal draft
|
|
622
628
|
|
|
623
629
|
```yaml
|
|
624
|
-
# docs/iteration-bets/<
|
|
630
|
+
# docs/iteration-bets/<sprint-slug>-construction.md (DRAFT) # cli#326
|
|
625
631
|
---
|
|
626
|
-
slug: <
|
|
632
|
+
slug: <sprint-slug>-construction
|
|
627
633
|
phase: Construction
|
|
628
634
|
status: draft # operator must change to "active" before /build-iteration runs
|
|
629
635
|
parent_roadmap: docs/roadmaps/<roadmap-slug>.md # if known
|
|
@@ -637,8 +643,8 @@ references:
|
|
|
637
643
|
- {type: spec, id: <sprint-slug>}
|
|
638
644
|
- {type: decomposition, id: <sprint-slug>-grasp}
|
|
639
645
|
- {type: input-artifact, id: <sprint-slug>-input}
|
|
640
|
-
|
|
641
|
-
mode: scope
|
|
646
|
+
time_budget:
|
|
647
|
+
mode: fixed-scope
|
|
642
648
|
estimated_turns: <derived from /spec step enumeration>
|
|
643
649
|
stopping_criteria:
|
|
644
650
|
- All step PRs filed (count from spec)
|
|
@@ -677,7 +683,7 @@ appetite:
|
|
|
677
683
|
**Viability signal**:
|
|
678
684
|
(derived from /spec acceptance criteria; operator confirms)
|
|
679
685
|
|
|
680
|
-
**
|
|
686
|
+
**Time budget**:
|
|
681
687
|
(derived from spec step enumeration; operator finalizes)
|
|
682
688
|
```
|
|
683
689
|
|
|
@@ -57,12 +57,6 @@ source lib/state.sh
|
|
|
57
57
|
INPUT=$(state_reference_get input-artifact "<slug>")
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
Or via wrapper:
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
INPUT=$(bash scripts/state.sh get reference input-artifact "<slug>")
|
|
64
|
-
```
|
|
65
|
-
|
|
66
60
|
**Required fields** (extract via `jq`):
|
|
67
61
|
- `extracted_text` — the load-bearing input for analysis
|
|
68
62
|
- `type` — guides extraction (text vs. URL vs. image have different signals)
|
|
@@ -164,18 +158,12 @@ phase: jacobson # marks Phase B MVP scope; cockburn is follow-up
|
|
|
164
158
|
|
|
165
159
|
### 4. Validate output
|
|
166
160
|
|
|
167
|
-
|
|
168
|
-
against `decomposition.schema.json`.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
Or invoke explicitly:
|
|
172
|
-
|
|
173
|
-
```bash
|
|
174
|
-
bash scripts/state.sh validate docs/decompositions/<slug>-domain.md
|
|
175
|
-
```
|
|
161
|
+
The state-validate.sh hook runs on write. It checks the frontmatter
|
|
162
|
+
against `decomposition.schema.json`. A failed check BLOCKs the write.
|
|
163
|
+
Fix the frontmatter and retry.
|
|
176
164
|
|
|
177
|
-
Exit 0 = clean
|
|
178
|
-
field
|
|
165
|
+
Exit 0 = clean. Exit 3 = schema violation. The BLOCK message names
|
|
166
|
+
the field.
|
|
179
167
|
|
|
180
168
|
### 5. Surface to next step
|
|
181
169
|
|
|
@@ -101,10 +101,11 @@ if a required input is missing:
|
|
|
101
101
|
1. **Personas** — `docs/personas/*.md` (preferred) or
|
|
102
102
|
`docs/design/personas/*.md` (legacy). Use-case's **primary
|
|
103
103
|
actor** MUST be a persona slug, not "user."
|
|
104
|
-
2. **JTBDs** — `docs/
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
2. **JTBDs** — `docs/jtbd/*.md` (preferred, per cli#317) or
|
|
105
|
+
`docs/design/personas/[persona]/jtbds.md` (legacy fallback).
|
|
106
|
+
Each use case has a single **goal** that traces to a JTBD
|
|
107
|
+
outcome. Halt if no JTBDs for the scope in question (warn the
|
|
108
|
+
operator; offer to run `/jtbd-tasks` first).
|
|
108
109
|
3. **Value Prop Canvas** (optional) — `docs/value-prop-canvas/*.md`.
|
|
109
110
|
When present, Pain-Relievers + Gain-Creators ground the "why
|
|
110
111
|
this goal matters" framing in the stakeholders+interests block.
|
|
@@ -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": "
|
|
2
|
+
"version": "v1.8.0",
|
|
3
|
+
"marker_tag": "release-2026-10-06-a874c748",
|
|
4
|
+
"release_date": "2026-10-06T20:56:57Z",
|
|
5
|
+
"release_sha": "a874c748",
|
|
6
|
+
"release_tag": "v1.8.0"
|
|
7
7
|
}
|
|
@@ -269,6 +269,18 @@ composer_preflight_check() {
|
|
|
269
269
|
echo "check_6_message=hosting_platform=none + --no-launch (variants-only run)"
|
|
270
270
|
echo "check_6_recommendation="
|
|
271
271
|
pass=$((pass + 1))
|
|
272
|
+
elif [ "$skill_name" = "build" ]; then
|
|
273
|
+
# cli#309 cure — /build has no --no-launch flag and its Phase 7 deploy
|
|
274
|
+
# is a documented stub (see .claude/skills/build/SKILL.md Phase 7). Warn
|
|
275
|
+
# (not fail) so dispatch proceeds; Phase 7 will emit a TODO rather than
|
|
276
|
+
# run a real deploy. Adopter can set a real hosting_platform when ready
|
|
277
|
+
# to use Phase 7 live.
|
|
278
|
+
# Anchor: @luminary tony-hoare (postcondition per skill) +
|
|
279
|
+
# @luminary michael-nygard (fail-soft when it does not matter).
|
|
280
|
+
echo "check_6_status=warn"
|
|
281
|
+
echo "check_6_message=hosting_platform=none — /build Phase 7 deploy is a stub; dispatch proceeds, deploy emits TODO instead of running"
|
|
282
|
+
echo "check_6_recommendation=Set hosting_platform to a deploy provider (amplify) when ready to live-deploy; see .claude/skills/build/SKILL.md Phase 7"
|
|
283
|
+
warn=$((warn + 1))
|
|
272
284
|
else
|
|
273
285
|
echo "check_6_status=fail"
|
|
274
286
|
echo "check_6_message=hosting_platform=none — composer would generate variants that can't launch"
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# lib/last-block.sh — persist and read BLOCK banners.
|
|
4
|
+
#
|
|
5
|
+
# Per @luminary john-ousterhout deep module: three narrow functions hide
|
|
6
|
+
# the plaintext marker format. Callers persist, read, and list without
|
|
7
|
+
# touching file layout.
|
|
8
|
+
#
|
|
9
|
+
# Exposed functions:
|
|
10
|
+
# record_last_block <hook> <severity> <message> — write a marker
|
|
11
|
+
# read_last_block [<hook>] — read one marker (newest if no arg)
|
|
12
|
+
# list_last_blocks — list every marker newest first
|
|
13
|
+
#
|
|
14
|
+
# Marker format (plaintext, grep-friendly):
|
|
15
|
+
# hook: <hook-name>
|
|
16
|
+
# severity: blocking|advisory|info
|
|
17
|
+
# fired_at: <ISO-8601 UTC>
|
|
18
|
+
# session: <session-id or "unknown">
|
|
19
|
+
# ---
|
|
20
|
+
# <verbatim banner text, truncated to 8KB with [truncated] marker>
|
|
21
|
+
#
|
|
22
|
+
# Fail-soft: every write path returns 0 even on failure; stderr WARN
|
|
23
|
+
# describes the problem. Per @luminary michael-nygard — a persistence lib
|
|
24
|
+
# must never crash its caller.
|
|
25
|
+
#
|
|
26
|
+
# Per bassclef-upstream#2036 Finding #10 + UC-skill-last-block.md.
|
|
27
|
+
|
|
28
|
+
# Resolve the marker directory. Caller overrides via LAST_BLOCK_DIR for
|
|
29
|
+
# tests. Default is state/markers/last-block/ under CLAUDE_PROJECT_DIR
|
|
30
|
+
# (fallback to current dir when unset).
|
|
31
|
+
_last_block_dir() {
|
|
32
|
+
if [ -n "${LAST_BLOCK_DIR:-}" ]; then
|
|
33
|
+
echo "$LAST_BLOCK_DIR"
|
|
34
|
+
else
|
|
35
|
+
echo "${CLAUDE_PROJECT_DIR:-.}/state/markers/last-block"
|
|
36
|
+
fi
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# record_last_block <hook> <severity> <message>
|
|
40
|
+
# Writes a marker. Fail-soft on every error (returns 0; WARN to stderr).
|
|
41
|
+
record_last_block() {
|
|
42
|
+
local hook="${1:-unknown}"
|
|
43
|
+
local severity="${2:-blocking}"
|
|
44
|
+
local message="${3:-}"
|
|
45
|
+
local dir
|
|
46
|
+
dir="$(_last_block_dir)"
|
|
47
|
+
|
|
48
|
+
# mkdir -p fail-soft — read-only disk, permission denied, etc.
|
|
49
|
+
if ! mkdir -p "$dir" 2>/dev/null; then
|
|
50
|
+
echo "record_last_block: cannot create '$dir' — marker not written" >&2
|
|
51
|
+
return 0
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# Truncate banner text to 8192 bytes to bound marker size.
|
|
55
|
+
local max_bytes=8192
|
|
56
|
+
local truncated=""
|
|
57
|
+
local body_bytes
|
|
58
|
+
body_bytes=$(printf '%s' "$message" | wc -c | tr -d ' ')
|
|
59
|
+
if [ "$body_bytes" -gt "$max_bytes" ]; then
|
|
60
|
+
message="$(printf '%s' "$message" | head -c "$max_bytes")"
|
|
61
|
+
truncated=$'\n[truncated — original was '"$body_bytes"' bytes]'
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
local ts
|
|
65
|
+
ts="$(date -u +'%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || echo 'unknown')"
|
|
66
|
+
local session_id="${CLAUDE_SESSION_ID:-unknown}"
|
|
67
|
+
local marker="$dir/${hook}.marker"
|
|
68
|
+
local tmp="${marker}.tmp.$$"
|
|
69
|
+
|
|
70
|
+
{
|
|
71
|
+
printf 'hook: %s\n' "$hook"
|
|
72
|
+
printf 'severity: %s\n' "$severity"
|
|
73
|
+
printf 'fired_at: %s\n' "$ts"
|
|
74
|
+
printf 'session: %s\n' "$session_id"
|
|
75
|
+
printf '%s\n' '---'
|
|
76
|
+
printf '%s%s\n' "$message" "$truncated"
|
|
77
|
+
} > "$tmp" 2>/dev/null || {
|
|
78
|
+
echo "record_last_block: write to '$tmp' failed — marker not written" >&2
|
|
79
|
+
rm -f "$tmp" 2>/dev/null
|
|
80
|
+
return 0
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if ! mv "$tmp" "$marker" 2>/dev/null; then
|
|
84
|
+
echo "record_last_block: rename to '$marker' failed — marker not written" >&2
|
|
85
|
+
rm -f "$tmp" 2>/dev/null
|
|
86
|
+
return 0
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
return 0
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# read_last_block [<hook>]
|
|
93
|
+
# Prints a marker's banner. With a hook arg, reads that marker. With no
|
|
94
|
+
# args, reads the newest marker by mtime. Returns 1 when no marker found.
|
|
95
|
+
read_last_block() {
|
|
96
|
+
local dir
|
|
97
|
+
dir="$(_last_block_dir)"
|
|
98
|
+
|
|
99
|
+
if [ ! -d "$dir" ]; then
|
|
100
|
+
echo "no block recorded (no markers directory at $dir)" >&2
|
|
101
|
+
return 1
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
local marker
|
|
105
|
+
if [ $# -ge 1 ] && [ -n "$1" ]; then
|
|
106
|
+
marker="$dir/${1}.marker"
|
|
107
|
+
if [ ! -f "$marker" ]; then
|
|
108
|
+
echo "no block recorded for '$1'" >&2
|
|
109
|
+
return 1
|
|
110
|
+
fi
|
|
111
|
+
else
|
|
112
|
+
# Newest by mtime. ls -t is intentional (sort-by-mtime is its purpose).
|
|
113
|
+
# shellcheck disable=SC2012
|
|
114
|
+
marker="$(ls -t "$dir"/*.marker 2>/dev/null | head -1)"
|
|
115
|
+
if [ -z "$marker" ]; then
|
|
116
|
+
echo "no block recorded this session or prior" >&2
|
|
117
|
+
return 1
|
|
118
|
+
fi
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
cat "$marker"
|
|
122
|
+
return 0
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# list_last_blocks
|
|
126
|
+
# Lists every marker newest first — one line per marker with hook name,
|
|
127
|
+
# severity, timestamp. Returns 1 when the directory is empty or absent.
|
|
128
|
+
list_last_blocks() {
|
|
129
|
+
local dir
|
|
130
|
+
dir="$(_last_block_dir)"
|
|
131
|
+
|
|
132
|
+
if [ ! -d "$dir" ]; then
|
|
133
|
+
echo "no blocks recorded (no markers directory at $dir)" >&2
|
|
134
|
+
return 1
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
local markers
|
|
138
|
+
# shellcheck disable=SC2012
|
|
139
|
+
markers="$(ls -t "$dir"/*.marker 2>/dev/null)"
|
|
140
|
+
if [ -z "$markers" ]; then
|
|
141
|
+
echo "no blocks recorded (directory empty)" >&2
|
|
142
|
+
return 1
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
local m hook severity fired_at
|
|
146
|
+
echo "$markers" | while IFS= read -r m; do
|
|
147
|
+
hook="$(awk -F': ' '/^hook:/{print $2; exit}' "$m" 2>/dev/null)"
|
|
148
|
+
severity="$(awk -F': ' '/^severity:/{print $2; exit}' "$m" 2>/dev/null)"
|
|
149
|
+
fired_at="$(awk -F': ' '/^fired_at:/{print $2; exit}' "$m" 2>/dev/null)"
|
|
150
|
+
printf '%s\t%s\t%s\n' "$hook" "$severity" "$fired_at"
|
|
151
|
+
done
|
|
152
|
+
return 0
|
|
153
|
+
}
|
package/dist/lite/lib/state.sh
CHANGED
|
@@ -1090,6 +1090,17 @@ state_reference_get() {
|
|
|
1090
1090
|
marker)
|
|
1091
1091
|
state_marker_get "$id"
|
|
1092
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
|
+
;;
|
|
1093
1104
|
gate)
|
|
1094
1105
|
# Gates derive from Markers; not addressable directly in v0
|
|
1095
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
|
-
|
|
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
|
-
|
|
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,76 @@ 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
|
+
|
|
37
107
|
### cli#305 — identifier-leak scrub self-exclude + commit author check (v1.15.3 → v1.15.4, 2026-10-04)
|
|
38
108
|
|
|
39
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.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"tier": "lite",
|
|
3
|
-
"manifest_version": "1.
|
|
4
|
-
"generated_at": "2026-10-
|
|
3
|
+
"manifest_version": "1.16.10",
|
|
4
|
+
"generated_at": "2026-10-06T20:37:31Z",
|
|
5
5
|
"entries": [
|
|
6
6
|
{
|
|
7
7
|
"slug": "029-release-pipeline",
|
|
@@ -315,12 +315,20 @@
|
|
|
315
315
|
"content_hash": "sha256:84ad2a7a53280369506a584d96085acc143a592d50fc9dd6ca70fe9ca9f6eaef",
|
|
316
316
|
"description": "97-install-class-dispatch.sh — fragment that sources the install-class"
|
|
317
317
|
},
|
|
318
|
+
{
|
|
319
|
+
"slug": "97-last-block-count",
|
|
320
|
+
"type": "hook",
|
|
321
|
+
"path": ".claude/hooks/session-reflection.d/97-last-block-count.sh",
|
|
322
|
+
"tier": "lite",
|
|
323
|
+
"content_hash": "sha256:850263f8cb92f2a42758f577ea7e06dce990d4c0b8d8d3a4a2cbc8fd6497a2fa",
|
|
324
|
+
"description": "Session-reflection fragment — surface last-block marker count."
|
|
325
|
+
},
|
|
318
326
|
{
|
|
319
327
|
"slug": "artifact-ingestion-gate",
|
|
320
328
|
"type": "hook",
|
|
321
329
|
"path": ".claude/hooks/artifact-ingestion-gate.sh",
|
|
322
330
|
"tier": "lite",
|
|
323
|
-
"content_hash": "sha256:
|
|
331
|
+
"content_hash": "sha256:1d317b210ee902be6e60d47b94db5c265b24932e21d09623650a154cb5bda443",
|
|
324
332
|
"description": "Artifact Ingestion Gate — fires on Write/Edit to generative artifact paths."
|
|
325
333
|
},
|
|
326
334
|
{
|
|
@@ -592,7 +600,7 @@
|
|
|
592
600
|
"type": "hook",
|
|
593
601
|
"path": ".claude/hooks/save-state.sh",
|
|
594
602
|
"tier": "lite",
|
|
595
|
-
"content_hash": "sha256:
|
|
603
|
+
"content_hash": "sha256:f2eb5009b9550ec235090021da1b46ab1fd9fe582bda56068ec16759bf5e7185",
|
|
596
604
|
"description": "Save State — graceful checkpoint of all work in progress."
|
|
597
605
|
},
|
|
598
606
|
{
|
|
@@ -608,7 +616,7 @@
|
|
|
608
616
|
"type": "hook",
|
|
609
617
|
"path": ".claude/hooks/session-reflection.sh",
|
|
610
618
|
"tier": "lite",
|
|
611
|
-
"content_hash": "sha256:
|
|
619
|
+
"content_hash": "sha256:bd3c0fd948b150d9d94cd3dd60b3e288d20b8c58ebc11de98025371331026950",
|
|
612
620
|
"description": "Session Start hook coordinator. Injects reflection prompt and iterates"
|
|
613
621
|
},
|
|
614
622
|
{
|
|
@@ -776,7 +784,7 @@
|
|
|
776
784
|
"type": "lib",
|
|
777
785
|
"path": "lib/composer-preflight.sh",
|
|
778
786
|
"tier": "lite",
|
|
779
|
-
"content_hash": "sha256:
|
|
787
|
+
"content_hash": "sha256:6276c6e012837104c8cbe14266319cc5809ab31e4b4bc9c4cad1de8fd19c2f74",
|
|
780
788
|
"description": "lib/composer-preflight.sh — shared pre-flight check for composer skills."
|
|
781
789
|
},
|
|
782
790
|
{
|
|
@@ -851,6 +859,14 @@
|
|
|
851
859
|
"content_hash": "sha256:38148e8de456928b13846c2999377c30ac303cf0369cc7755ecc0ba9a8f899fb",
|
|
852
860
|
"description": "Install-written-paths manifest + accessor lib."
|
|
853
861
|
},
|
|
862
|
+
{
|
|
863
|
+
"slug": "last-block",
|
|
864
|
+
"type": "lib",
|
|
865
|
+
"path": "lib/last-block.sh",
|
|
866
|
+
"tier": "lite",
|
|
867
|
+
"content_hash": "sha256:ebe007f9a6f01f76e3fa30ca8db6da0812bcc8ba0ad22d9d98625cd556c56e15",
|
|
868
|
+
"description": "lib/last-block.sh — persist and read BLOCK banners."
|
|
869
|
+
},
|
|
854
870
|
{
|
|
855
871
|
"slug": "luminary-pick",
|
|
856
872
|
"type": "lib",
|
|
@@ -936,7 +952,7 @@
|
|
|
936
952
|
"type": "lib",
|
|
937
953
|
"path": "lib/state.sh",
|
|
938
954
|
"tier": "lite",
|
|
939
|
-
"content_hash": "sha256:
|
|
955
|
+
"content_hash": "sha256:1bd77e04250aa785363851c0aaf26f594b526cd95e4ec539a6f2d74615afb218",
|
|
940
956
|
"description": "lib/state.sh — typed accessor library over bassclef's state spine."
|
|
941
957
|
},
|
|
942
958
|
{
|
|
@@ -1671,6 +1687,13 @@
|
|
|
1671
1687
|
"content_hash": "sha256:bcee28ea68d61b635e0a6880eee6d92d2b04ecfc8cf7eeef4a726b48dd831115",
|
|
1672
1688
|
"description": "When Orchestrator (or any agent) delegates a Construction stack to a subagent (Builder, Designer, Architect), the brief MUST include, verbatim and with file paths, every artifact…"
|
|
1673
1689
|
},
|
|
1690
|
+
{
|
|
1691
|
+
"slug": "last-block-marker",
|
|
1692
|
+
"type": "rule",
|
|
1693
|
+
"path": ".claude/rules/last-block-marker.md",
|
|
1694
|
+
"tier": "lite",
|
|
1695
|
+
"content_hash": "sha256:e9148cb0ad5a179097719f19263beaabaa125e2952f16425406da6d1039901e8"
|
|
1696
|
+
},
|
|
1674
1697
|
{
|
|
1675
1698
|
"slug": "lite-manifest-schema-change-discipline",
|
|
1676
1699
|
"type": "rule",
|
|
@@ -2092,7 +2115,7 @@
|
|
|
2092
2115
|
"type": "script",
|
|
2093
2116
|
"path": "scripts/interpret-input.sh",
|
|
2094
2117
|
"tier": "lite",
|
|
2095
|
-
"content_hash": "sha256:
|
|
2118
|
+
"content_hash": "sha256:5faa7d7139471053d0a76883ceef0b39754326e489726aad7673421a3cf16f88",
|
|
2096
2119
|
"description": "scripts/interpret-input.sh — input-handler CLI wrapper."
|
|
2097
2120
|
},
|
|
2098
2121
|
{
|
|
@@ -2249,7 +2272,7 @@
|
|
|
2249
2272
|
"type": "skill",
|
|
2250
2273
|
"path": ".claude/skills/build/SKILL.md",
|
|
2251
2274
|
"tier": "lite",
|
|
2252
|
-
"content_hash": "sha256:
|
|
2275
|
+
"content_hash": "sha256:739d409886adc42c0111d7fc21d6290ede7d4403d5b51a1050efd3799c9eb1c2",
|
|
2253
2276
|
"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.",
|
|
2254
2277
|
"inputs": [
|
|
2255
2278
|
"A goal slug pointing to a spec on disk"
|
|
@@ -2503,7 +2526,7 @@
|
|
|
2503
2526
|
"type": "skill",
|
|
2504
2527
|
"path": ".claude/skills/ia-model/SKILL.md",
|
|
2505
2528
|
"tier": "lite",
|
|
2506
|
-
"content_hash": "sha256:
|
|
2529
|
+
"content_hash": "sha256:35ad02ec8931ce235c23b62f103d41b0edd3cecf88d2d25e6e01530c98d5302b",
|
|
2507
2530
|
"description": "Build information architecture from an entity inventory plus task flows. Derives nav structure, labeling, and screen map from the domain nouns.",
|
|
2508
2531
|
"inputs": [
|
|
2509
2532
|
"Task flows from spec",
|
|
@@ -2522,7 +2545,7 @@
|
|
|
2522
2545
|
"type": "skill",
|
|
2523
2546
|
"path": ".claude/skills/interaction-design/SKILL.md",
|
|
2524
2547
|
"tier": "lite",
|
|
2525
|
-
"content_hash": "sha256:
|
|
2548
|
+
"content_hash": "sha256:9382a33e8f35fb04c77ed89916abfacf018ab6f1fe2f8e42e59b2547fe0b8eec",
|
|
2526
2549
|
"description": "Turn IA plus task flows into interaction design — state diagrams, sequence diagrams, user flows. Final step in the UX translation chain before decompose.",
|
|
2527
2550
|
"inputs": [
|
|
2528
2551
|
"IA model plus task flows"
|
|
@@ -2597,7 +2620,7 @@
|
|
|
2597
2620
|
"type": "skill",
|
|
2598
2621
|
"path": ".claude/skills/jtbd-tasks/SKILL.md",
|
|
2599
2622
|
"tier": "lite",
|
|
2600
|
-
"content_hash": "sha256:
|
|
2623
|
+
"content_hash": "sha256:7982dbf7ff7da9a3c85ca0cc6d406abbbbc1cbcdef29d7d7eea1c938cdaa2f7f",
|
|
2601
2624
|
"description": "Translate Jobs-to-be-Done into task analysis — HTA trees plus CTA decision points. First step in the UX translation chain.",
|
|
2602
2625
|
"inputs": [
|
|
2603
2626
|
"A JTBD statement or persona ref"
|
|
@@ -2632,12 +2655,20 @@
|
|
|
2632
2655
|
"problem": "Scope grows. Prose fills with jargon. Adopters read a wall of bassclef-internal words and give up.",
|
|
2633
2656
|
"value": "Two modes — scope compresses what ships. Words rewrites vocabulary to grade 8. Plain English at the wire."
|
|
2634
2657
|
},
|
|
2658
|
+
{
|
|
2659
|
+
"slug": "last-block",
|
|
2660
|
+
"type": "skill",
|
|
2661
|
+
"path": ".claude/skills/last-block/SKILL.md",
|
|
2662
|
+
"tier": "lite",
|
|
2663
|
+
"content_hash": "sha256:bc7868b20c5a3e9a53461ef741d2e7799de3b5a20595fd272c59ea04817a8518",
|
|
2664
|
+
"description": "Print the exact text of a BLOCK or guardrail banner you saw this session or prior. No args shows the newest. One hook name shows that hook. --list enumerates every stored banner newest first."
|
|
2665
|
+
},
|
|
2635
2666
|
{
|
|
2636
2667
|
"slug": "launch",
|
|
2637
2668
|
"type": "skill",
|
|
2638
2669
|
"path": ".claude/skills/launch/SKILL.md",
|
|
2639
2670
|
"tier": "lite",
|
|
2640
|
-
"content_hash": "sha256:
|
|
2671
|
+
"content_hash": "sha256:2a950b16e8ef4cdfe2f7f418855c66f5566c48b392dd6e950f83a087669fcd6b",
|
|
2641
2672
|
"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.",
|
|
2642
2673
|
"inputs": [
|
|
2643
2674
|
"A rough idea or ticket or canvas"
|
|
@@ -2722,7 +2753,7 @@
|
|
|
2722
2753
|
"type": "skill",
|
|
2723
2754
|
"path": ".claude/skills/objectory-decompose/SKILL.md",
|
|
2724
2755
|
"tier": "lite",
|
|
2725
|
-
"content_hash": "sha256:
|
|
2756
|
+
"content_hash": "sha256:d1174e8877d3b0d67801dcce3458952c6c556f7b4eee717bf11fd9a513b71ce6",
|
|
2726
2757
|
"description": "Apply Jacobson's analysis method to an InputArtifact. Extracts entities (domain nouns), actors (verb subjects), verb-goal pairs (use case candidates). Classifies objects per BCE (Boundary / Control / Entity). Output at docs/decompositions/.",
|
|
2727
2758
|
"inputs": [
|
|
2728
2759
|
"An InputArtifact from /interpret-input"
|
|
@@ -3385,7 +3416,7 @@
|
|
|
3385
3416
|
"type": "skill",
|
|
3386
3417
|
"path": ".claude/skills/use-case/SKILL.md",
|
|
3387
3418
|
"tier": "lite",
|
|
3388
|
-
"content_hash": "sha256:
|
|
3419
|
+
"content_hash": "sha256:43859ba8c958dbc281cc244616898c1f0f2547f99a789724632f0a4293cc7cf6",
|
|
3389
3420
|
"description": "Produce Cockburn-style use cases — main success scenario, numbered extensions, preconditions, postconditions, and stakeholders. Closes the gap between /user-stories and /spec. Distinct from /task-scenarios (UX narrative).",
|
|
3390
3421
|
"inputs": [
|
|
3391
3422
|
"A feature or persona-scoped scope"
|
|
@@ -3840,7 +3871,7 @@
|
|
|
3840
3871
|
"type": "standard",
|
|
3841
3872
|
"path": "standards/lite-manifest-schema-changes.md",
|
|
3842
3873
|
"tier": "lite",
|
|
3843
|
-
"content_hash": "sha256:
|
|
3874
|
+
"content_hash": "sha256:94cb2a396222954fed8bc29bbf13dd379c46ae49b983f8d546a83adbc89b1285",
|
|
3844
3875
|
"description": "Chronological ledger of every lite-manifest schema change. Adopters read this before sync to know what shifted."
|
|
3845
3876
|
},
|
|
3846
3877
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thebassclef/lite",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.11",
|
|
4
4
|
"description": "Bassclef CLI — install and upgrade bassclef in your project with two commands.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bassclef",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"LICENSE"
|
|
43
43
|
],
|
|
44
44
|
"bassclef": {
|
|
45
|
-
"upstream_tag": "
|
|
45
|
+
"upstream_tag": "v1.8.0",
|
|
46
46
|
"wiring_manifest_schema_major": 2,
|
|
47
47
|
"bundle_paths": [
|
|
48
48
|
"dist/lite/"
|