@thebassclef/lite 1.0.0 → 1.0.2
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/dist/cli.cjs +240 -23
- package/dist/cli.js +242 -25
- 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 +357 -0
- package/dist/lite/.claude/hooks/assert-verify-steering.sh +77 -0
- package/dist/lite/.claude/hooks/bassclef-source-config-validate.sh +215 -0
- package/dist/lite/.claude/hooks/bassclef-sync.sh +716 -0
- package/dist/lite/.claude/hooks/compound-noun-scrub.sh +292 -0
- package/dist/lite/.claude/hooks/kiss-expansion-inject.sh +69 -0
- package/dist/lite/.claude/hooks/longrun-prep-compounding-sequence-check.sh +492 -0
- package/dist/lite/.claude/hooks/plain-english-steering.sh +156 -0
- package/dist/lite/.claude/hooks/post-skill-friction-check.sh +177 -0
- package/dist/lite/.claude/hooks/post-skill-telemetry.sh +62 -0
- package/dist/lite/.claude/hooks/pre-build-gate.sh +511 -0
- package/dist/lite/.claude/hooks/pre-commit-gate.sh +451 -0
- package/dist/lite/.claude/hooks/session-end.sh +433 -0
- package/dist/lite/.claude/hooks/session-reflection.sh +303 -0
- package/dist/lite/.claude/hooks/skill-body-grade-gate.sh +219 -0
- package/dist/lite/.claude/hooks/skill-body-intent-drift.sh +107 -0
- package/dist/lite/.claude/hooks/state-validate.sh +271 -0
- package/dist/lite/.claude/hooks/substrate-clarity-gate.sh +1110 -0
- package/dist/lite/.claude/hooks/temperance-gate.sh +147 -0
- package/dist/lite/.claude/hooks/testing-tier-enforce.sh +233 -0
- package/dist/lite/.claude/hooks/turn-prose-grade-measure.sh +219 -0
- package/dist/lite/.claude/hooks/turn-prose-kiss-check.sh +463 -0
- package/dist/lite/.claude/hooks/vocabulary-migration-check.sh +171 -0
- package/dist/lite/.claude/hooks/whereami-utc-gate.sh +142 -0
- package/dist/lite/CLAUDE.md +2 -2
- package/dist/lite/whereami.md +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
# Pre-Build Gate — fires on first Edit/Write per session.
|
|
6
|
+
# Requires visible SDLC evidence before writing code.
|
|
7
|
+
# Loads relevant design diagrams contextually based on file being edited.
|
|
8
|
+
|
|
9
|
+
# === Hook liveness heartbeat (WU-3 of bet 2026-07-31d; closes #1002) ===
|
|
10
|
+
# Silent-fail — a missing lib never crashes the hook. Discipline: ADR-048 +
|
|
11
|
+
# standards/hook-cadence.json.
|
|
12
|
+
{
|
|
13
|
+
_hb_sd="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
+
for _hb_c in "${_hb_sd}/../../lib/hook-heartbeat.sh" "${HOME:-/}/lib/hook-heartbeat.sh"; do
|
|
15
|
+
[ -f "$_hb_c" ] && source "$_hb_c" && heartbeat_mark "pre-build-gate" && break
|
|
16
|
+
done
|
|
17
|
+
unset _hb_sd _hb_c
|
|
18
|
+
} 2>/dev/null || true
|
|
19
|
+
|
|
20
|
+
INPUT=$(cat)
|
|
21
|
+
# Per bassclef#1125: fail-soft on jq parse failure (malformed stdin JSON
|
|
22
|
+
# returns jq exit 5; set -e would crash the hook).
|
|
23
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"' 2>/dev/null || echo "unknown")
|
|
24
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // "unknown"' 2>/dev/null || echo "unknown")
|
|
25
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""' 2>/dev/null || echo "")
|
|
26
|
+
CWD=$(echo "$INPUT" | jq -r '.cwd // "."' 2>/dev/null || echo ".")
|
|
27
|
+
|
|
28
|
+
# === Substrate-defect path detection (bassclef#277) ===
|
|
29
|
+
# Paths where a fix proposal ripples to every adopter via /release.
|
|
30
|
+
# These paths MUST NOT be skipped — the luminary gate (below) enforces
|
|
31
|
+
# /temperance + /luminary discipline on this class of work per ADR-031.
|
|
32
|
+
SUBSTRATE_DEFECT_PATH=0
|
|
33
|
+
if echo "$FILE_PATH" | grep -qE '\.claude/hooks/.*\.sh$|\.claude/skills/.*/SKILL\.md$|\.claude/rules/.*\.md$|\.claude/luminaries/.*\.md$|lib/state\.sh$|scripts/migrate-adopter-references\.sh$|scripts/bassclef-orphan-parking\.sh$|scripts/bassclef-migrate-summary\.sh$|presence/install/.*\.sh$|standards/state-spine/schemas/.*\.schema\.json$|bassclef-sync\.sh$'; then
|
|
34
|
+
SUBSTRATE_DEFECT_PATH=1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# Skip for non-source files (hooks, config, docs, memory, github workflows)
|
|
38
|
+
# UNLESS this is a substrate-defect path — those always run the gate.
|
|
39
|
+
if [ "$SUBSTRATE_DEFECT_PATH" -eq 0 ] && echo "$FILE_PATH" | grep -qE '\.(sh|json)$|/\.claude/|/memory/|/docs/|/\.github/|CLAUDE\.md|MEMORY\.md'; then
|
|
40
|
+
exit 0
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Trace log
|
|
44
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
45
|
+
source "$SCRIPT_DIR/trace-helper.sh"
|
|
46
|
+
trace_log "pre-build" "$TOOL_NAME $FILE_PATH"
|
|
47
|
+
|
|
48
|
+
# --- Feature branch enforcement ---
|
|
49
|
+
CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
|
|
50
|
+
if [ "$CURRENT_BRANCH" = "main" ] || [ "$CURRENT_BRANCH" = "master" ]; then
|
|
51
|
+
echo ""
|
|
52
|
+
echo "============================================"
|
|
53
|
+
echo "WRITING TO MAIN BRANCH — CREATE A FEATURE BRANCH"
|
|
54
|
+
echo "============================================"
|
|
55
|
+
echo ""
|
|
56
|
+
echo "You are editing source code directly on main."
|
|
57
|
+
echo "All work should happen on a feature branch:"
|
|
58
|
+
echo " git checkout -b feature/ISSUE-NNN-description"
|
|
59
|
+
echo "============================================"
|
|
60
|
+
echo ""
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# --- Durable marker convention (dual-read: /tmp flag OR git-tracked marker) ---
|
|
64
|
+
# Per .claude/rules/mobile-ephemeral-session.md §2 and
|
|
65
|
+
# standards/deferred-actions-schema.md. Either form satisfies the gate;
|
|
66
|
+
# durable form lets mobile firings cross sandbox boundaries.
|
|
67
|
+
BRANCH_SLUG=$(echo "$CURRENT_BRANCH" | tr '/' '-')
|
|
68
|
+
DURABLE_DIAG_MARKER="$CWD/state/markers/diagnose/${BRANCH_SLUG}.marker"
|
|
69
|
+
DURABLE_TEMP_MARKER="$CWD/state/markers/temperance/${BRANCH_SLUG}.marker"
|
|
70
|
+
|
|
71
|
+
# --- DIAGNOSIS GATE (fix branches MUST show diagnosis) ---
|
|
72
|
+
if echo "$CURRENT_BRANCH" | grep -qiE '^fix/'; then
|
|
73
|
+
DIAG_FLAG="/tmp/claude-diagnosis-done-${SESSION_ID}-$(echo "$CURRENT_BRANCH" | md5 -q 2>/dev/null || echo "$CURRENT_BRANCH" | md5sum 2>/dev/null | cut -d' ' -f1)"
|
|
74
|
+
if [ ! -f "$DIAG_FLAG" ] && [ ! -f "$DURABLE_DIAG_MARKER" ]; then
|
|
75
|
+
# Per bassclef#1060: BLOCK (exit 2), not advise. Sister fix to
|
|
76
|
+
# the temperance block; both gates were advisory which is the
|
|
77
|
+
# same shape as bassclef#1027 turn-prose (methodology-only fails).
|
|
78
|
+
if [ "${SKIP_DIAGNOSE:-0}" = "1" ]; then
|
|
79
|
+
echo "" >&2
|
|
80
|
+
echo "DIAGNOSIS — skipped via SKIP_DIAGNOSE=1 (logged)" >&2
|
|
81
|
+
[ -f "$SCRIPT_DIR/trace-helper.sh" ] && \
|
|
82
|
+
bash -c "source $SCRIPT_DIR/trace-helper.sh && trace_log diagnose-skip override 2>/dev/null" || true
|
|
83
|
+
else
|
|
84
|
+
cat >&2 <<EOF
|
|
85
|
+
|
|
86
|
+
🛑 DIAGNOSIS — BLOCK (fix/ branch with no diagnosis evidence)
|
|
87
|
+
|
|
88
|
+
You are on a fix/ branch but have NOT run /diagnose.
|
|
89
|
+
|
|
90
|
+
Before writing ANY code:
|
|
91
|
+
|
|
92
|
+
0. Reproduce — exact command / URL / action that triggers the failure
|
|
93
|
+
(skip only for typo / missing env var / off-by-one — state why)
|
|
94
|
+
1. Is / Is Not table — what fails? what similar thing works?
|
|
95
|
+
2. Five Whys — trace to root cause, not symptom
|
|
96
|
+
3. Hypothesis — one sentence, with a falsifiable test
|
|
97
|
+
|
|
98
|
+
Run /diagnose. When done, write the marker:
|
|
99
|
+
touch state/markers/diagnose/${BRANCH_SLUG}.marker
|
|
100
|
+
|
|
101
|
+
Override per-call: SKIP_DIAGNOSE=1 <command> (logged via trace-helper)
|
|
102
|
+
|
|
103
|
+
The POA incident (2026-04-05) showed agents jumping straight to code
|
|
104
|
+
without understanding the problem. This gate prevents that.
|
|
105
|
+
|
|
106
|
+
EOF
|
|
107
|
+
exit 2
|
|
108
|
+
fi
|
|
109
|
+
fi
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
# --- TEMPERANCE GATE (scope-decision boundaries; per .claude/rules/sdlc-gates.md) ---
|
|
113
|
+
# In stacked-PR work, branches are pre-shaped in a stack manifest. The
|
|
114
|
+
# scope decision was made at manifest-write time. Re-firing per-branch
|
|
115
|
+
# is ceremony, not gate. When the current branch is declared in any
|
|
116
|
+
# stack manifest, suppress per-branch temperance — phase-level firing
|
|
117
|
+
# (session kickoff, phase boundary, post-compaction, scope-drift) is
|
|
118
|
+
# the load-bearing trigger. Per bassclef#272.
|
|
119
|
+
|
|
120
|
+
TEMP_FLAG="/tmp/claude-temperance-done-${SESSION_ID}-$(echo "$CURRENT_BRANCH" | md5 -q 2>/dev/null || echo "$CURRENT_BRANCH" | md5sum 2>/dev/null | cut -d' ' -f1)"
|
|
121
|
+
|
|
122
|
+
# Detect: is current branch declared in any stack manifest?
|
|
123
|
+
BRANCH_IN_STACK=0
|
|
124
|
+
STACK_MANIFEST=""
|
|
125
|
+
if [ "$CURRENT_BRANCH" != "main" ] && [ "$CURRENT_BRANCH" != "master" ] && [ "$CURRENT_BRANCH" != "unknown" ]; then
|
|
126
|
+
for stack_dir in "$CWD/docs/stacks" "$CWD/docs/branch-stacks"; do
|
|
127
|
+
if [ -d "$stack_dir" ]; then
|
|
128
|
+
MATCH=$(grep -lF "$CURRENT_BRANCH" "$stack_dir"/*.md 2>/dev/null | head -1)
|
|
129
|
+
if [ -n "$MATCH" ]; then
|
|
130
|
+
BRANCH_IN_STACK=1
|
|
131
|
+
STACK_MANIFEST="$MATCH"
|
|
132
|
+
break
|
|
133
|
+
fi
|
|
134
|
+
fi
|
|
135
|
+
done
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
if [ "$BRANCH_IN_STACK" -eq 1 ]; then
|
|
139
|
+
# Suppress: branch is pre-shaped in stack manifest. Phase-level firing covers this.
|
|
140
|
+
if [ ! -f "$DURABLE_TEMP_MARKER" ]; then
|
|
141
|
+
echo ""
|
|
142
|
+
echo "============================================"
|
|
143
|
+
echo "TEMPERANCE — suppressed (stacked branch)"
|
|
144
|
+
echo "============================================"
|
|
145
|
+
echo ""
|
|
146
|
+
echo "Branch '$CURRENT_BRANCH' is declared in:"
|
|
147
|
+
echo " $STACK_MANIFEST"
|
|
148
|
+
echo ""
|
|
149
|
+
echo "Scope decision was made at manifest-write time."
|
|
150
|
+
echo "Per .claude/rules/sdlc-gates.md, temperance fires at"
|
|
151
|
+
echo "scope-decision boundaries (session kickoff, phase boundary,"
|
|
152
|
+
echo "post-compaction, scope-drift), not per-branch."
|
|
153
|
+
echo "============================================"
|
|
154
|
+
echo ""
|
|
155
|
+
# Auto-write the durable marker so /verify and other consumers see it.
|
|
156
|
+
mkdir -p "$(dirname "$DURABLE_TEMP_MARKER")" 2>/dev/null
|
|
157
|
+
echo "auto-suppressed-stacked $(date -u +"%Y-%m-%dT%H:%M:%SZ")" > "$DURABLE_TEMP_MARKER" 2>/dev/null || true
|
|
158
|
+
touch "$TEMP_FLAG" 2>/dev/null || true
|
|
159
|
+
fi
|
|
160
|
+
elif [ ! -f "$TEMP_FLAG" ] && [ ! -f "$DURABLE_TEMP_MARKER" ]; then
|
|
161
|
+
# Non-stacked work — fire the standard per-branch gate.
|
|
162
|
+
# Per bassclef#1060: BLOCK (exit 2), not advise. Methodology-only
|
|
163
|
+
# enforcement has 0% adoption — last 15 PRs shipped without markers.
|
|
164
|
+
# Override via SKIP_TEMPERANCE=1 env (logged to trace) for genuine cases.
|
|
165
|
+
if [ "${SKIP_TEMPERANCE:-0}" = "1" ]; then
|
|
166
|
+
echo "" >&2
|
|
167
|
+
echo "TEMPERANCE — skipped via SKIP_TEMPERANCE=1 (logged)" >&2
|
|
168
|
+
[ -f "$SCRIPT_DIR/trace-helper.sh" ] && \
|
|
169
|
+
bash -c "source $SCRIPT_DIR/trace-helper.sh && trace_log temperance-skip override 2>/dev/null" || true
|
|
170
|
+
else
|
|
171
|
+
cat >&2 <<EOF
|
|
172
|
+
|
|
173
|
+
🛑 TEMPERANCE — BLOCK (mandatory pause before first edit)
|
|
174
|
+
|
|
175
|
+
Before your first edit on this branch, STOP and answer:
|
|
176
|
+
|
|
177
|
+
0. RIGHT THING? Does this trace to a real need?
|
|
178
|
+
1. SIMPLEST? Is this the simplest correct approach?
|
|
179
|
+
2. BRUTE FORCE? Am I guessing instead of understanding?
|
|
180
|
+
3. BLAST RADIUS? What else could this break?
|
|
181
|
+
4. VERIFICATION? How will I know this works?
|
|
182
|
+
5. REVERSIBLE? If wrong, how hard to undo?
|
|
183
|
+
|
|
184
|
+
State your approach in ONE sentence, then write the marker:
|
|
185
|
+
touch state/markers/temperance/${BRANCH_SLUG}.marker
|
|
186
|
+
|
|
187
|
+
This gate fires once per branch (non-stacked work).
|
|
188
|
+
Stacked branches are pre-shaped in a manifest — the gate suppresses.
|
|
189
|
+
Override per-call: SKIP_TEMPERANCE=1 <command> (logged via trace-helper)
|
|
190
|
+
|
|
191
|
+
EOF
|
|
192
|
+
exit 2
|
|
193
|
+
fi
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
# --- MARKER ENRICHMENT (V1 advisory; per bassclef-upstream#438 + bet 28a WU-5) ---
|
|
197
|
+
# Per .claude/rules/marker-enrichment-discipline.md: gate-marker files
|
|
198
|
+
# should carry body content (date + branch + decision/lens/thread context),
|
|
199
|
+
# not just exist as empty touch files. V1 advisory; flips to BLOCK in V2
|
|
200
|
+
# after the allowlist baseline is cleared.
|
|
201
|
+
MARKER_ENRICHMENT_ALLOWLIST="$CWD/.claude/hooks/marker-enrichment-allowlist.txt"
|
|
202
|
+
check_marker_enriched() {
|
|
203
|
+
local marker_path="$1"
|
|
204
|
+
local marker_class="$2" # temperance / luminary / arc-walk / orientation-gate / roadmap-reconcile-gate
|
|
205
|
+
[ "${SKIP_MARKER_ENRICHMENT:-0}" = "1" ] && return 0
|
|
206
|
+
[ ! -f "$marker_path" ] && return 0
|
|
207
|
+
[ -s "$marker_path" ] && return 0
|
|
208
|
+
# Empty marker. Check allowlist.
|
|
209
|
+
if [ -f "$MARKER_ENRICHMENT_ALLOWLIST" ]; then
|
|
210
|
+
local relative_path="${marker_path#$CWD/}"
|
|
211
|
+
if grep -qxF "$relative_path" "$MARKER_ENRICHMENT_ALLOWLIST" 2>/dev/null; then
|
|
212
|
+
return 0 # grandfathered
|
|
213
|
+
fi
|
|
214
|
+
fi
|
|
215
|
+
# Empty marker, not in allowlist — ADVISORY warn (V1 doesn't block)
|
|
216
|
+
echo "" >&2
|
|
217
|
+
echo "⚠️ MARKER ENRICHMENT — ADVISORY (V1; per .claude/rules/marker-enrichment-discipline.md)" >&2
|
|
218
|
+
echo "" >&2
|
|
219
|
+
echo " $marker_class marker is empty: $marker_path" >&2
|
|
220
|
+
echo " Per bet 28a WU-5: gate markers should carry body content" >&2
|
|
221
|
+
echo " (date + branch + decision/lens/thread context)." >&2
|
|
222
|
+
echo "" >&2
|
|
223
|
+
echo " Add body via heredoc in the same WU/PR. V2 will flip to BLOCK." >&2
|
|
224
|
+
echo "" >&2
|
|
225
|
+
}
|
|
226
|
+
# Check the marker that just cleared the temperance gate
|
|
227
|
+
[ -f "$DURABLE_TEMP_MARKER" ] && check_marker_enriched "$DURABLE_TEMP_MARKER" "temperance"
|
|
228
|
+
DURABLE_LUMINARY_MARKER_EARLY="$CWD/state/markers/luminary/${BRANCH_SLUG}.marker"
|
|
229
|
+
[ -f "$DURABLE_LUMINARY_MARKER_EARLY" ] && check_marker_enriched "$DURABLE_LUMINARY_MARKER_EARLY" "luminary"
|
|
230
|
+
DURABLE_ARC_WALK_MARKER="$CWD/state/markers/arc-walk/${BRANCH_SLUG}.marker"
|
|
231
|
+
[ -f "$DURABLE_ARC_WALK_MARKER" ] && check_marker_enriched "$DURABLE_ARC_WALK_MARKER" "arc-walk"
|
|
232
|
+
|
|
233
|
+
# --- LUMINARY GATE (substrate-defect paths require BOTH temperance + luminary markers) ---
|
|
234
|
+
# Per bassclef#277. Substrate-defect fixes ripple to every adopter via
|
|
235
|
+
# /release. Methodology-only failed — operator asked 3 times in one
|
|
236
|
+
# session for /temperance + /luminary before fix proposals. This gate
|
|
237
|
+
# enforces the discipline at the mechanical layer per memory
|
|
238
|
+
# feedback_hooks_beat_methodology_for_substrate_quality.
|
|
239
|
+
#
|
|
240
|
+
# Primary lens for the gate's own design: Hyrum Wright — every adopter
|
|
241
|
+
# branch that lacks a luminary marker at the moment this gate ships
|
|
242
|
+
# would break under strict enforcement. The grandfather allowlist
|
|
243
|
+
# (.claude/hooks/luminary-gate-allowlist.txt) gives existing branches
|
|
244
|
+
# a deprecation window per ADR-031.
|
|
245
|
+
|
|
246
|
+
if [ "$SUBSTRATE_DEFECT_PATH" -eq 1 ]; then
|
|
247
|
+
DURABLE_LUMINARY_MARKER="$CWD/state/markers/luminary/${BRANCH_SLUG}.marker"
|
|
248
|
+
ALLOWLIST="$CWD/.claude/hooks/luminary-gate-allowlist.txt"
|
|
249
|
+
|
|
250
|
+
# Hyrum grandfather window — branches matching any line in the
|
|
251
|
+
# allowlist bypass the gate during the deprecation window. Lines
|
|
252
|
+
# support shell glob patterns (feature/*, fix/*, bet-*, etc.) via
|
|
253
|
+
# bash's case/esac matching. Lines starting with # are ignored.
|
|
254
|
+
# Exact branch names also work — they're a degenerate-case glob.
|
|
255
|
+
IN_ALLOWLIST=0
|
|
256
|
+
if [ -f "$ALLOWLIST" ]; then
|
|
257
|
+
while IFS= read -r pattern || [ -n "$pattern" ]; do
|
|
258
|
+
# Skip comments and blank lines
|
|
259
|
+
case "$pattern" in
|
|
260
|
+
''|\#*) continue ;;
|
|
261
|
+
esac
|
|
262
|
+
# Trim trailing whitespace defensively
|
|
263
|
+
pattern="${pattern%"${pattern##*[![:space:]]}"}"
|
|
264
|
+
# Glob match against current branch
|
|
265
|
+
case "$CURRENT_BRANCH" in
|
|
266
|
+
$pattern)
|
|
267
|
+
IN_ALLOWLIST=1
|
|
268
|
+
break
|
|
269
|
+
;;
|
|
270
|
+
esac
|
|
271
|
+
done < "$ALLOWLIST"
|
|
272
|
+
fi
|
|
273
|
+
|
|
274
|
+
if [ "$IN_ALLOWLIST" -eq 0 ] && [ ! -f "$DURABLE_LUMINARY_MARKER" ]; then
|
|
275
|
+
if [ "${SKIP_LUMINARY_GATE:-0}" = "1" ]; then
|
|
276
|
+
echo "" >&2
|
|
277
|
+
echo "LUMINARY GATE — skipped via SKIP_LUMINARY_GATE=1 (logged)" >&2
|
|
278
|
+
[ -f "$SCRIPT_DIR/trace-helper.sh" ] && \
|
|
279
|
+
bash -c "source $SCRIPT_DIR/trace-helper.sh && trace_log luminary-gate-skip override 2>/dev/null" || true
|
|
280
|
+
else
|
|
281
|
+
cat >&2 <<EOF
|
|
282
|
+
|
|
283
|
+
🛑 LUMINARY GATE — BLOCK (substrate-defect path without luminary marker)
|
|
284
|
+
|
|
285
|
+
You are editing a substrate path: $FILE_PATH
|
|
286
|
+
|
|
287
|
+
Substrate-defect fixes ripple to every adopter via /release. Per
|
|
288
|
+
ADR-031 (we don't break adopters), every substrate-defect fix needs
|
|
289
|
+
BOTH /temperance AND /luminary applied before code lands.
|
|
290
|
+
|
|
291
|
+
/temperance marker — found.
|
|
292
|
+
/luminary marker — missing at state/markers/luminary/${BRANCH_SLUG}.marker
|
|
293
|
+
|
|
294
|
+
Before writing code at this path:
|
|
295
|
+
|
|
296
|
+
1. /luminary --primary — which lenses pin this work?
|
|
297
|
+
Common pins: Hyrum (api-stability), Linus (adopter-compat),
|
|
298
|
+
Beck (TDD red-green), Hoare (pre/post conditions),
|
|
299
|
+
Ousterhout (deep modules), Saltzer-Schroeder (defensive bash).
|
|
300
|
+
|
|
301
|
+
2. Write the marker:
|
|
302
|
+
mkdir -p state/markers/luminary
|
|
303
|
+
cat > state/markers/luminary/${BRANCH_SLUG}.marker <<'MARKER'
|
|
304
|
+
session: <session-id>
|
|
305
|
+
branch: ${CURRENT_BRANCH}
|
|
306
|
+
primary_lens: <luminary slug>
|
|
307
|
+
compatibility_anchor: <luminary slug>
|
|
308
|
+
reason: <one line>
|
|
309
|
+
MARKER
|
|
310
|
+
|
|
311
|
+
Override per-call: SKIP_LUMINARY_GATE=1 <command> (logged via trace-helper)
|
|
312
|
+
|
|
313
|
+
Hyrum grandfather: add branch to .claude/hooks/luminary-gate-allowlist.txt
|
|
314
|
+
for deprecation-window bypass. The allowlist exists so this gate landing
|
|
315
|
+
does not break in-flight branches that pre-date the discipline.
|
|
316
|
+
|
|
317
|
+
Source incident: operator asked 3 times in one session (2026-06-22) for
|
|
318
|
+
/temperance + /luminary before substrate-defect fix proposals. Methodology
|
|
319
|
+
alone (rules + memory) did not hold. Filed as bassclef#277.
|
|
320
|
+
|
|
321
|
+
EOF
|
|
322
|
+
exit 2
|
|
323
|
+
fi
|
|
324
|
+
fi
|
|
325
|
+
fi
|
|
326
|
+
|
|
327
|
+
# --- Budget awareness (cost governance) ---
|
|
328
|
+
MANIFEST="$CWD/substrate.config.md"
|
|
329
|
+
if [ -f "$MANIFEST" ]; then
|
|
330
|
+
# Prefer accessor (state_iteration_bet_active returns frontmatter as JSON);
|
|
331
|
+
# fall back to grep for repos pre-spine-v0 OR for bets that store turn_limit
|
|
332
|
+
# in body markdown rather than frontmatter.
|
|
333
|
+
TURN_LIMIT=""
|
|
334
|
+
if [ -f "$CWD/lib/state.sh" ]; then
|
|
335
|
+
# shellcheck source=/dev/null
|
|
336
|
+
source "$CWD/lib/state.sh" 2>/dev/null && \
|
|
337
|
+
TURN_LIMIT=$(state_iteration_bet_active 2>/dev/null | \
|
|
338
|
+
jq -r '.appetite.turn_limit // .turn_limit // empty' 2>/dev/null)
|
|
339
|
+
fi
|
|
340
|
+
if [ -z "$TURN_LIMIT" ]; then
|
|
341
|
+
TURN_LIMIT=$(grep 'turn_limit:' "$CWD/docs/iteration-bets/"*.md 2>/dev/null | tail -1 | grep -oE '[0-9]+' | head -1)
|
|
342
|
+
fi
|
|
343
|
+
if [ -n "$TURN_LIMIT" ]; then
|
|
344
|
+
# Count edits this session as a proxy for turns consumed.
|
|
345
|
+
# Dual-write: /tmp (existing) + durable (mobile-ephemeral-session.md §2).
|
|
346
|
+
EDIT_FLAG="/tmp/claude-edit-count-${SESSION_ID}"
|
|
347
|
+
DURABLE_EDIT_DIR="$CWD/state/markers/edit-count"
|
|
348
|
+
DURABLE_EDIT_FLAG="$DURABLE_EDIT_DIR/${SESSION_ID}.count"
|
|
349
|
+
EDIT_COUNT=0
|
|
350
|
+
if [ -f "$DURABLE_EDIT_FLAG" ]; then
|
|
351
|
+
EDIT_COUNT=$(cat "$DURABLE_EDIT_FLAG")
|
|
352
|
+
elif [ -f "$EDIT_FLAG" ]; then
|
|
353
|
+
EDIT_COUNT=$(cat "$EDIT_FLAG")
|
|
354
|
+
fi
|
|
355
|
+
EDIT_COUNT=$((EDIT_COUNT + 1))
|
|
356
|
+
echo "$EDIT_COUNT" > "$EDIT_FLAG"
|
|
357
|
+
mkdir -p "$DURABLE_EDIT_DIR" 2>/dev/null
|
|
358
|
+
echo "$EDIT_COUNT" > "$DURABLE_EDIT_FLAG" 2>/dev/null
|
|
359
|
+
|
|
360
|
+
THRESHOLD=$((TURN_LIMIT * 75 / 100))
|
|
361
|
+
if [ "$EDIT_COUNT" -ge "$TURN_LIMIT" ]; then
|
|
362
|
+
echo ""
|
|
363
|
+
echo "============================================"
|
|
364
|
+
echo "BUDGET: TURN LIMIT REACHED ($EDIT_COUNT / $TURN_LIMIT)"
|
|
365
|
+
echo "============================================"
|
|
366
|
+
echo "Pause. Report state. Propose descoped continuation."
|
|
367
|
+
echo "============================================"
|
|
368
|
+
echo ""
|
|
369
|
+
elif [ "$EDIT_COUNT" -ge "$THRESHOLD" ]; then
|
|
370
|
+
echo ""
|
|
371
|
+
echo "BUDGET: 75% of turn limit ($EDIT_COUNT / $TURN_LIMIT)"
|
|
372
|
+
echo ""
|
|
373
|
+
fi
|
|
374
|
+
fi
|
|
375
|
+
fi
|
|
376
|
+
|
|
377
|
+
# Fire full gate first time per session, lightweight reminder on subsequent edits
|
|
378
|
+
FLAG_FILE="/tmp/claude-pre-build-gate-${SESSION_ID}"
|
|
379
|
+
DOCS_DIR="$CWD/docs/design"
|
|
380
|
+
|
|
381
|
+
if [ -f "$FLAG_FILE" ]; then
|
|
382
|
+
# Subsequent edit — lightweight per-task reminder
|
|
383
|
+
cat <<'PERTASK'
|
|
384
|
+
============================================
|
|
385
|
+
PRE-BUILD — PER-TASK CHECK
|
|
386
|
+
============================================
|
|
387
|
+
|
|
388
|
+
Before this edit, confirm:
|
|
389
|
+
- Building against: [ticket # or "trivial — logged in commit"]
|
|
390
|
+
- Temperance: Simplest correct approach? Brute-forcing?
|
|
391
|
+
- UI surface impacted? If yes, is IA/state diagram current?
|
|
392
|
+
- Env vars needed? Set?
|
|
393
|
+
============================================
|
|
394
|
+
PERTASK
|
|
395
|
+
|
|
396
|
+
else
|
|
397
|
+
# First edit this session — full SDLC checkpoint
|
|
398
|
+
touch "$FLAG_FILE"
|
|
399
|
+
|
|
400
|
+
cat <<'GATE'
|
|
401
|
+
============================================
|
|
402
|
+
PRE-BUILD GATE — MANDATORY BEFORE WRITING CODE
|
|
403
|
+
============================================
|
|
404
|
+
|
|
405
|
+
You are about to write/edit source code. STOP.
|
|
406
|
+
|
|
407
|
+
YOUR RESPONSE must contain ALL of the following sections as visible text
|
|
408
|
+
BEFORE this Edit/Write and any subsequent code changes:
|
|
409
|
+
|
|
410
|
+
## SDLC Checkpoint
|
|
411
|
+
- Building against: [ticket #NNN — title, or "no ticket" with justification]
|
|
412
|
+
- Spec current?: [yes/no — if no, what needs updating first?]
|
|
413
|
+
- Design diagrams: [which need updating and why, or "none — because X"]
|
|
414
|
+
- Engineering principles: [any SoC/abstraction/cohesion concerns? or "clean"]
|
|
415
|
+
|
|
416
|
+
## Environment Check
|
|
417
|
+
- External credentials needed?: [OAuth, API keys, tokens, etc.]
|
|
418
|
+
- Are they set?: [check NOW — if empty, flag blocker before writing code]
|
|
419
|
+
|
|
420
|
+
## Requirements Check
|
|
421
|
+
- New requirements surfaced?: [yes → where logged | no]
|
|
422
|
+
- Untracked work?: [am I coding something not on any ticket? if yes, log it first]
|
|
423
|
+
|
|
424
|
+
## Test Plan
|
|
425
|
+
- Testable logic?: [yes/no — pure functions, utilities, parsers = yes]
|
|
426
|
+
- Test approach: [which test file will cover this? new or existing?]
|
|
427
|
+
|
|
428
|
+
If this is a bug fix, you must ALSO have completed the Bug Diagnosis
|
|
429
|
+
(Is/Is Not + Five Whys) before proceeding.
|
|
430
|
+
|
|
431
|
+
Only after writing these sections may you proceed to write code.
|
|
432
|
+
============================================
|
|
433
|
+
GATE
|
|
434
|
+
fi
|
|
435
|
+
|
|
436
|
+
# === Contextual diagram loading ===
|
|
437
|
+
echo ""
|
|
438
|
+
echo "=== RELEVANT DESIGN DIAGRAMS (verify against these) ==="
|
|
439
|
+
echo ""
|
|
440
|
+
|
|
441
|
+
LOADED=0
|
|
442
|
+
|
|
443
|
+
# Schema / Prisma / migration → load ERD
|
|
444
|
+
if echo "$FILE_PATH" | grep -qiE 'prisma|schema|model|migration'; then
|
|
445
|
+
[ -f "$DOCS_DIR/erd.md" ] && { echo "### ERD (triggered by: schema/model file)"; head -80 "$DOCS_DIR/erd.md" 2>/dev/null; echo ""; LOADED=1; }
|
|
446
|
+
fi
|
|
447
|
+
|
|
448
|
+
# Queue / worker / agent → load Sequence + Component diagrams
|
|
449
|
+
if echo "$FILE_PATH" | grep -qiE 'worker|queue|agent|job|bull|cron|ingest'; then
|
|
450
|
+
[ -f "$DOCS_DIR/sequences/"*.md ] 2>/dev/null && { echo "### Sequence diagrams (triggered by: worker/agent file)"; ls "$DOCS_DIR/sequences/"*.md 2>/dev/null; echo ""; LOADED=1; }
|
|
451
|
+
[ -f "$DOCS_DIR/component-diagram.md" ] && { echo "### Component Diagram"; head -60 "$DOCS_DIR/component-diagram.md" 2>/dev/null; echo ""; LOADED=1; }
|
|
452
|
+
fi
|
|
453
|
+
|
|
454
|
+
# Docker / deploy / infra → load Component + Deployment diagrams
|
|
455
|
+
if echo "$FILE_PATH" | grep -qiE 'docker|deploy|infra|compose|Dockerfile|workflow'; then
|
|
456
|
+
[ -f "$DOCS_DIR/component-diagram.md" ] && { echo "### Component Diagram (triggered by: infra file)"; head -60 "$DOCS_DIR/component-diagram.md" 2>/dev/null; echo ""; LOADED=1; }
|
|
457
|
+
fi
|
|
458
|
+
|
|
459
|
+
# Auth / user / role → load Use Case diagram
|
|
460
|
+
if echo "$FILE_PATH" | grep -qiE 'auth|user|role|permission|session'; then
|
|
461
|
+
[ -f "$DOCS_DIR/use-cases.md" ] && { echo "### Use Cases (triggered by: auth/role file)"; head -60 "$DOCS_DIR/use-cases.md" 2>/dev/null; echo ""; LOADED=1; }
|
|
462
|
+
fi
|
|
463
|
+
|
|
464
|
+
# Page route → UX check + E2E reminder
|
|
465
|
+
if echo "$FILE_PATH" | grep -qiE 'src/app/.*/page\.tsx'; then
|
|
466
|
+
echo "### UX Check (triggered by: page route file)"
|
|
467
|
+
echo "- Does a state diagram exist for this page?"
|
|
468
|
+
echo "- Is this page in the entity model / screen map?"
|
|
469
|
+
echo "- Are components named after domain objects?"
|
|
470
|
+
echo "- Run /ia-model if navigation or screen structure is changing."
|
|
471
|
+
echo ""
|
|
472
|
+
echo "### E2E Smoke Test"
|
|
473
|
+
echo "User-facing pages MUST be covered by E2E smoke tests."
|
|
474
|
+
echo ""
|
|
475
|
+
LOADED=1
|
|
476
|
+
fi
|
|
477
|
+
|
|
478
|
+
# API route → E2E reminder
|
|
479
|
+
if echo "$FILE_PATH" | grep -qiE 'src/app/api/.*/route\.ts'; then
|
|
480
|
+
echo "### E2E Smoke Test (triggered by: API route file)"
|
|
481
|
+
echo "API routes MUST be covered by E2E smoke tests."
|
|
482
|
+
echo "- Protected route: add auth gate test"
|
|
483
|
+
echo "- Public route: add response shape test"
|
|
484
|
+
echo ""
|
|
485
|
+
LOADED=1
|
|
486
|
+
fi
|
|
487
|
+
|
|
488
|
+
# Schema change → entity model + migration reminder
|
|
489
|
+
if echo "$FILE_PATH" | grep -qiE 'prisma/schema'; then
|
|
490
|
+
echo "### Entity Model Check (triggered by: schema file)"
|
|
491
|
+
echo "- Has the entity been added to the object model?"
|
|
492
|
+
echo "- Does this entity need a UI surface?"
|
|
493
|
+
echo "- Run /ia-model if adding a new entity."
|
|
494
|
+
echo ""
|
|
495
|
+
echo "### MIGRATION REQUIRED"
|
|
496
|
+
echo "After editing schema, run: npx prisma migrate dev --name descriptive_name"
|
|
497
|
+
echo "NEVER use prisma db push. It causes schema drift."
|
|
498
|
+
echo ""
|
|
499
|
+
LOADED=1
|
|
500
|
+
fi
|
|
501
|
+
|
|
502
|
+
if [ "$LOADED" -eq 0 ]; then
|
|
503
|
+
echo "(No diagram matched: $FILE_PATH)"
|
|
504
|
+
echo "If this change introduces new objects, async flows, or lifecycle changes,"
|
|
505
|
+
echo "manually read the relevant diagram from docs/design/ before proceeding."
|
|
506
|
+
fi
|
|
507
|
+
|
|
508
|
+
echo ""
|
|
509
|
+
echo "=== END DIAGRAMS ==="
|
|
510
|
+
|
|
511
|
+
exit 0
|