@thebassclef/lite 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +23 -6
- package/dist/cli.js +23 -6
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lite/.claude/bassclef-configs.jsonc +56 -0
- package/dist/lite/.claude/bassclef-orientation.md +120 -0
- package/dist/lite/.claude/hooks/adr-discipline-check.sh +132 -0
- package/dist/lite/.claude/hooks/atomic-pr-check.sh +169 -0
- package/dist/lite/.claude/hooks/auto-save-idle.sh +51 -0
- package/dist/lite/.claude/hooks/bassclef-sync.sh +51 -13
- package/dist/lite/.claude/hooks/bet-doc-gate.sh +283 -0
- package/dist/lite/.claude/hooks/bug-diagnosis.sh +64 -0
- package/dist/lite/.claude/hooks/destructive-command-guard.sh +136 -0
- package/dist/lite/.claude/hooks/file-rename-discipline-check.sh +147 -0
- package/dist/lite/.claude/hooks/hook-classifier-canary.sh +158 -0
- package/dist/lite/.claude/hooks/kiss-default-check.sh +129 -0
- package/dist/lite/.claude/hooks/multi-tenancy-check.sh +83 -0
- package/dist/lite/.claude/hooks/oo-ad-entry-point-check.sh +216 -0
- package/dist/lite/.claude/hooks/operator-private-path-check.sh +203 -0
- package/dist/lite/.claude/hooks/pattern-annotation-validate.sh +142 -0
- package/dist/lite/.claude/hooks/plain-english-steering.sh +15 -1
- package/dist/lite/.claude/hooks/post-write-trace.sh +66 -0
- package/dist/lite/.claude/hooks/pr-body-scrub-check.sh +446 -0
- package/dist/lite/.claude/hooks/pre-commit-identifier-leak-scrub.sh +137 -0
- package/dist/lite/.claude/hooks/pre-gh-pr-body-scrub.sh +192 -0
- package/dist/lite/.claude/hooks/pre-gh-pr-title-scrub.sh +172 -0
- package/dist/lite/.claude/hooks/role-entry-gate.sh +115 -0
- package/dist/lite/.claude/hooks/save-state.sh +268 -0
- package/dist/lite/.claude/hooks/session-reflection.d/08-settings-drift.sh +8 -1
- package/dist/lite/.claude/hooks/session-reflection.d/10-abrupt-stop-recovery.sh +52 -0
- package/dist/lite/.claude/hooks/session-reflection.d/55-orientation-gate.sh +40 -4
- package/dist/lite/.claude/hooks/session-reflection.d/80-hook-heartbeat-check.sh +12 -5
- package/dist/lite/.claude/hooks/session-reflection.d/81-hook-manifest-staleness.sh +18 -2
- package/dist/lite/.claude/hooks/session-reflection.d/91-npm-version-drift.sh +54 -0
- package/dist/lite/.claude/hooks/session-reflection.d/tests/10-abrupt-stop-recovery.test.sh +95 -0
- package/dist/lite/.claude/hooks/session-reflection.sh +62 -15
- package/dist/lite/.claude/hooks/tool-failure-diagnosis.sh +50 -0
- package/dist/lite/.claude/luminaries/andrej-karpathy.md +165 -0
- package/dist/lite/.claude/luminaries/dave-gray.md +155 -0
- package/dist/lite/.claude/luminaries/gary-klein.md +166 -0
- package/dist/lite/.claude/luminaries/kaoru-ishikawa.md +162 -0
- package/dist/lite/.claude/luminaries/lean-product-canvas.md +33 -0
- package/dist/lite/.claude/luminaries/luke-wroblewski.md +224 -0
- package/dist/lite/.claude/luminaries/ryan-singer.md +113 -0
- package/dist/lite/.claude/luminaries/steve-krug.md +168 -0
- package/dist/lite/.claude/luminaries/tony-ulwick.md +134 -0
- package/dist/lite/.claude/rules/arc-level-orientation-before-leaf.md +60 -0
- package/dist/lite/.claude/rules/architect-review-discipline.md +71 -0
- package/dist/lite/.claude/rules/auto-fix-criteria.md +126 -0
- package/dist/lite/.claude/rules/bassclef-hook-drift-blocker.md +144 -0
- package/dist/lite/.claude/rules/bassclef-models-discovery.md +107 -0
- package/dist/lite/.claude/rules/bassclef-workflow-parity.md +104 -0
- package/dist/lite/.claude/rules/cost-governance.md +154 -0
- package/dist/lite/.claude/rules/file-rename-discipline.md +95 -0
- package/dist/lite/.claude/rules/mechanism-fidelity.md +88 -0
- package/dist/lite/.claude/rules/prototype-production-coupling.md +125 -0
- package/dist/lite/.claude/rules/quick-fix-criteria.md +90 -0
- package/dist/lite/.claude/rules/session-artifacts.md +30 -0
- package/dist/lite/.claude/rules/sibling-smoke-after-substrate-change.md +107 -0
- package/dist/lite/.claude/rules/substrate-as-system.md +67 -0
- package/dist/lite/.claude/rules/usability.md +22 -0
- package/dist/lite/.claude/settings.json +20 -0
- package/dist/lite/.claude/skills/agent-research-spawn/SKILL.md +100 -0
- package/dist/lite/.claude/skills/architect-review/SKILL.md +201 -0
- package/dist/lite/.claude/skills/auto-fix/SKILL.md +297 -0
- package/dist/lite/.claude/skills/autonomous/SKILL.md +300 -0
- package/dist/lite/.claude/skills/bind-subdomain/SKILL.md +232 -0
- package/dist/lite/.claude/skills/canvas/SKILL.md +41 -0
- package/dist/lite/.claude/skills/deploy-prod/SKILL.md +106 -0
- package/dist/lite/.claude/skills/docs-sync/SKILL.md +188 -0
- package/dist/lite/.claude/skills/eisenhower/SKILL.md +273 -0
- package/dist/lite/.claude/skills/empathy-map/SKILL.md +168 -0
- package/dist/lite/.claude/skills/frontend-design/SKILL.md +53 -0
- package/dist/lite/.claude/skills/interview-me/SKILL.md +171 -0
- package/dist/lite/.claude/skills/journal-export/SKILL.md +293 -0
- package/dist/lite/.claude/skills/jtbd-tasks/SKILL.md +203 -0
- package/dist/lite/.claude/skills/objectory-decompose/SKILL.md +256 -0
- package/dist/lite/.claude/skills/pick-luminaries/SKILL.md +100 -0
- package/dist/lite/.claude/skills/pre-mortem/SKILL.md +180 -0
- package/dist/lite/.claude/skills/prototype-gallery/SKILL.md +264 -0
- package/dist/lite/.claude/skills/provision-deploy-host/SKILL.md +257 -0
- package/dist/lite/.claude/skills/quick-fix/SKILL.md +273 -0
- package/dist/lite/.claude/skills/release/SKILL.md +311 -0
- package/dist/lite/.claude/skills/release-notes/SKILL.md +202 -0
- package/dist/lite/.claude/skills/rfc/SKILL.md +222 -0
- package/dist/lite/.claude/skills/riff-prototypes/SKILL.md +637 -0
- package/dist/lite/.claude/skills/secrets-bootstrap/SKILL.md +209 -0
- package/dist/lite/.claude/skills/skill/SKILL.md +304 -0
- package/dist/lite/.claude/skills/smoke-test/SKILL.md +100 -0
- package/dist/lite/.claude/skills/state/SKILL.md +204 -0
- package/dist/lite/.claude/skills/substrate-check/SKILL.md +218 -0
- package/dist/lite/.claude/skills/synthetic-user/SKILL.md +381 -0
- package/dist/lite/.claude/skills/task-scenarios/SKILL.md +105 -0
- package/dist/lite/.claude/skills/tier-dependency-audit/SKILL.md +139 -0
- package/dist/lite/.claude/skills/ux-migration/SKILL.md +424 -0
- package/dist/lite/.claude/skills/value-prop-canvas/SKILL.md +297 -0
- package/dist/lite/README.md +5 -1
- package/dist/lite/gitignore +6 -0
- package/dist/lite/lib/bassclef-dir-resolver.sh +85 -0
- package/dist/lite/lib/clone-failure-classifier.sh +59 -0
- package/dist/lite/lib/fixture-builder.sh +190 -0
- package/dist/lite/lib/fresh-install-check.sh +49 -0
- package/dist/lite/lib/hook-heartbeat.sh +0 -1
- package/dist/lite/lib/mechanism-fidelity.sh +569 -0
- package/dist/lite/lib/npm-version-check.sh +112 -0
- package/dist/lite/lib/preview-state.sh +229 -0
- package/dist/lite/lib/session-rescue.sh +0 -1
- package/dist/lite/scripts/generate-lite-manifest.sh +145 -4
- package/dist/lite/scripts/lite-manifest-drift-check.sh +5 -1
- package/dist/lite/standards/architect-review-discipline.md +222 -0
- package/dist/lite/standards/bassclef-platform.md +249 -0
- package/dist/lite/standards/bassclef-test-repos.md +97 -0
- package/dist/lite/standards/bassclef-wiring-manifest.json +25 -1
- package/dist/lite/standards/cold-adopter-harness.md +205 -0
- package/dist/lite/standards/data-management.md +135 -0
- package/dist/lite/standards/frontend-stack/nextjs-prisma.md +124 -0
- package/dist/lite/standards/lite-manifest-schema-changes.md +27 -0
- package/dist/lite/standards/lite-manifest.json +1456 -14
- package/dist/lite/standards/mechanism-fidelity.md +205 -0
- package/dist/lite/standards/operator-private-discipline.md +200 -0
- package/dist/lite/standards/operator-to-public-flow.md +195 -0
- package/dist/lite/standards/prototype-gallery.md +251 -0
- package/dist/lite/standards/pseudonym-key-schema.md +109 -0
- package/dist/lite/standards/sibling-validation-checklist.md +181 -0
- package/dist/lite/standards/slack-notifications.md +161 -0
- package/dist/lite/standards/synthetic-user-testing.md +173 -0
- package/dist/lite/standards/vendor-bindings.md +165 -0
- package/dist/lite/standards/vendor-plugins/frontend-design.md +66 -0
- package/dist/lite/standards/vpc-fit-validation.md +167 -0
- package/dist/lite/templates/gallery-yml.md +113 -0
- package/dist/lite/templates/product-canvas.md +169 -0
- package/dist/lite/templates/value-prop-canvas.md +180 -0
- package/package.json +3 -3
- /package/dist/lite/{whereami.md → docs/whereami.md} +0 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# oo-ad-entry-point-check.sh — PreToolUse Edit/Write hook mechanizing .claude/rules/oo-ad-entry-point.md.
|
|
4
|
+
#
|
|
5
|
+
# Fires on Write to a new file matching the rule's globs. Requires:
|
|
6
|
+
# 1. A decomposition marker at state/markers/decompose/<branch>.marker
|
|
7
|
+
# OR any file under docs/decompositions/
|
|
8
|
+
# 2. A use-case artifact under docs/use-cases/ matching the code class
|
|
9
|
+
#
|
|
10
|
+
# Per bassclef-upstream#1169 + canvas docs/canvases/2026-08-25-ooad-chain-as-substrate-offering.md.
|
|
11
|
+
# Composes with .claude/hooks/adr-discipline-check.sh (sister hook on architectural boundaries).
|
|
12
|
+
# Override: SKIP_OO_AD_ENTRY_POINT=1 (logged via trace-helper).
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
# ============================================================================
|
|
17
|
+
# Read stdin JSON from Claude Code harness
|
|
18
|
+
# ============================================================================
|
|
19
|
+
PAYLOAD=$(cat 2>/dev/null || echo '{}')
|
|
20
|
+
|
|
21
|
+
TOOL_NAME=$(echo "$PAYLOAD" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
22
|
+
FILE_PATH=$(echo "$PAYLOAD" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
23
|
+
|
|
24
|
+
# ============================================================================
|
|
25
|
+
# Filter: only fire on Write / Edit / MultiEdit
|
|
26
|
+
# ============================================================================
|
|
27
|
+
case "$TOOL_NAME" in
|
|
28
|
+
Write|Edit|MultiEdit) ;;
|
|
29
|
+
*) exit 0 ;;
|
|
30
|
+
esac
|
|
31
|
+
|
|
32
|
+
# Defensive precondition — empty file_path
|
|
33
|
+
if [ -z "$FILE_PATH" ]; then
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# ============================================================================
|
|
38
|
+
# Override check
|
|
39
|
+
# ============================================================================
|
|
40
|
+
if [ "${SKIP_OO_AD_ENTRY_POINT:-0}" = "1" ]; then
|
|
41
|
+
TRACE_HELPER="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/trace-helper.sh"
|
|
42
|
+
if [ -f "$TRACE_HELPER" ]; then
|
|
43
|
+
bash "$TRACE_HELPER" override "oo-ad-entry-point-check" "SKIP_OO_AD_ENTRY_POINT=1 on $FILE_PATH" 2>/dev/null || true
|
|
44
|
+
fi
|
|
45
|
+
exit 0
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# ============================================================================
|
|
49
|
+
# Path matcher — classify code class per rule oo-ad-entry-point.md L18-25
|
|
50
|
+
# Bash case globs do NOT expand ** — use per-level cases + src/ recursive fallback.
|
|
51
|
+
# ============================================================================
|
|
52
|
+
CODE_CLASS=""
|
|
53
|
+
case "$FILE_PATH" in
|
|
54
|
+
*/.claude/hooks/*.sh|.claude/hooks/*.sh)
|
|
55
|
+
CODE_CLASS="substrate-hook"
|
|
56
|
+
;;
|
|
57
|
+
*/lib/*.sh|lib/*.sh)
|
|
58
|
+
CODE_CLASS="substrate-lib"
|
|
59
|
+
;;
|
|
60
|
+
*/scripts/*.sh|scripts/*.sh)
|
|
61
|
+
CODE_CLASS="adopter-script"
|
|
62
|
+
;;
|
|
63
|
+
esac
|
|
64
|
+
|
|
65
|
+
# src/ handled via recursive path check — any depth under src/ with .ts/.tsx/.js/.py extension
|
|
66
|
+
if [ -z "$CODE_CLASS" ]; then
|
|
67
|
+
case "$FILE_PATH" in
|
|
68
|
+
src/*|*/src/*)
|
|
69
|
+
case "$FILE_PATH" in
|
|
70
|
+
*.ts|*.tsx|*.js|*.py) CODE_CLASS="adopter-app" ;;
|
|
71
|
+
esac
|
|
72
|
+
;;
|
|
73
|
+
esac
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# Not a matched path → pass through
|
|
77
|
+
if [ -z "$CODE_CLASS" ]; then
|
|
78
|
+
exit 0
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# ============================================================================
|
|
82
|
+
# Cockburn casual tier — existing file Edit passes through
|
|
83
|
+
# ============================================================================
|
|
84
|
+
# Edit / MultiEdit on existing files needs no new artifacts per rule L24.
|
|
85
|
+
# Only new files (Write, OR Edit of missing file) require ceremony.
|
|
86
|
+
REPO_ROOT="${HOOK_TEST_REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
|
|
87
|
+
|
|
88
|
+
# Resolve absolute path — file_path may be relative
|
|
89
|
+
if [ "${FILE_PATH:0:1}" = "/" ]; then
|
|
90
|
+
ABS_PATH="$FILE_PATH"
|
|
91
|
+
else
|
|
92
|
+
ABS_PATH="$REPO_ROOT/$FILE_PATH"
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
if [ -f "$ABS_PATH" ]; then
|
|
96
|
+
exit 0
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# ============================================================================
|
|
100
|
+
# Decomposition check
|
|
101
|
+
# ============================================================================
|
|
102
|
+
BRANCH="${HOOK_TEST_BRANCH:-}"
|
|
103
|
+
if [ -z "$BRANCH" ]; then
|
|
104
|
+
BRANCH=$(cd "$REPO_ROOT" && git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
MARKER_PATH="$REPO_ROOT/state/markers/decompose/${BRANCH}.marker"
|
|
108
|
+
DECOMP_DIR="$REPO_ROOT/docs/decompositions"
|
|
109
|
+
|
|
110
|
+
DECOMP_OK=0
|
|
111
|
+
if [ -f "$MARKER_PATH" ]; then
|
|
112
|
+
DECOMP_OK=1
|
|
113
|
+
elif [ -d "$DECOMP_DIR" ] && find "$DECOMP_DIR" -maxdepth 1 -name '*.md' -type f 2>/dev/null | grep -q .; then
|
|
114
|
+
DECOMP_OK=1
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
# ============================================================================
|
|
118
|
+
# Use case check per code class
|
|
119
|
+
# ============================================================================
|
|
120
|
+
UC_DIR="$REPO_ROOT/docs/use-cases"
|
|
121
|
+
UC_OK=0
|
|
122
|
+
|
|
123
|
+
if [ -d "$UC_DIR" ]; then
|
|
124
|
+
case "$CODE_CLASS" in
|
|
125
|
+
adopter-app)
|
|
126
|
+
# UC-*.md that does NOT start with UC-hook-, UC-lib-, or UC-script-
|
|
127
|
+
if find "$UC_DIR" -maxdepth 1 -name 'UC-*.md' -type f \
|
|
128
|
+
-not -name 'UC-hook-*' -not -name 'UC-lib-*' -not -name 'UC-script-*' \
|
|
129
|
+
2>/dev/null | grep -q .; then
|
|
130
|
+
UC_OK=1
|
|
131
|
+
fi
|
|
132
|
+
;;
|
|
133
|
+
substrate-hook)
|
|
134
|
+
if find "$UC_DIR" -maxdepth 1 -name 'UC-hook-*.md' -type f 2>/dev/null | grep -q .; then
|
|
135
|
+
UC_OK=1
|
|
136
|
+
fi
|
|
137
|
+
;;
|
|
138
|
+
substrate-lib)
|
|
139
|
+
if find "$UC_DIR" -maxdepth 1 -name 'UC-lib-*.md' -type f 2>/dev/null | grep -q .; then
|
|
140
|
+
UC_OK=1
|
|
141
|
+
fi
|
|
142
|
+
;;
|
|
143
|
+
adopter-script)
|
|
144
|
+
if find "$UC_DIR" -maxdepth 1 -name 'UC-script-*.md' -type f 2>/dev/null | grep -q .; then
|
|
145
|
+
UC_OK=1
|
|
146
|
+
fi
|
|
147
|
+
;;
|
|
148
|
+
esac
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
# ============================================================================
|
|
152
|
+
# Pass or block
|
|
153
|
+
# ============================================================================
|
|
154
|
+
if [ "$DECOMP_OK" -eq 1 ] && [ "$UC_OK" -eq 1 ]; then
|
|
155
|
+
exit 0
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
# ============================================================================
|
|
159
|
+
# Block message
|
|
160
|
+
# ============================================================================
|
|
161
|
+
case "$CODE_CLASS" in
|
|
162
|
+
adopter-app)
|
|
163
|
+
CEREMONY="Fully-dressed use case + /decompose + /spec"
|
|
164
|
+
UC_PATTERN="docs/use-cases/UC-<slug>.md"
|
|
165
|
+
;;
|
|
166
|
+
substrate-hook)
|
|
167
|
+
CEREMONY="Brief use case + /decompose"
|
|
168
|
+
UC_PATTERN="docs/use-cases/UC-hook-<slug>.md"
|
|
169
|
+
;;
|
|
170
|
+
substrate-lib)
|
|
171
|
+
CEREMONY="Brief use case + /decompose"
|
|
172
|
+
UC_PATTERN="docs/use-cases/UC-lib-<slug>.md"
|
|
173
|
+
;;
|
|
174
|
+
adopter-script)
|
|
175
|
+
CEREMONY="Brief use case + Tier 0 tests"
|
|
176
|
+
UC_PATTERN="docs/use-cases/UC-script-<slug>.md"
|
|
177
|
+
;;
|
|
178
|
+
esac
|
|
179
|
+
|
|
180
|
+
cat >&2 <<EOF
|
|
181
|
+
🛑 oo-ad-entry-point-check — BLOCK (bassclef-upstream#1169)
|
|
182
|
+
|
|
183
|
+
You are creating a new file in the ${CODE_CLASS} class.
|
|
184
|
+
|
|
185
|
+
Path: ${FILE_PATH}
|
|
186
|
+
Branch: ${BRANCH}
|
|
187
|
+
|
|
188
|
+
The rule .claude/rules/oo-ad-entry-point.md requires design artifacts
|
|
189
|
+
before this file lands. Ceremony for this class:
|
|
190
|
+
${CEREMONY}
|
|
191
|
+
|
|
192
|
+
Missing artifacts:
|
|
193
|
+
$([ "$DECOMP_OK" -eq 0 ] && echo " - Decomposition: marker at ${MARKER_PATH} OR any file under docs/decompositions/")
|
|
194
|
+
$([ "$UC_OK" -eq 0 ] && echo " - Use case: ${UC_PATTERN}")
|
|
195
|
+
|
|
196
|
+
Three resolution paths:
|
|
197
|
+
|
|
198
|
+
1. Run /decompose to produce the decomposition, then write a use case.
|
|
199
|
+
Touch the marker after:
|
|
200
|
+
mkdir -p $(dirname "$MARKER_PATH")
|
|
201
|
+
touch $MARKER_PATH
|
|
202
|
+
|
|
203
|
+
2. Write the artifacts by hand per the rule table (L18-25) and touch
|
|
204
|
+
the marker.
|
|
205
|
+
|
|
206
|
+
3. Override for genuine trivial work:
|
|
207
|
+
SKIP_OO_AD_ENTRY_POINT=1 <command>
|
|
208
|
+
|
|
209
|
+
Then retry the original write.
|
|
210
|
+
|
|
211
|
+
Per .claude/rules/oo-ad-entry-point.md
|
|
212
|
+
Composes with .claude/hooks/adr-discipline-check.sh
|
|
213
|
+
Cockburn ceremony tiering — Writing Effective Use Cases (2000)
|
|
214
|
+
EOF
|
|
215
|
+
|
|
216
|
+
exit 2
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# Operator-private path enforcement — Rule 3 of bassclef#739
|
|
4
|
+
# Per bassclef#769 (canvas-path discipline) + bassclef#803 (persona-review extension).
|
|
5
|
+
#
|
|
6
|
+
# Checks: persona-review-shaped + canvas-shaped artifacts must live under
|
|
7
|
+
# docs/operator-private/<artifact-type>/. Public-tree placement is explicit
|
|
8
|
+
# operator request via PR label `bassclef-internal-prose-ok` (shared with
|
|
9
|
+
# Rule 1 + Rule 4) OR commit footer `SKIP_OPERATOR_PRIVATE_PATH=1`.
|
|
10
|
+
#
|
|
11
|
+
# Shape patterns flagged outside docs/operator-private/:
|
|
12
|
+
#
|
|
13
|
+
# 1. Persona-review-shaped (any of):
|
|
14
|
+
# - Path matches docs/persona-reviews/**
|
|
15
|
+
# - Filename matches *-review*.md OR *-persona-review*.md
|
|
16
|
+
# AND located outside docs/operator-private/persona-reviews/
|
|
17
|
+
#
|
|
18
|
+
# 2. Canvas-shaped (any of):
|
|
19
|
+
# - Path matches docs/strategy/canvases/** AND filename contains canvas-related
|
|
20
|
+
# keywords (lean-canvas, value-prop-canvas, chain-of-beliefs)
|
|
21
|
+
# - Filename matches *-canvas.md OR *-chain-of-beliefs.md
|
|
22
|
+
# AND located outside docs/operator-private/canvases/
|
|
23
|
+
# AND not under templates/ or presence/install/ (scaffolds + adopter assets are public)
|
|
24
|
+
#
|
|
25
|
+
# Usage:
|
|
26
|
+
# bash operator-private-path-check.sh <changed-files-list>
|
|
27
|
+
#
|
|
28
|
+
# <changed-files-list> is a newline-separated file of changed paths
|
|
29
|
+
# (from `git diff --name-only`).
|
|
30
|
+
#
|
|
31
|
+
# Optional env:
|
|
32
|
+
# PR_LABELS=label1,label2,... — labels on the PR
|
|
33
|
+
# STRICT_MODE=1 — force blocking even with override label
|
|
34
|
+
#
|
|
35
|
+
# Exit codes:
|
|
36
|
+
# 0 — pass (no violations, OR violations + override label present)
|
|
37
|
+
# 1 — block (violations + no override)
|
|
38
|
+
# 2 — usage error
|
|
39
|
+
|
|
40
|
+
set +e
|
|
41
|
+
|
|
42
|
+
CHANGED_FILES="${1:-}"
|
|
43
|
+
if [ -z "$CHANGED_FILES" ] || [ ! -f "$CHANGED_FILES" ]; then
|
|
44
|
+
echo "usage: $0 <changed-files-list>" >&2
|
|
45
|
+
exit 2
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
PR_LABELS="${PR_LABELS:-}"
|
|
49
|
+
STRICT_MODE="${STRICT_MODE:-0}"
|
|
50
|
+
|
|
51
|
+
LABELS_NORMALIZED=$(echo "$PR_LABELS" | tr ',' '\n' | sed 's/^ *//;s/ *$//' | grep -v '^$' || true)
|
|
52
|
+
|
|
53
|
+
HAS_OVERRIDE=0
|
|
54
|
+
if [ "$STRICT_MODE" != "1" ]; then
|
|
55
|
+
if echo "$LABELS_NORMALIZED" | grep -qx "bassclef-internal-prose-ok"; then
|
|
56
|
+
HAS_OVERRIDE=1
|
|
57
|
+
fi
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
PERSONA_REVIEW_VIOLATIONS=""
|
|
61
|
+
CANVAS_VIOLATIONS=""
|
|
62
|
+
PR_COUNT=0
|
|
63
|
+
CV_COUNT=0
|
|
64
|
+
|
|
65
|
+
while IFS= read -r path; do
|
|
66
|
+
[ -z "$path" ] && continue
|
|
67
|
+
|
|
68
|
+
# Skip files inside the bassclef operator-private tree
|
|
69
|
+
case "$path" in
|
|
70
|
+
docs/operator-private/*) continue ;;
|
|
71
|
+
esac
|
|
72
|
+
|
|
73
|
+
# Skip templates + distribution assets (public; used as scaffolding sources)
|
|
74
|
+
# Per bassclef-upstream#317 reorg: templates/ + presence/install/ replaced
|
|
75
|
+
# strategy/templates/ as public-scaffold homes.
|
|
76
|
+
case "$path" in
|
|
77
|
+
templates/*) continue ;;
|
|
78
|
+
presence/install/*) continue ;;
|
|
79
|
+
esac
|
|
80
|
+
|
|
81
|
+
# Skip itself + the hook test fixtures
|
|
82
|
+
case "$path" in
|
|
83
|
+
.claude/hooks/operator-private-path-check.sh) continue ;;
|
|
84
|
+
.claude/hooks/tests/operator-private-path-check.test.sh) continue ;;
|
|
85
|
+
esac
|
|
86
|
+
|
|
87
|
+
# Skip Feathers rollback snapshots of rule bodies under docs/rule-history/
|
|
88
|
+
# These are byte-identical copies of rule files; filenames match source
|
|
89
|
+
# rule names (e.g., architect-review-discipline.md) which false-positive
|
|
90
|
+
# on the persona-review regex. Landed via bassclef-upstream PR #1649.
|
|
91
|
+
case "$path" in
|
|
92
|
+
docs/rule-history/*) continue ;;
|
|
93
|
+
esac
|
|
94
|
+
|
|
95
|
+
# Skip substrate definition directories (#1700 sister). A persona review or
|
|
96
|
+
# a canvas never lives in one of these — they hold rules, luminaries, skills,
|
|
97
|
+
# agents, hooks, standards, libs and scripts, each with its own naming and
|
|
98
|
+
# tier gates. Before this narrowing the check flagged
|
|
99
|
+
# `.claude/rules/architect-review-discipline.md` (matched `-review-[a-z]+`)
|
|
100
|
+
# and `.claude/luminaries/lean-product-canvas.md` (matched `-canvas.md`),
|
|
101
|
+
# neither of which is an operator-private artifact. Named as a deferred
|
|
102
|
+
# follow-on in .claude/rules/bootstrap-pair-discipline.md.
|
|
103
|
+
#
|
|
104
|
+
# The narrowing does not weaken the rule: docs/ and strategy/ paths, where
|
|
105
|
+
# these artifacts actually live, still fire. Pinned by the negative test in
|
|
106
|
+
# .claude/hooks/tests/operator-private-path-check.test.sh.
|
|
107
|
+
case "$path" in
|
|
108
|
+
.claude/rules/*|.claude/luminaries/*|.claude/skills/*|.claude/agents/*|.claude/hooks/*) continue ;;
|
|
109
|
+
standards/*|lib/*|scripts/*) continue ;;
|
|
110
|
+
esac
|
|
111
|
+
|
|
112
|
+
filename=$(basename "$path")
|
|
113
|
+
|
|
114
|
+
# === Rule 3 Shape 1 — persona-review-shaped ===
|
|
115
|
+
case "$path" in
|
|
116
|
+
docs/persona-reviews/*)
|
|
117
|
+
PERSONA_REVIEW_VIOLATIONS="${PERSONA_REVIEW_VIOLATIONS}- \`${path}\` — under \`docs/persona-reviews/\` (legacy path; relocate to \`docs/operator-private/persona-reviews/\`)"$'\n'
|
|
118
|
+
PR_COUNT=$((PR_COUNT + 1))
|
|
119
|
+
continue
|
|
120
|
+
;;
|
|
121
|
+
esac
|
|
122
|
+
|
|
123
|
+
# Filename pattern check for persona-review (case-insensitive)
|
|
124
|
+
if echo "$filename" | grep -qiE '(^|[-_])(persona-review|persona_review)|.*-review-[a-z]+\.md$|persona-review.*\.md$'; then
|
|
125
|
+
PERSONA_REVIEW_VIOLATIONS="${PERSONA_REVIEW_VIOLATIONS}- \`${path}\` — filename pattern matches persona-review (relocate to \`docs/operator-private/persona-reviews/\`)"$'\n'
|
|
126
|
+
PR_COUNT=$((PR_COUNT + 1))
|
|
127
|
+
continue
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
# === Rule 3 Shape 2 — canvas-shaped ===
|
|
131
|
+
# Filename pattern check for canvas
|
|
132
|
+
if echo "$filename" | grep -qiE '\-lean\-canvas\.md$|\-value\-prop\-canvas\.md$|\-chain\-of\-beliefs\.md$|\-canvas\.md$'; then
|
|
133
|
+
CANVAS_VIOLATIONS="${CANVAS_VIOLATIONS}- \`${path}\` — filename pattern matches canvas-shaped (relocate to \`docs/operator-private/canvases/\`)"$'\n'
|
|
134
|
+
CV_COUNT=$((CV_COUNT + 1))
|
|
135
|
+
continue
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
# Path-based: under strategy/canvases/ but not template/example
|
|
139
|
+
case "$path" in
|
|
140
|
+
strategy/canvases/*.md)
|
|
141
|
+
# Allow strategy/canvases/* only if it's the public-tree exception
|
|
142
|
+
# (verified by operator request via override label). Without override,
|
|
143
|
+
# this is a violation.
|
|
144
|
+
CANVAS_VIOLATIONS="${CANVAS_VIOLATIONS}- \`${path}\` — under \`strategy/canvases/\` (relocate to \`docs/operator-private/canvases/\` unless explicit public-tree placement)"$'\n'
|
|
145
|
+
CV_COUNT=$((CV_COUNT + 1))
|
|
146
|
+
;;
|
|
147
|
+
esac
|
|
148
|
+
|
|
149
|
+
done < "$CHANGED_FILES"
|
|
150
|
+
|
|
151
|
+
TOTAL=$((PR_COUNT + CV_COUNT))
|
|
152
|
+
|
|
153
|
+
if [ "$TOTAL" -eq 0 ]; then
|
|
154
|
+
echo "PASS — no operator-private path violations"
|
|
155
|
+
exit 0
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
echo "==============================================="
|
|
159
|
+
echo "Operator-private path violations (bassclef#739 Rule 3)"
|
|
160
|
+
echo "==============================================="
|
|
161
|
+
echo ""
|
|
162
|
+
|
|
163
|
+
if [ -n "$PERSONA_REVIEW_VIOLATIONS" ]; then
|
|
164
|
+
echo "## Persona-review-shaped artifacts outside operator-private (${PR_COUNT})"
|
|
165
|
+
echo ""
|
|
166
|
+
echo "$PERSONA_REVIEW_VIOLATIONS"
|
|
167
|
+
echo ""
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
if [ -n "$CANVAS_VIOLATIONS" ]; then
|
|
171
|
+
echo "## Canvas-shaped artifacts outside operator-private (${CV_COUNT})"
|
|
172
|
+
echo ""
|
|
173
|
+
echo "$CANVAS_VIOLATIONS"
|
|
174
|
+
echo ""
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
echo "==============================================="
|
|
178
|
+
echo "Why this rule fires:"
|
|
179
|
+
echo ""
|
|
180
|
+
echo "Persona-review-shaped + canvas-shaped artifacts default to"
|
|
181
|
+
echo " docs/operator-private/<artifact-type>/"
|
|
182
|
+
echo "Public-tree placement is explicit operator request, not default."
|
|
183
|
+
echo ""
|
|
184
|
+
echo "Per bassclef#769 (canvas-path discipline) + bassclef#803 (persona-review extension):"
|
|
185
|
+
echo "- Canvases live at: docs/operator-private/canvases/"
|
|
186
|
+
echo "- Persona reviews live at: docs/operator-private/persona-reviews/"
|
|
187
|
+
echo ""
|
|
188
|
+
echo "Resolution paths:"
|
|
189
|
+
echo ""
|
|
190
|
+
echo "1. Relocate the file to docs/operator-private/<artifact-type>/"
|
|
191
|
+
echo " (use git mv to preserve history)"
|
|
192
|
+
echo ""
|
|
193
|
+
echo "2. Add label \`bassclef-internal-prose-ok\` to the PR if public-tree"
|
|
194
|
+
echo " placement is intentional (e.g., template, example, public-facing reference)."
|
|
195
|
+
echo "==============================================="
|
|
196
|
+
|
|
197
|
+
if [ "$HAS_OVERRIDE" -eq 1 ]; then
|
|
198
|
+
echo ""
|
|
199
|
+
echo "ADVISORY — \`bassclef-internal-prose-ok\` label present; downgraded from blocking."
|
|
200
|
+
exit 0
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
exit 1
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# pattern-annotation-validate.sh — Layer 3 of bassclef#478 traceability.
|
|
4
|
+
#
|
|
5
|
+
# Fires on PreToolUse for Edit/Write/MultiEdit. When the file being written
|
|
6
|
+
# is source code (src/**, scripts/**, lib/**, .claude/**) and contains
|
|
7
|
+
# @pattern annotations, validate that each annotation points to an existing
|
|
8
|
+
# catalog entry under patterns/<shelf>/<source>/<slug>.md. BLOCK on missing
|
|
9
|
+
# catalog paths.
|
|
10
|
+
#
|
|
11
|
+
# Also auto-populates state/pattern-implementations/*.json manifest entries
|
|
12
|
+
# via state_pattern_implementation_set accessor (post-validation).
|
|
13
|
+
#
|
|
14
|
+
# Override: SKIP_PATTERN_ANNOTATION=1 <command> — logged.
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
# Read tool input from stdin (Claude Code hook protocol).
|
|
19
|
+
INPUT="${1:-$(cat)}"
|
|
20
|
+
|
|
21
|
+
# Skip when override is set.
|
|
22
|
+
if [ "${SKIP_PATTERN_ANNOTATION:-0}" = "1" ]; then
|
|
23
|
+
echo "[pattern-annotation-validate: SKIP_PATTERN_ANNOTATION=1; bypassing]" >&2
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Extract tool name + file path from JSON input.
|
|
28
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null || echo "")
|
|
29
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null || echo "")
|
|
30
|
+
NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // empty' 2>/dev/null || echo "")
|
|
31
|
+
|
|
32
|
+
# Only fire on Edit/Write/MultiEdit.
|
|
33
|
+
case "$TOOL_NAME" in
|
|
34
|
+
Edit|Write|MultiEdit) ;;
|
|
35
|
+
*) exit 0 ;;
|
|
36
|
+
esac
|
|
37
|
+
|
|
38
|
+
# Only fire on source-code paths.
|
|
39
|
+
case "$FILE_PATH" in
|
|
40
|
+
*/src/*|*/scripts/*|*/lib/*|*/.claude/skills/*|*/.claude/hooks/*) ;;
|
|
41
|
+
*) exit 0 ;;
|
|
42
|
+
esac
|
|
43
|
+
|
|
44
|
+
# Skip if file doesn't currently exist (Write of new file) AND new content
|
|
45
|
+
# has no @pattern annotation — no validation needed.
|
|
46
|
+
EXISTING_CONTENT=""
|
|
47
|
+
if [ -f "$FILE_PATH" ]; then
|
|
48
|
+
EXISTING_CONTENT=$(cat "$FILE_PATH")
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Combine existing + new content for annotation extraction (covers both
|
|
52
|
+
# Write of new files and Edit of existing).
|
|
53
|
+
COMBINED="${EXISTING_CONTENT}
|
|
54
|
+
${NEW_CONTENT}"
|
|
55
|
+
|
|
56
|
+
# Extract @pattern annotations: match `@pattern\s+(patterns/...)`.
|
|
57
|
+
ANNOTATIONS=$(echo "$COMBINED" | grep -oE '@pattern[[:space:]]+patterns/[a-z0-9_-]+/[a-z0-9_-]+/[a-z0-9_-]+\.md' || true)
|
|
58
|
+
|
|
59
|
+
if [ -z "$ANNOTATIONS" ]; then
|
|
60
|
+
exit 0 # No annotations; nothing to validate.
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# Get repo root.
|
|
64
|
+
REPO_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
65
|
+
|
|
66
|
+
# For each annotation, check the catalog path exists.
|
|
67
|
+
INVALID=""
|
|
68
|
+
while IFS= read -r line; do
|
|
69
|
+
# Extract the catalog path from `@pattern patterns/...`
|
|
70
|
+
pattern_path=$(echo "$line" | sed -E 's/.*@pattern[[:space:]]+(patterns\/[a-z0-9_-]+\/[a-z0-9_-]+\/[a-z0-9_-]+\.md).*/\1/')
|
|
71
|
+
if [ ! -f "$REPO_ROOT/$pattern_path" ]; then
|
|
72
|
+
INVALID="${INVALID}${pattern_path}\n"
|
|
73
|
+
fi
|
|
74
|
+
done <<< "$ANNOTATIONS"
|
|
75
|
+
|
|
76
|
+
if [ -n "$INVALID" ]; then
|
|
77
|
+
cat >&2 <<EOF
|
|
78
|
+
============================================
|
|
79
|
+
🛑 PATTERN-ANNOTATION GATE — BLOCKED 🛑
|
|
80
|
+
============================================
|
|
81
|
+
|
|
82
|
+
File: $FILE_PATH
|
|
83
|
+
Tool: $TOOL_NAME
|
|
84
|
+
|
|
85
|
+
Violation: @pattern annotation references catalog path that doesn't exist.
|
|
86
|
+
|
|
87
|
+
Missing catalog entries:
|
|
88
|
+
$(echo -e "$INVALID")
|
|
89
|
+
|
|
90
|
+
Resolution paths:
|
|
91
|
+
1. Fix the annotation to point at an existing catalog entry (preferred).
|
|
92
|
+
2. Run /agent-research-spawn to add the missing entry, then write.
|
|
93
|
+
3. Override (logged): SKIP_PATTERN_ANNOTATION=1 <command>
|
|
94
|
+
|
|
95
|
+
Per rule: .claude/rules/pattern-annotation.md
|
|
96
|
+
Per ticket: bassclef#478 Layer 3
|
|
97
|
+
============================================
|
|
98
|
+
EOF
|
|
99
|
+
exit 3 # Hook BLOCK exit code.
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# Validation passed; auto-populate manifest entries.
|
|
103
|
+
# (Best-effort; failures here are non-blocking.)
|
|
104
|
+
#
|
|
105
|
+
# Filter ephemeral paths before writing to manifest. The auto-populate path
|
|
106
|
+
# uses ${FILE_PATH#$REPO_ROOT/} to compute a relative path; if FILE_PATH is
|
|
107
|
+
# outside REPO_ROOT (e.g., test fixtures under /tmp or /var/folders), the
|
|
108
|
+
# substitution doesn't strip and the absolute path becomes the manifest key.
|
|
109
|
+
# That leaks ephemeral paths into git-tracked state. Skip these.
|
|
110
|
+
# Override: SKIP_PATTERN_ANNOTATION=1 (already handled above).
|
|
111
|
+
case "$FILE_PATH" in
|
|
112
|
+
/tmp/*|/var/folders/*)
|
|
113
|
+
# Test fixture path; skip manifest write.
|
|
114
|
+
exit 0
|
|
115
|
+
;;
|
|
116
|
+
esac
|
|
117
|
+
case "$FILE_PATH" in
|
|
118
|
+
"$REPO_ROOT"/*) ;; # Inside repo — proceed.
|
|
119
|
+
*)
|
|
120
|
+
# File is outside REPO_ROOT (no leading match). Skip manifest write.
|
|
121
|
+
exit 0
|
|
122
|
+
;;
|
|
123
|
+
esac
|
|
124
|
+
|
|
125
|
+
if command -v jq >/dev/null 2>&1; then
|
|
126
|
+
cd "$REPO_ROOT"
|
|
127
|
+
if [ -f "lib/state.sh" ]; then
|
|
128
|
+
# shellcheck disable=SC1091
|
|
129
|
+
source lib/state.sh 2>/dev/null || exit 0
|
|
130
|
+
|
|
131
|
+
# Re-extract annotations with line numbers from the COMBINED content.
|
|
132
|
+
while IFS= read -r line; do
|
|
133
|
+
pattern_path=$(echo "$line" | sed -E 's/.*@pattern[[:space:]]+(patterns\/[a-z0-9_-]+\/[a-z0-9_-]+\/[a-z0-9_-]+\.md).*/\1/')
|
|
134
|
+
# Find line number in the file (best-effort).
|
|
135
|
+
line_num=$(grep -nF "@pattern $pattern_path" "$FILE_PATH" 2>/dev/null | head -1 | cut -d: -f1 || echo "1")
|
|
136
|
+
relative_path="${FILE_PATH#$REPO_ROOT/}"
|
|
137
|
+
state_pattern_implementation_set "$relative_path" "$pattern_path" "$line_num" 2>/dev/null || true
|
|
138
|
+
done <<< "$ANNOTATIONS"
|
|
139
|
+
fi
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
exit 0
|
|
@@ -83,10 +83,24 @@ ${OFFENDERS_BULLETS}"
|
|
|
83
83
|
GRADE_LINE="
|
|
84
84
|
|
|
85
85
|
**From your LAST turn (not this one): grade ${LAST_GRADE} (${LAST_WORDS} words). Target: grade 8 for 80%+ of prose. Ceiling: 10 for simple technical sentences. ${GRADE_STATUS}. THIS turn is not measured yet — self-check each sentence before I ship it.** (Per #1044 Option B: post-turn measurement + pre-turn injection means the number I see is one turn old by design.)${OFFENDERS_LINE}"
|
|
86
|
+
# cli#107 cure — fire the textstat-missing warning at most once per session.
|
|
87
|
+
# Prior version emitted this on every UserPromptSubmit fire, training the
|
|
88
|
+
# reader to skim the steering block it sits in. Marker under
|
|
89
|
+
# $HOME/.claude/state/sessions/<session-id>/ scopes the check to one
|
|
90
|
+
# session; fresh sessions get the warning once. Marker write failure is
|
|
91
|
+
# silent (never crashes the hook) per bassclef defensive-bash pattern.
|
|
86
92
|
if [ "$SOURCE" = "FALLBACK" ]; then
|
|
87
|
-
|
|
93
|
+
__ts_session_id="${CLAUDE_SESSION_ID:-${PPID:-$$}}"
|
|
94
|
+
__ts_marker_dir="${CLAUDE_SESSION_STATE_DIR:-$HOME/.claude/state/sessions}/${__ts_session_id}"
|
|
95
|
+
__ts_marker="${__ts_marker_dir}/textstat-warned"
|
|
96
|
+
if [ ! -f "$__ts_marker" ]; then
|
|
97
|
+
FALLBACK_WARNING="
|
|
88
98
|
|
|
89
99
|
**textstat not installed; grade uses the hand-rolled fallback. Numbers are approximate. Run \`pip install textstat\` for the sharper score.**"
|
|
100
|
+
mkdir -p "$__ts_marker_dir" 2>/dev/null || true
|
|
101
|
+
touch "$__ts_marker" 2>/dev/null || true
|
|
102
|
+
fi
|
|
103
|
+
unset __ts_session_id __ts_marker_dir __ts_marker
|
|
90
104
|
fi
|
|
91
105
|
fi
|
|
92
106
|
fi
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
# Post-Write Traceability — fires on PostToolUse for Write/Edit.
|
|
6
|
+
# Checks that artifacts maintain traceability to their source.
|
|
7
|
+
|
|
8
|
+
INPUT=$(cat)
|
|
9
|
+
# Fail-soft on malformed JSON — match bassclef#1101 pre-commit-gate.sh
|
|
10
|
+
# pattern. Without the 2>/dev/null fallback, jq exit 5 + set -e crashes
|
|
11
|
+
# the hook on malformed harness payloads. Caught WU-5 of bet 2026-06-17c.
|
|
12
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null || echo "")
|
|
13
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""' 2>/dev/null || echo "")
|
|
14
|
+
|
|
15
|
+
# Only check Write and Edit operations
|
|
16
|
+
if [ "$TOOL_NAME" != "Write" ] && [ "$TOOL_NAME" != "Edit" ]; then
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Skip non-markdown files (traceability is for documentation artifacts)
|
|
21
|
+
if ! echo "$FILE_PATH" | grep -qE '\.md$'; then
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Skip memory, config, and template files
|
|
26
|
+
if echo "$FILE_PATH" | grep -qE 'memory/|MEMORY\.md|CLAUDE\.md|AGENTS\.md|template|settings'; then
|
|
27
|
+
exit 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# === Spec traceability ===
|
|
31
|
+
if echo "$FILE_PATH" | grep -qiE 'specs?/'; then
|
|
32
|
+
echo ""
|
|
33
|
+
echo "TRACE CHECK: Spec written to $FILE_PATH"
|
|
34
|
+
echo " → Does this spec reference its source canvas or issue?"
|
|
35
|
+
echo " → Format: 'Source: [canvas path or issue #NNN]'"
|
|
36
|
+
echo ""
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# === Chronicle traceability ===
|
|
40
|
+
if echo "$FILE_PATH" | grep -qiE 'chronicle/'; then
|
|
41
|
+
echo ""
|
|
42
|
+
echo "TRACE CHECK: Chronicle written to $FILE_PATH"
|
|
43
|
+
echo " → Does it include commit refs in 'Work done' section?"
|
|
44
|
+
echo " → Does it have 'Open threads' for next session?"
|
|
45
|
+
echo ""
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# === ADR traceability ===
|
|
49
|
+
if echo "$FILE_PATH" | grep -qiE 'decisions/|adrs?/'; then
|
|
50
|
+
echo ""
|
|
51
|
+
echo "TRACE CHECK: ADR written to $FILE_PATH"
|
|
52
|
+
echo " → Does it reference the risk or issue that prompted it?"
|
|
53
|
+
echo " → Status set? (proposed/accepted/deprecated/superseded)"
|
|
54
|
+
echo ""
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# === Iteration bet traceability ===
|
|
58
|
+
if echo "$FILE_PATH" | grep -qiE 'iteration-bets?/'; then
|
|
59
|
+
echo ""
|
|
60
|
+
echo "TRACE CHECK: Iteration bet written to $FILE_PATH"
|
|
61
|
+
echo " → Does it reference specific issue numbers in scope?"
|
|
62
|
+
echo " → Does it declare the risk being retired?"
|
|
63
|
+
echo ""
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
exit 0
|