@thebassclef/lite 0.1.3 → 1.0.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 +216 -137
- package/dist/cli.js +218 -139
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lite/.bassclef-source.json +10 -0
- package/dist/lite/.claude/settings.json +212 -0
- package/dist/lite/CLAUDE.md +41 -0
- package/dist/lite/gitignore +58 -0
- package/dist/lite/standards/bassclef-wiring-manifest.json +497 -0
- package/dist/lite/whereami.md +24 -0
- package/package.json +8 -2
- package/substrate/.bassclef/lite-manifest.json +0 -2701
- package/substrate/.claude/agents/architect.md +0 -70
- package/substrate/.claude/agents/builder.md +0 -114
- package/substrate/.claude/agents/designer.md +0 -156
- package/substrate/.claude/agents/reviewer.md +0 -88
- package/substrate/.claude/hooks/artifact-ingestion-gate.sh +0 -357
- package/substrate/.claude/hooks/assert-verify-steering.sh +0 -77
- package/substrate/.claude/hooks/bassclef-source-config-validate.sh +0 -215
- package/substrate/.claude/hooks/bassclef-sync.sh +0 -634
- package/substrate/.claude/hooks/compound-noun-scrub.sh +0 -292
- package/substrate/.claude/hooks/kiss-expansion-inject.sh +0 -69
- package/substrate/.claude/hooks/longrun-prep-compounding-axis-check.sh +0 -492
- package/substrate/.claude/hooks/longrun-prep-compounding-sequence-check.sh +0 -492
- package/substrate/.claude/hooks/plain-english-steering.sh +0 -156
- package/substrate/.claude/hooks/post-skill-friction-check.sh +0 -177
- package/substrate/.claude/hooks/post-skill-telemetry.sh +0 -62
- package/substrate/.claude/hooks/pre-build-gate.sh +0 -511
- package/substrate/.claude/hooks/pre-commit-gate.sh +0 -451
- package/substrate/.claude/hooks/session-end.sh +0 -433
- package/substrate/.claude/hooks/session-reflection.sh +0 -303
- package/substrate/.claude/hooks/skill-body-grade-gate.sh +0 -219
- package/substrate/.claude/hooks/skill-body-intent-drift.sh +0 -107
- package/substrate/.claude/hooks/skill-step-list-check.sh +0 -171
- package/substrate/.claude/hooks/state-validate.sh +0 -271
- package/substrate/.claude/hooks/substrate-clarity-gate.sh +0 -1110
- package/substrate/.claude/hooks/temperance-gate.sh +0 -147
- package/substrate/.claude/hooks/testing-tier-enforce.sh +0 -233
- package/substrate/.claude/hooks/turn-prose-grade-measure.sh +0 -219
- package/substrate/.claude/hooks/turn-prose-kiss-check.sh +0 -463
- package/substrate/.claude/hooks/vocabulary-migration-check.sh +0 -171
- package/substrate/.claude/hooks/whereami-utc-gate.sh +0 -142
- package/substrate/.claude/luminaries/alan-cooper.md +0 -170
- package/substrate/.claude/luminaries/alistair-cockburn.md +0 -140
- package/substrate/.claude/luminaries/amazon-pr-faq.md +0 -34
- package/substrate/.claude/luminaries/ash-maurya.md +0 -121
- package/substrate/.claude/luminaries/bill-buxton.md +0 -210
- package/substrate/.claude/luminaries/charles-sanders-peirce.md +0 -150
- package/substrate/.claude/luminaries/david-ogilvy.md +0 -192
- package/substrate/.claude/luminaries/don-norman.md +0 -173
- package/substrate/.claude/luminaries/edward-tufte.md +0 -179
- package/substrate/.claude/luminaries/eric-evans.md +0 -160
- package/substrate/.claude/luminaries/frederick-brooks.md +0 -50
- package/substrate/.claude/luminaries/gang-of-four.md +0 -157
- package/substrate/.claude/luminaries/glenford-myers.md +0 -230
- package/substrate/.claude/luminaries/hunt-thomas.md +0 -115
- package/substrate/.claude/luminaries/hyrum-wright.md +0 -94
- package/substrate/.claude/luminaries/jason-fried-dhh.md +0 -46
- package/substrate/.claude/luminaries/jesse-james-garrett.md +0 -154
- package/substrate/.claude/luminaries/john-ousterhout.md +0 -94
- package/substrate/.claude/luminaries/karl-popper.md +0 -132
- package/substrate/.claude/luminaries/kent-beck.md +0 -168
- package/substrate/.claude/luminaries/linus-torvalds.md +0 -218
- package/substrate/.claude/luminaries/martin-fowler.md +0 -164
- package/substrate/.claude/luminaries/michael-feathers.md +0 -125
- package/substrate/.claude/luminaries/michael-nygard.md +0 -118
- package/substrate/.claude/luminaries/robert-c-martin.md +0 -164
- package/substrate/.claude/luminaries/saltzer-schroeder.md +0 -194
- package/substrate/.claude/luminaries/sophia-prater.md +0 -193
- package/substrate/.claude/luminaries/stephen-toulmin.md +0 -131
- package/substrate/.claude/luminaries/tony-hoare.md +0 -170
- package/substrate/.claude/luminaries/vaughn-vernon.md +0 -50
- package/substrate/.claude/luminaries/w-edwards-deming.md +0 -158
- package/substrate/.claude/rules/accessor-library-discipline.md +0 -138
- package/substrate/.claude/rules/adr-discipline.md +0 -120
- package/substrate/.claude/rules/api-conventions.md +0 -125
- package/substrate/.claude/rules/artifact-ingestion.md +0 -179
- package/substrate/.claude/rules/assert-only-after-verify.md +0 -137
- package/substrate/.claude/rules/blocked-items.md +0 -146
- package/substrate/.claude/rules/bootstrap-pair-discipline.md +0 -141
- package/substrate/.claude/rules/branching.md +0 -28
- package/substrate/.claude/rules/cold-adopter-harness-discipline.md +0 -129
- package/substrate/.claude/rules/commit-conventions.md +0 -22
- package/substrate/.claude/rules/compounding-axis-fresh-analysis.md +0 -188
- package/substrate/.claude/rules/compounding-sequence-fresh-analysis.md +0 -188
- package/substrate/.claude/rules/context-engineering.md +0 -202
- package/substrate/.claude/rules/context-management.md +0 -85
- package/substrate/.claude/rules/defensive-bash.md +0 -68
- package/substrate/.claude/rules/deferred-actions.md +0 -233
- package/substrate/.claude/rules/destructive-operations.md +0 -69
- package/substrate/.claude/rules/diagnosis.md +0 -38
- package/substrate/.claude/rules/github-issue-flash-tweet.md +0 -156
- package/substrate/.claude/rules/guardrails.md +0 -73
- package/substrate/.claude/rules/hook-wire-on-author.md +0 -103
- package/substrate/.claude/rules/identifier-leak-prevention.md +0 -104
- package/substrate/.claude/rules/iteration-bet-brief-completeness.md +0 -54
- package/substrate/.claude/rules/lite-manifest-schema-change-discipline.md +0 -98
- package/substrate/.claude/rules/longrun-prep-plan-doc-compression.md +0 -89
- package/substrate/.claude/rules/loop-discipline.md +0 -81
- package/substrate/.claude/rules/manual-prod-approval.md +0 -100
- package/substrate/.claude/rules/marker-enrichment-discipline.md +0 -99
- package/substrate/.claude/rules/mobile-ephemeral-session.md +0 -109
- package/substrate/.claude/rules/new-dependency-check.md +0 -51
- package/substrate/.claude/rules/oo-ad-entry-point.md +0 -117
- package/substrate/.claude/rules/operator-facing-prose.md +0 -196
- package/substrate/.claude/rules/option-label-discipline.md +0 -108
- package/substrate/.claude/rules/pattern-annotation.md +0 -100
- package/substrate/.claude/rules/plain-english-discipline.md +0 -156
- package/substrate/.claude/rules/plan-enumeration-needs-value-props.md +0 -211
- package/substrate/.claude/rules/pr-body-shape.md +0 -317
- package/substrate/.claude/rules/pr-strategy.md +0 -167
- package/substrate/.claude/rules/pr-title-shape.md +0 -161
- package/substrate/.claude/rules/prototype-workflow.md +0 -65
- package/substrate/.claude/rules/reserved-skill-names.md +0 -123
- package/substrate/.claude/rules/schema-management.md +0 -49
- package/substrate/.claude/rules/sdlc-gates.md +0 -149
- package/substrate/.claude/rules/security.md +0 -37
- package/substrate/.claude/rules/session-artifacts.md +0 -236
- package/substrate/.claude/rules/skill-composition-declarations.md +0 -124
- package/substrate/.claude/rules/skill-description-clarity.md +0 -247
- package/substrate/.claude/rules/skill-procedure-step-list.md +0 -137
- package/substrate/.claude/rules/state-schema-validation.md +0 -162
- package/substrate/.claude/rules/stuck-signal-diagnostic.md +0 -140
- package/substrate/.claude/rules/substrate-config-schema.md +0 -98
- package/substrate/.claude/rules/test-list-discipline.md +0 -175
- package/substrate/.claude/rules/test-sufficiency.md +0 -210
- package/substrate/.claude/rules/testing-tier-config.md +0 -145
- package/substrate/.claude/rules/testing.md +0 -38
- package/substrate/.claude/rules/turn-estimate-grounding.md +0 -134
- package/substrate/.claude/rules/visual-hierarchy.md +0 -437
- package/substrate/.claude/rules/we-dont-break-adopters.md +0 -126
- package/substrate/.claude/rules/whereami-load-bearing.md +0 -202
- package/substrate/.claude/rules/writing-craft-discipline.md +0 -92
- package/substrate/.claude/rules/wu-sequencing-compounds.md +0 -145
- package/substrate/.claude/skills/build/SKILL.md +0 -640
- package/substrate/.claude/skills/chronicle/SKILL.md +0 -55
- package/substrate/.claude/skills/clean-artifacts/SKILL.md +0 -249
- package/substrate/.claude/skills/decompose/SKILL.md +0 -280
- package/substrate/.claude/skills/diagnose/SKILL.md +0 -297
- package/substrate/.claude/skills/feynman/SKILL.md +0 -90
- package/substrate/.claude/skills/howdoi/SKILL.md +0 -105
- package/substrate/.claude/skills/ia-model/SKILL.md +0 -108
- package/substrate/.claude/skills/interaction-design/SKILL.md +0 -112
- package/substrate/.claude/skills/interpret-input/SKILL.md +0 -180
- package/substrate/.claude/skills/journal/SKILL.md +0 -209
- package/substrate/.claude/skills/kiss/SKILL.md +0 -449
- package/substrate/.claude/skills/launch/SKILL.md +0 -915
- package/substrate/.claude/skills/lean-canvas/SKILL.md +0 -332
- package/substrate/.claude/skills/longrun/SKILL.md +0 -463
- package/substrate/.claude/skills/luminary/SKILL.md +0 -481
- package/substrate/.claude/skills/ogilvy-writing-audit/SKILL.md +0 -177
- package/substrate/.claude/skills/onboard-repo/SKILL.md +0 -1624
- package/substrate/.claude/skills/pattern-review/SKILL.md +0 -99
- package/substrate/.claude/skills/personas/SKILL.md +0 -207
- package/substrate/.claude/skills/promote/SKILL.md +0 -283
- package/substrate/.claude/skills/requirement/SKILL.md +0 -98
- package/substrate/.claude/skills/retro/SKILL.md +0 -117
- package/substrate/.claude/skills/riff/SKILL.md +0 -114
- package/substrate/.claude/skills/roadmap-reconcile/SKILL.md +0 -163
- package/substrate/.claude/skills/session-end/SKILL.md +0 -309
- package/substrate/.claude/skills/session-log/SKILL.md +0 -299
- package/substrate/.claude/skills/skills/SKILL.md +0 -228
- package/substrate/.claude/skills/spec/SKILL.md +0 -105
- package/substrate/.claude/skills/sprint/SKILL.md +0 -392
- package/substrate/.claude/skills/stage/SKILL.md +0 -384
- package/substrate/.claude/skills/state-a-problem/SKILL.md +0 -185
- package/substrate/.claude/skills/temperance/SKILL.md +0 -108
- package/substrate/.claude/skills/use-case/SKILL.md +0 -417
- package/substrate/.claude/skills/user-stories/SKILL.md +0 -268
- package/substrate/.claude/skills/value-prop/SKILL.md +0 -251
- package/substrate/.claude/skills/verify/SKILL.md +0 -160
- package/substrate/.claude/skills/visual-review/SKILL.md +0 -503
- package/substrate/.claude/skills/whats-the-plan/SKILL.md +0 -202
- package/substrate/.claude/skills/whereami/SKILL.md +0 -307
- package/substrate/AGENTS.md +0 -79
- package/substrate/CLAUDE-lite.md +0 -85
- package/substrate/CODE_OF_CONDUCT.md +0 -28
- package/substrate/CONTRIBUTING.md +0 -177
- package/substrate/README.md +0 -173
- package/substrate/SECURITY.md +0 -19
- package/substrate/architecture/decisions/ADR-029-release-pipeline.md +0 -79
- package/substrate/architecture/decisions/ADR-031-non-breaking-changes-adopter-discipline.md +0 -139
- package/substrate/architecture/decisions/ADR-032-adopter-sync-dispatcher-architecture.md +0 -192
- package/substrate/architecture/decisions/ADR-039-release-tagging-scheme.md +0 -145
- package/substrate/architecture/decisions/ADR-040-planning-skill-vocabulary-and-lite-profile.md +0 -155
- package/substrate/architecture/decisions/ADR-044-unified-skill-body-template.md +0 -162
- package/substrate/lib/clean-artifacts-sweep.sh +0 -112
- package/substrate/lib/code-comment-discipline.sh +0 -144
- package/substrate/lib/composer-preflight.sh +0 -459
- package/substrate/lib/hook-inject.sh +0 -255
- package/substrate/lib/luminary-pick.sh +0 -96
- package/substrate/lib/output-discipline.sh +0 -143
- package/substrate/lib/prose-scan-boundary.sh +0 -171
- package/substrate/lib/rewrite-check.sh +0 -214
- package/substrate/lib/state.sh +0 -1372
- package/substrate/lib/telemetry.sh +0 -205
- package/substrate/lib/tier-check.sh +0 -187
- package/substrate/lib/tier-dependency-audit.sh +0 -1088
- package/substrate/presence/install/bassclef-hook-connect.sh +0 -178
- package/substrate/presence/install/bassclef-sync.dispatcher.template.sh +0 -841
- package/substrate/presence/install/bassclef-sync.template.sh +0 -2076
- package/substrate/presence/install/schedule-auto-save.cron.sh +0 -88
- package/substrate/presence/install/schedule-auto-save.taskscheduler.md +0 -122
- package/substrate/scripts/aggregate-telemetry.sh +0 -217
- package/substrate/scripts/analyze-tier-dependencies.sh +0 -239
- package/substrate/scripts/generate-lite-manifest.sh +0 -505
- package/substrate/scripts/generate-tier-manifest.sh +0 -28
- package/substrate/scripts/intent-drift-check.sh +0 -456
- package/substrate/scripts/lite-manifest-drift-check.sh +0 -146
- package/substrate/scripts/render-lite-manifest-doc.sh +0 -150
- package/substrate/standards/adr-template.md +0 -86
- package/substrate/standards/api-conventions/nextjs.md +0 -84
- package/substrate/standards/artifact-composition.md +0 -209
- package/substrate/standards/bash-hook-safety.md +0 -246
- package/substrate/standards/bassclef-configs-schema.md +0 -232
- package/substrate/standards/bassclef-evolution.md +0 -143
- package/substrate/standards/bassclef-internal-jargon.md +0 -244
- package/substrate/standards/bassclef-managed-sentinel.md +0 -96
- package/substrate/standards/bassclef-source-config.md +0 -228
- package/substrate/standards/branch-stacking.md +0 -408
- package/substrate/standards/code-safety-principles.md +0 -176
- package/substrate/standards/composer-prerequisites.md +0 -155
- package/substrate/standards/deferred-actions-schema.md +0 -204
- package/substrate/standards/dependency-discipline/cargo.md +0 -39
- package/substrate/standards/dependency-discipline/gem.md +0 -43
- package/substrate/standards/dependency-discipline/go-mod.md +0 -41
- package/substrate/standards/dependency-discipline/npm.md +0 -42
- package/substrate/standards/dependency-discipline/pip.md +0 -42
- package/substrate/standards/deployment-topology/ec2-tailscale.md +0 -225
- package/substrate/standards/deployment-topology.md +0 -69
- package/substrate/standards/docs-sync-allowlist.md +0 -76
- package/substrate/standards/domain-and-dns.md +0 -145
- package/substrate/standards/frontend-stack.md +0 -67
- package/substrate/standards/frontmatter-schema.md +0 -154
- package/substrate/standards/graceful-exit.md +0 -227
- package/substrate/standards/hook-idempotency.md +0 -102
- package/substrate/standards/hook-injection-discipline.md +0 -202
- package/substrate/standards/hook-install-class.md +0 -215
- package/substrate/standards/input-handler-interface.md +0 -152
- package/substrate/standards/lite-manifest-schema-changes.md +0 -135
- package/substrate/standards/luminary-matching.md +0 -105
- package/substrate/standards/luminary-problem-patterns.md +0 -481
- package/substrate/standards/migration-discipline/active-record.md +0 -50
- package/substrate/standards/migration-discipline/alembic.md +0 -43
- package/substrate/standards/migration-discipline/gorm.md +0 -50
- package/substrate/standards/migration-discipline/prisma.md +0 -53
- package/substrate/standards/migration-discipline/sqlalchemy.md +0 -51
- package/substrate/standards/mobile-ephemeral-session.md +0 -167
- package/substrate/standards/model-routing-discipline.md +0 -160
- package/substrate/standards/ogilvy-writing-rules.md +0 -225
- package/substrate/standards/opener-discipline.md +0 -96
- package/substrate/standards/operator-facing-prose-discipline.md +0 -201
- package/substrate/standards/persona-schema.md +0 -229
- package/substrate/standards/pluggable-luminaries.md +0 -323
- package/substrate/standards/pr-body-discipline.md +0 -115
- package/substrate/standards/preview-state-schema.md +0 -189
- package/substrate/standards/project-directory-layout.md +0 -276
- package/substrate/standards/release-tagging.md +0 -137
- package/substrate/standards/reserved-skill-names.md +0 -120
- package/substrate/standards/scannable-multi-option-output.md +0 -261
- package/substrate/standards/sdlc-compliance.md +0 -286
- package/substrate/standards/sdlc-gates/typescript.md +0 -57
- package/substrate/standards/secrets-lifecycle.md +0 -210
- package/substrate/standards/security-scanner-adapter.md +0 -145
- package/substrate/standards/session-board.md +0 -256
- package/substrate/standards/skill-output-discipline.md +0 -90
- package/substrate/standards/state-spine-contract.md +0 -255
- package/substrate/standards/state-spine.md +0 -511
- package/substrate/standards/steering-hints/kiss-words.md +0 -11
- package/substrate/standards/substrate-config-schema.md +0 -267
- package/substrate/standards/tech-stack-config.md +0 -109
- package/substrate/standards/tier-dependency-analysis.md +0 -167
- package/substrate/standards/tier-runtime-deps/lite.md +0 -57
- package/substrate/standards/tier-tag-schema.md +0 -155
- package/substrate/standards/two-layer-config.md +0 -99
- package/substrate/standards/use-case-format.md +0 -292
- package/substrate/standards/user-story-invest.md +0 -268
- package/substrate/standards/velocity-and-appetite.md +0 -229
- package/substrate/standards/voice-input-pattern.md +0 -119
- package/substrate/standards/whereami-schema.md +0 -301
- package/substrate/standards/worktree-management.md +0 -211
- package/substrate/standards/writing-guide.md +0 -213
- package/substrate/templates/chronicle-template.md +0 -75
- package/substrate/templates/deferred-action-template.md +0 -45
- package/substrate/templates/memory-proposal-template.md +0 -77
- package/substrate/templates/persona-template.md +0 -200
- package/substrate/templates/pr-faq.md +0 -45
- package/substrate/templates/secret-rotation-template.md +0 -162
- package/substrate/templates/spec-template.md +0 -131
- package/substrate/templates/use-case-template.md +0 -194
- package/substrate/templates/user-story-template.md +0 -107
- package/substrate/templates/whereami-template.md +0 -101
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# tier: lite
|
|
3
|
-
# Artifact Ingestion Gate — fires on Write/Edit to generative artifact paths.
|
|
4
|
-
# Enforces .claude/rules/artifact-ingestion.md: every substantive generative
|
|
5
|
-
# output must include a "## Sources read" block (or a stated-from-scratch
|
|
6
|
-
# rationale) so agents can't substitute training priors for staged artifacts.
|
|
7
|
-
#
|
|
8
|
-
# Exit codes:
|
|
9
|
-
# 0 — allow (path doesn't match, or Sources-read block present, or override)
|
|
10
|
-
# 2 — block (matcher hit, no Sources-read, no override)
|
|
11
|
-
#
|
|
12
|
-
# Override: set SKIP_ARTIFACT_INGESTION=1 to bypass (logged to trace).
|
|
13
|
-
#
|
|
14
|
-
# Rule: .claude/rules/artifact-ingestion.md
|
|
15
|
-
# Issue: bassclef#116
|
|
16
|
-
|
|
17
|
-
set +e # never crash the tool call; fall through to allow on any unexpected error
|
|
18
|
-
|
|
19
|
-
# === Hook liveness heartbeat (WU-3 of bet 2026-07-31d; closes #1002) ===
|
|
20
|
-
# Silent-fail — a missing lib never crashes the hook.
|
|
21
|
-
{
|
|
22
|
-
_hb_sd="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
-
for _hb_c in "${_hb_sd}/../../lib/hook-heartbeat.sh" "${HOME:-/}/lib/hook-heartbeat.sh"; do
|
|
24
|
-
[ -f "$_hb_c" ] && source "$_hb_c" && heartbeat_mark "artifact-ingestion-gate" && break
|
|
25
|
-
done
|
|
26
|
-
unset _hb_sd _hb_c
|
|
27
|
-
} 2>/dev/null || true
|
|
28
|
-
|
|
29
|
-
INPUT=$(cat)
|
|
30
|
-
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null)
|
|
31
|
-
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""' 2>/dev/null)
|
|
32
|
-
|
|
33
|
-
# Only fire on Write / Edit
|
|
34
|
-
if [ "$TOOL_NAME" != "Write" ] && [ "$TOOL_NAME" != "Edit" ]; then
|
|
35
|
-
exit 0
|
|
36
|
-
fi
|
|
37
|
-
|
|
38
|
-
# No file path → allow (defensive)
|
|
39
|
-
if [ -z "$FILE_PATH" ]; then
|
|
40
|
-
exit 0
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
# Matcher paths — generative artifacts requiring Sources-read block.
|
|
44
|
-
# Narrow by design; expand carefully. Skip paths (.claude/, memory, configs,
|
|
45
|
-
# release-notes, journals) are NOT matched here and pass through.
|
|
46
|
-
MATCH=0
|
|
47
|
-
case "$FILE_PATH" in
|
|
48
|
-
*/strategy/canvases/*.md) MATCH=1 ;;
|
|
49
|
-
*/docs/specs/*.md) MATCH=1 ;;
|
|
50
|
-
*/docs/iteration-bets/*.md) MATCH=1 ;;
|
|
51
|
-
*/architecture/decisions/ADR-*.md) MATCH=1 ;;
|
|
52
|
-
*/docs/prototypes/*/index.html) MATCH=1 ;;
|
|
53
|
-
*/docs/prototypes/*/*.html) MATCH=1 ;;
|
|
54
|
-
*/docs/decompositions/*.md) MATCH=1 ;;
|
|
55
|
-
esac
|
|
56
|
-
|
|
57
|
-
if [ "$MATCH" -eq 0 ]; then
|
|
58
|
-
exit 0
|
|
59
|
-
fi
|
|
60
|
-
|
|
61
|
-
# Override hatch — log and allow
|
|
62
|
-
if [ "${SKIP_ARTIFACT_INGESTION:-0}" = "1" ]; then
|
|
63
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
|
|
64
|
-
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/trace-helper.sh" ]; then
|
|
65
|
-
source "$SCRIPT_DIR/trace-helper.sh" 2>/dev/null || true
|
|
66
|
-
trace_log "artifact-ingestion-gate" "OVERRIDE $TOOL_NAME $FILE_PATH" 2>/dev/null || true
|
|
67
|
-
fi
|
|
68
|
-
exit 0
|
|
69
|
-
fi
|
|
70
|
-
|
|
71
|
-
# Assemble the content that WILL exist after this operation.
|
|
72
|
-
# Write: tool_input.content is authoritative.
|
|
73
|
-
# Edit: check existing file on disk (if any) + new_string together.
|
|
74
|
-
FUTURE_CONTENT=""
|
|
75
|
-
if [ "$TOOL_NAME" = "Write" ]; then
|
|
76
|
-
FUTURE_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // ""' 2>/dev/null)
|
|
77
|
-
elif [ "$TOOL_NAME" = "Edit" ]; then
|
|
78
|
-
NEW_STRING=$(echo "$INPUT" | jq -r '.tool_input.new_string // ""' 2>/dev/null)
|
|
79
|
-
EXISTING=""
|
|
80
|
-
if [ -f "$FILE_PATH" ]; then
|
|
81
|
-
EXISTING=$(cat "$FILE_PATH" 2>/dev/null)
|
|
82
|
-
fi
|
|
83
|
-
FUTURE_CONTENT="${EXISTING}
|
|
84
|
-
${NEW_STRING}"
|
|
85
|
-
fi
|
|
86
|
-
|
|
87
|
-
# Check for Sources-read block (case-insensitive, tolerant of heading depth).
|
|
88
|
-
# Accepted forms:
|
|
89
|
-
# ## Sources read
|
|
90
|
-
# ### Sources read
|
|
91
|
-
# ## Sources Read
|
|
92
|
-
# ## SOURCES READ
|
|
93
|
-
# Also accepts an explicit from-scratch rationale:
|
|
94
|
-
# "No prior artifacts" (per rule)
|
|
95
|
-
HAS_SOURCES_READ=0
|
|
96
|
-
if echo "$FUTURE_CONTENT" | grep -qiE '^#{1,6}[[:space:]]+sources[[:space:]]+read'; then
|
|
97
|
-
HAS_SOURCES_READ=1
|
|
98
|
-
fi
|
|
99
|
-
if echo "$FUTURE_CONTENT" | grep -qi 'no prior artifacts'; then
|
|
100
|
-
HAS_SOURCES_READ=1
|
|
101
|
-
fi
|
|
102
|
-
|
|
103
|
-
# Block if no Sources-read block at all.
|
|
104
|
-
if [ "$HAS_SOURCES_READ" -eq 0 ]; then
|
|
105
|
-
EMIT_BLOCK=1
|
|
106
|
-
fi
|
|
107
|
-
|
|
108
|
-
# === Parent citation check (bassclef#334 — Layer A determinism gate) ===
|
|
109
|
-
#
|
|
110
|
-
# When the artifact's frontmatter declares parent_roadmap, parent_bet, or
|
|
111
|
-
# parent_canvas, the "Sources read" block MUST cite those paths/ids. This
|
|
112
|
-
# catches the case where an agent declares lineage in frontmatter but
|
|
113
|
-
# doesn't actually read the parent (a determinism failure mode caught by
|
|
114
|
-
# the operator 2026-04-27d when an agent missed parent_roadmap framing).
|
|
115
|
-
#
|
|
116
|
-
# Only fires when HAS_SOURCES_READ=1; if the Sources-read block is missing
|
|
117
|
-
# entirely, the prior check covers it.
|
|
118
|
-
PARENT_CITATION_BLOCK=""
|
|
119
|
-
if [ "$HAS_SOURCES_READ" -eq 1 ]; then
|
|
120
|
-
# Extract YAML frontmatter (between leading --- ... ---)
|
|
121
|
-
FRONTMATTER=$(echo "$FUTURE_CONTENT" | awk '/^---$/{c++; if(c==2) exit; next} c==1')
|
|
122
|
-
|
|
123
|
-
# Extract Sources-read section content (everything from "## Sources read"
|
|
124
|
-
# heading to the next ## heading or end-of-file)
|
|
125
|
-
SOURCES_SECTION=$(echo "$FUTURE_CONTENT" | awk '
|
|
126
|
-
/^#{1,6}[[:space:]]+[Ss][Oo][Uu][Rr][Cc][Ee][Ss][[:space:]]+[Rr][Ee][Aa][Dd]/ { in_section=1; next }
|
|
127
|
-
in_section && /^#{1,6}[[:space:]]/ { exit }
|
|
128
|
-
in_section { print }
|
|
129
|
-
')
|
|
130
|
-
|
|
131
|
-
# For each parent_* field set in frontmatter, verify it (or its slug/id)
|
|
132
|
-
# is referenced in the Sources-read section. Bash regex via grep -F for
|
|
133
|
-
# literal-string match (paths contain dots).
|
|
134
|
-
MISSING_CITATIONS=""
|
|
135
|
-
for parent_key in parent_roadmap parent_bet parent_canvas parent_decomposition; do
|
|
136
|
-
PARENT_VALUE=$(echo "$FRONTMATTER" | awk -v key="$parent_key" '
|
|
137
|
-
$0 ~ "^"key":" {
|
|
138
|
-
sub("^"key":[[:space:]]*", "")
|
|
139
|
-
# Strip leading/trailing whitespace and surrounding quotes
|
|
140
|
-
gsub(/^[[:space:]]+|[[:space:]]+$/, "")
|
|
141
|
-
gsub(/^"|"$|^'\''|'\''$/, "")
|
|
142
|
-
print
|
|
143
|
-
exit
|
|
144
|
-
}
|
|
145
|
-
')
|
|
146
|
-
if [ -z "$PARENT_VALUE" ]; then
|
|
147
|
-
continue
|
|
148
|
-
fi
|
|
149
|
-
|
|
150
|
-
# Check if the parent path (or its basename without extension) appears
|
|
151
|
-
# in the Sources-read section. Accepts either:
|
|
152
|
-
# - Full path (docs/roadmaps/foo.md)
|
|
153
|
-
# - Slug/id (foo, no path/extension)
|
|
154
|
-
PARENT_BASENAME=$(basename "$PARENT_VALUE" .md)
|
|
155
|
-
|
|
156
|
-
if echo "$SOURCES_SECTION" | grep -qF "$PARENT_VALUE"; then
|
|
157
|
-
continue
|
|
158
|
-
fi
|
|
159
|
-
if echo "$SOURCES_SECTION" | grep -qF "$PARENT_BASENAME"; then
|
|
160
|
-
continue
|
|
161
|
-
fi
|
|
162
|
-
|
|
163
|
-
MISSING_CITATIONS="${MISSING_CITATIONS} - ${parent_key}: ${PARENT_VALUE}\n"
|
|
164
|
-
done
|
|
165
|
-
|
|
166
|
-
if [ -n "$MISSING_CITATIONS" ]; then
|
|
167
|
-
PARENT_CITATION_BLOCK="$MISSING_CITATIONS"
|
|
168
|
-
EMIT_BLOCK=1
|
|
169
|
-
fi
|
|
170
|
-
|
|
171
|
-
# === Layer A composition (bassclef#337 WU-5) ===
|
|
172
|
-
#
|
|
173
|
-
# When parent_roadmap is cited, verify the citation includes the
|
|
174
|
-
# current last_event.ts OR last_event.event. This proves the agent
|
|
175
|
-
# read CURRENT roadmap state, not a stale snapshot. Closes the
|
|
176
|
-
# determinism loop: parent declared, parent path cited, current
|
|
177
|
-
# event referenced.
|
|
178
|
-
#
|
|
179
|
-
# Skips silently if no parent_roadmap was set (covered by prior loop).
|
|
180
|
-
STALE_EVENT_BLOCK=""
|
|
181
|
-
PARENT_ROADMAP=$(echo "$FRONTMATTER" | awk '
|
|
182
|
-
$0 ~ /^parent_roadmap:/ {
|
|
183
|
-
sub(/^parent_roadmap:[[:space:]]*/, "")
|
|
184
|
-
gsub(/^[[:space:]]+|[[:space:]]+$/, "")
|
|
185
|
-
gsub(/^"|"$|^'\''|'\''$/, "")
|
|
186
|
-
print
|
|
187
|
-
exit
|
|
188
|
-
}
|
|
189
|
-
')
|
|
190
|
-
if [ -n "$PARENT_ROADMAP" ] && [ -f "$PARENT_ROADMAP" ]; then
|
|
191
|
-
# Extract current last_event from roadmap frontmatter via Python
|
|
192
|
-
LAST_EVENT_TS=$(python3 -c "
|
|
193
|
-
import sys, yaml
|
|
194
|
-
try:
|
|
195
|
-
with open('$PARENT_ROADMAP', 'r') as f:
|
|
196
|
-
content = f.read()
|
|
197
|
-
parts = content.split('---', 2)
|
|
198
|
-
if len(parts) < 3:
|
|
199
|
-
sys.exit(0)
|
|
200
|
-
fm = yaml.safe_load(parts[1]) or {}
|
|
201
|
-
le = fm.get('last_event', {}) or {}
|
|
202
|
-
ts = le.get('ts', '')
|
|
203
|
-
print(str(ts))
|
|
204
|
-
except Exception:
|
|
205
|
-
pass
|
|
206
|
-
" 2>/dev/null)
|
|
207
|
-
LAST_EVENT_NAME=$(python3 -c "
|
|
208
|
-
import sys, yaml
|
|
209
|
-
try:
|
|
210
|
-
with open('$PARENT_ROADMAP', 'r') as f:
|
|
211
|
-
content = f.read()
|
|
212
|
-
parts = content.split('---', 2)
|
|
213
|
-
if len(parts) < 3:
|
|
214
|
-
sys.exit(0)
|
|
215
|
-
fm = yaml.safe_load(parts[1]) or {}
|
|
216
|
-
le = fm.get('last_event', {}) or {}
|
|
217
|
-
ev = le.get('event', '')
|
|
218
|
-
print(str(ev))
|
|
219
|
-
except Exception:
|
|
220
|
-
pass
|
|
221
|
-
" 2>/dev/null)
|
|
222
|
-
|
|
223
|
-
# If roadmap has a last_event, verify Sources cites ts or event name
|
|
224
|
-
if [ -n "$LAST_EVENT_TS" ] || [ -n "$LAST_EVENT_NAME" ]; then
|
|
225
|
-
EVENT_CITED=0
|
|
226
|
-
if [ -n "$LAST_EVENT_TS" ] && echo "$SOURCES_SECTION" | grep -qF "$LAST_EVENT_TS"; then
|
|
227
|
-
EVENT_CITED=1
|
|
228
|
-
fi
|
|
229
|
-
if [ -n "$LAST_EVENT_NAME" ] && echo "$SOURCES_SECTION" | grep -qF "$LAST_EVENT_NAME"; then
|
|
230
|
-
EVENT_CITED=1
|
|
231
|
-
fi
|
|
232
|
-
if [ "$EVENT_CITED" -eq 0 ]; then
|
|
233
|
-
STALE_EVENT_BLOCK="parent_roadmap=$PARENT_ROADMAP\n current last_event.ts=$LAST_EVENT_TS\n current last_event.event=$LAST_EVENT_NAME\n"
|
|
234
|
-
EMIT_BLOCK=1
|
|
235
|
-
fi
|
|
236
|
-
fi
|
|
237
|
-
fi
|
|
238
|
-
fi
|
|
239
|
-
|
|
240
|
-
# All checks passed → allow.
|
|
241
|
-
if [ "${EMIT_BLOCK:-0}" -eq 0 ]; then
|
|
242
|
-
exit 0
|
|
243
|
-
fi
|
|
244
|
-
|
|
245
|
-
# Block — emit banner to stderr (Claude Code surfaces stderr on exit 2)
|
|
246
|
-
if [ "$HAS_SOURCES_READ" -eq 0 ]; then
|
|
247
|
-
cat >&2 <<EOF
|
|
248
|
-
|
|
249
|
-
============================================
|
|
250
|
-
🛑 ARTIFACT-INGESTION GATE — BLOCKED 🛑
|
|
251
|
-
============================================
|
|
252
|
-
|
|
253
|
-
File: $FILE_PATH
|
|
254
|
-
Tool: $TOOL_NAME
|
|
255
|
-
|
|
256
|
-
Generative artifacts require a "## Sources read" block citing the
|
|
257
|
-
artifacts you read before producing this output. This is per
|
|
258
|
-
.claude/rules/artifact-ingestion.md — no Sources-read = no write.
|
|
259
|
-
|
|
260
|
-
Required block (add near top of file):
|
|
261
|
-
|
|
262
|
-
## Sources read
|
|
263
|
-
|
|
264
|
-
- [path]:[line range] — [what you took]
|
|
265
|
-
- ...
|
|
266
|
-
|
|
267
|
-
## What I'm NOT reading (with reason)
|
|
268
|
-
|
|
269
|
-
- [path] — [deliberate exclusion reason]
|
|
270
|
-
|
|
271
|
-
If producing genuinely from scratch, state:
|
|
272
|
-
|
|
273
|
-
"No prior artifacts; producing from scratch because [reason]."
|
|
274
|
-
|
|
275
|
-
Override (logged): SKIP_ARTIFACT_INGESTION=1 <command>
|
|
276
|
-
|
|
277
|
-
Rule: .claude/rules/artifact-ingestion.md
|
|
278
|
-
Issue: bassclef#116
|
|
279
|
-
============================================
|
|
280
|
-
|
|
281
|
-
EOF
|
|
282
|
-
elif [ -n "$STALE_EVENT_BLOCK" ]; then
|
|
283
|
-
cat >&2 <<EOF
|
|
284
|
-
|
|
285
|
-
============================================
|
|
286
|
-
🛑 STALE-EVENT GATE — BLOCKED 🛑
|
|
287
|
-
============================================
|
|
288
|
-
|
|
289
|
-
File: $FILE_PATH
|
|
290
|
-
Tool: $TOOL_NAME
|
|
291
|
-
|
|
292
|
-
This artifact cites parent_roadmap but the "Sources read" block
|
|
293
|
-
doesn't reference the roadmap's CURRENT last_event. Citing the
|
|
294
|
-
parent path proves you read the file; citing the current event
|
|
295
|
-
proves you read the current state, not a stale snapshot.
|
|
296
|
-
|
|
297
|
-
Roadmap state:
|
|
298
|
-
|
|
299
|
-
$(printf "$STALE_EVENT_BLOCK")
|
|
300
|
-
|
|
301
|
-
Add the current ts OR event name to your Sources-read entry. Example:
|
|
302
|
-
|
|
303
|
-
## Sources read
|
|
304
|
-
|
|
305
|
-
- $PARENT_ROADMAP — current last_event ($LAST_EVENT_NAME at $LAST_EVENT_TS)
|
|
306
|
-
- ...
|
|
307
|
-
|
|
308
|
-
Override (logged): SKIP_ARTIFACT_INGESTION=1 <command>
|
|
309
|
-
|
|
310
|
-
Rule: .claude/rules/artifact-ingestion.md
|
|
311
|
-
Issue: bassclef#337 WU-5 (Layer A composition)
|
|
312
|
-
============================================
|
|
313
|
-
|
|
314
|
-
EOF
|
|
315
|
-
elif [ -n "$PARENT_CITATION_BLOCK" ]; then
|
|
316
|
-
cat >&2 <<EOF
|
|
317
|
-
|
|
318
|
-
============================================
|
|
319
|
-
🛑 PARENT-CITATION GATE — BLOCKED 🛑
|
|
320
|
-
============================================
|
|
321
|
-
|
|
322
|
-
File: $FILE_PATH
|
|
323
|
-
Tool: $TOOL_NAME
|
|
324
|
-
|
|
325
|
-
This artifact declares parent lineage in frontmatter but the
|
|
326
|
-
"Sources read" block doesn't cite the parent path(s). Declaring a
|
|
327
|
-
parent without reading it is a determinism failure — agent infers
|
|
328
|
-
context instead of reading current state.
|
|
329
|
-
|
|
330
|
-
Missing citations (parent declared in frontmatter, not cited in Sources read):
|
|
331
|
-
|
|
332
|
-
$(printf "$PARENT_CITATION_BLOCK")
|
|
333
|
-
|
|
334
|
-
Add each parent path to the "## Sources read" block. Example:
|
|
335
|
-
|
|
336
|
-
## Sources read
|
|
337
|
-
|
|
338
|
-
- docs/roadmaps/<YYYY-MM-DD>-<slug>.md — parent_roadmap; current task list + last_event
|
|
339
|
-
- ...
|
|
340
|
-
|
|
341
|
-
Override (logged): SKIP_ARTIFACT_INGESTION=1 <command>
|
|
342
|
-
|
|
343
|
-
Rule: .claude/rules/artifact-ingestion.md
|
|
344
|
-
Issue: bassclef#334 (Layer A determinism gate)
|
|
345
|
-
============================================
|
|
346
|
-
|
|
347
|
-
EOF
|
|
348
|
-
fi
|
|
349
|
-
|
|
350
|
-
# Trace the block (best-effort, don't let trace failure change the block signal)
|
|
351
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
|
|
352
|
-
if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/trace-helper.sh" ]; then
|
|
353
|
-
source "$SCRIPT_DIR/trace-helper.sh" 2>/dev/null || true
|
|
354
|
-
trace_log "artifact-ingestion-gate" "BLOCK $TOOL_NAME $FILE_PATH" 2>/dev/null || true
|
|
355
|
-
fi
|
|
356
|
-
|
|
357
|
-
exit 2
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# tier: lite
|
|
3
|
-
# install-class: dual
|
|
4
|
-
# assert-verify-steering.sh — UserPromptSubmit hook that injects
|
|
5
|
-
# additionalContext steering the agent toward Toulmin's argument model
|
|
6
|
-
# (claim + data + warrant) before any operator-facing assertion.
|
|
7
|
-
#
|
|
8
|
-
# Per bassclef-upstream#582 (parent ticket) + bassclef-upstream#559 (R8
|
|
9
|
-
# mechanization umbrella) + bet 2026-07-01b WU-1.
|
|
10
|
-
#
|
|
11
|
-
# Adds argument-shape steering to the reasoning-boundary defense layer,
|
|
12
|
-
# alongside .claude/hooks/plain-english-steering.sh (bassclef#443).
|
|
13
|
-
#
|
|
14
|
-
# The rule this hook pairs with: .claude/rules/assert-only-after-verify.md
|
|
15
|
-
# The three luminaries anchoring the shape:
|
|
16
|
-
# - Stephen Toulmin (argument model — claim/data/warrant/qualifier/rebuttal)
|
|
17
|
-
# - Karl Popper (falsifiability — rebuttal cell)
|
|
18
|
-
# - Charles Sanders Peirce (abductive reasoning — alternative-consideration)
|
|
19
|
-
#
|
|
20
|
-
# Sibling pattern: .claude/hooks/plain-english-steering.sh
|
|
21
|
-
# Envelope helper: lib/hook-inject.sh emit_additional_context
|
|
22
|
-
#
|
|
23
|
-
# Override per-call: SKIP_ASSERT_VERIFY_STEERING=1
|
|
24
|
-
#
|
|
25
|
-
# Tier 0 strict TDD: .claude/hooks/tests/assert-verify-steering.test.sh
|
|
26
|
-
|
|
27
|
-
set +e
|
|
28
|
-
|
|
29
|
-
# === Source install-class-aware lib (per standards/hook-install-class.md) ===
|
|
30
|
-
# Hook is `dual` — runs under both project install (<repo>/.claude/hooks/)
|
|
31
|
-
# and operator install ($HOME/.claude/hooks/). Uses layered resolution
|
|
32
|
-
# per lib/hook-inject.sh convention.
|
|
33
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
34
|
-
for _candidate in \
|
|
35
|
-
"${SCRIPT_DIR}/../../lib/hook-inject.sh" \
|
|
36
|
-
"${HOME_OVERRIDE:-${HOME:-/}}/lib/hook-inject.sh"; do
|
|
37
|
-
if [ -f "$_candidate" ]; then
|
|
38
|
-
# shellcheck disable=SC1090
|
|
39
|
-
source "$_candidate"
|
|
40
|
-
break
|
|
41
|
-
fi
|
|
42
|
-
done
|
|
43
|
-
|
|
44
|
-
# Defensive: if lib not loaded, exit silent (fail-soft per Nygard)
|
|
45
|
-
if ! declare -f emit_additional_context >/dev/null 2>&1; then
|
|
46
|
-
exit 0
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
# Override per-call
|
|
50
|
-
if [ "${SKIP_ASSERT_VERIFY_STEERING:-0}" = "1" ]; then
|
|
51
|
-
exit 0
|
|
52
|
-
fi
|
|
53
|
-
|
|
54
|
-
# === Steering content ===
|
|
55
|
-
STEERING_CONTENT="**Plain check:** Before I make a claim in this turn, I read the source first and cite it. If I cannot cite the source, I do not ship the claim.
|
|
56
|
-
|
|
57
|
-
**Check my claims before I write them:**
|
|
58
|
-
|
|
59
|
-
For any claim to the operator — a finding, a rating, a fix I recommend, why I think X caused Y, how many turns something will take, or a strong-word claim (wrong, broken, should, must, always) — I fill three parts:
|
|
60
|
-
|
|
61
|
-
1. **My claim** — the assertion in one sentence.
|
|
62
|
-
2. **Evidence** — the source I read, cited inline (file path, ticket number, git SHA, chronicle filename, standard section).
|
|
63
|
-
3. **Why the evidence supports the claim** — one sentence tying the source to the claim.
|
|
64
|
-
|
|
65
|
-
Add more when it helps:
|
|
66
|
-
|
|
67
|
-
4. **Confidence plus how many observations back it** — from Toulmin, *The Uses of Argument* (1958).
|
|
68
|
-
5. **What would falsify the claim** — from Popper, *The Logic of Scientific Discovery* (1959). A claim that cannot be falsified is a guess.
|
|
69
|
-
6. **Two or three alternative explanations that fit the same data** — from Peirce, *Deduction, Induction, and Hypothesis* (1878). Name each option before you pick one.
|
|
70
|
-
|
|
71
|
-
If I cannot fill all three, I do not ship the claim. I read the source first.
|
|
72
|
-
|
|
73
|
-
Per bassclef#559 and .claude/rules/assert-only-after-verify.md."
|
|
74
|
-
|
|
75
|
-
# === Emit envelope ===
|
|
76
|
-
emit_additional_context "UserPromptSubmit" "$STEERING_CONTENT"
|
|
77
|
-
exit 0
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# tier: lite
|
|
3
|
-
# bassclef-source-config-validate.sh — PreToolUse hook validating
|
|
4
|
-
# .bassclef-source.json writes against the bassclef schema.
|
|
5
|
-
#
|
|
6
|
-
# Wires per .claude/settings.json under hooks.PreToolUse with matcher
|
|
7
|
-
# "Edit|Write|MultiEdit". Reads the tool's event JSON from stdin, detects
|
|
8
|
-
# whether the target file is a `.bassclef-source.json`, validates against
|
|
9
|
-
# the schema, and BLOCKs (exit non-zero) on failure.
|
|
10
|
-
#
|
|
11
|
-
# Per ADR-015: JSON Schema draft-2020-12 with format support via ajv-formats.
|
|
12
|
-
# Pairs with: standards/bassclef-source-config.md
|
|
13
|
-
#
|
|
14
|
-
# Override: SKIP_BASSCLEF_SOURCE_VALIDATE=1 (logged to stderr).
|
|
15
|
-
#
|
|
16
|
-
# F77 fix (bassclef#743): probe ajv-cli before validation; distinguish
|
|
17
|
-
# offline / install failure from schema validation failure so the operator
|
|
18
|
-
# sees the real cause rather than a fake "validation failed" exit.
|
|
19
|
-
#
|
|
20
|
-
# F78 fix (bassclef#743): Edit/MultiEdit are PreToolUse — the file on disk
|
|
21
|
-
# is still pre-edit content. Synthesize post-edit content by replaying the
|
|
22
|
-
# event's edit operations against the on-disk content, then validate that.
|
|
23
|
-
# Write is unchanged — tool_input.content is the post-content.
|
|
24
|
-
|
|
25
|
-
set -u
|
|
26
|
-
|
|
27
|
-
# === Override path ===
|
|
28
|
-
|
|
29
|
-
if [ "${SKIP_BASSCLEF_SOURCE_VALIDATE:-}" = "1" ]; then
|
|
30
|
-
echo "[bassclef-source-config-validate.sh] SKIP_BASSCLEF_SOURCE_VALIDATE=1 — bypassing validation" >&2
|
|
31
|
-
exit 0
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
# === Read PreToolUse event ===
|
|
35
|
-
|
|
36
|
-
EVENT_JSON="$(cat)"
|
|
37
|
-
TOOL_NAME="$(printf '%s' "$EVENT_JSON" | jq -r '.tool_name // empty')"
|
|
38
|
-
TARGET_PATH="$(printf '%s' "$EVENT_JSON" | jq -r '.tool_input.file_path // empty')"
|
|
39
|
-
|
|
40
|
-
# Tool filter: only Edit/Write/MultiEdit trigger validation
|
|
41
|
-
case "$TOOL_NAME" in
|
|
42
|
-
Edit|Write|MultiEdit) ;;
|
|
43
|
-
*) exit 0 ;;
|
|
44
|
-
esac
|
|
45
|
-
|
|
46
|
-
# Empty path → pass-through
|
|
47
|
-
if [ -z "$TARGET_PATH" ]; then
|
|
48
|
-
exit 0
|
|
49
|
-
fi
|
|
50
|
-
|
|
51
|
-
# Path filter: only .bassclef-source.json files trigger validation
|
|
52
|
-
case "$TARGET_PATH" in
|
|
53
|
-
*/.bassclef-source.json|.bassclef-source.json) ;;
|
|
54
|
-
*) exit 0 ;;
|
|
55
|
-
esac
|
|
56
|
-
|
|
57
|
-
# === Resolve project dir + schema path ===
|
|
58
|
-
|
|
59
|
-
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
60
|
-
SCHEMA="${PROJECT_DIR}/standards/bassclef-source/schemas/bassclef-source.schema.json"
|
|
61
|
-
|
|
62
|
-
# If schema not present locally (consumer repo without bassclef-side
|
|
63
|
-
# files), pass-through — the hook is a defensive gate for bassclef-side
|
|
64
|
-
# edits, not for consumer-side edits where the schema isn't available.
|
|
65
|
-
if [ ! -f "$SCHEMA" ]; then
|
|
66
|
-
exit 0
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
# === Temp-file cleanup trap ===
|
|
70
|
-
|
|
71
|
-
TMP_JSON=""
|
|
72
|
-
cleanup() {
|
|
73
|
-
[ -n "$TMP_JSON" ] && rm -f "$TMP_JSON" 2>/dev/null
|
|
74
|
-
}
|
|
75
|
-
trap cleanup EXIT
|
|
76
|
-
|
|
77
|
-
# === F77 — Probe ajv-cli availability before validating ===
|
|
78
|
-
#
|
|
79
|
-
# Distinguish offline / install-failure from schema-validation failure.
|
|
80
|
-
# Without this probe, the error filter at the end strips npm/npx warnings
|
|
81
|
-
# and the operator sees what looks like a schema-validation failure even
|
|
82
|
-
# when the real cause is network or install. Pass-through on probe failure
|
|
83
|
-
# with a loud stderr explanation; do NOT block on infrastructure.
|
|
84
|
-
|
|
85
|
-
# Probe uses `ajv help` because `ajv --version` exits non-zero in
|
|
86
|
-
# ajv-cli@5's CLI parser (unrecognized option). help is the cheapest
|
|
87
|
-
# parse-and-print that exits 0 when the binary is installed.
|
|
88
|
-
AJV_PROBE_OUTPUT="$(npx -y -p ajv-cli@5 ajv help 2>&1)"
|
|
89
|
-
AJV_PROBE_EXIT=$?
|
|
90
|
-
if [ "$AJV_PROBE_EXIT" -ne 0 ]; then
|
|
91
|
-
echo >&2
|
|
92
|
-
echo "[bassclef-source-config-validate.sh] ajv-cli unavailable — validation skipped" >&2
|
|
93
|
-
echo "[bassclef-source-config-validate.sh] Likely cause: offline, npm registry unreachable, or first-run install failed." >&2
|
|
94
|
-
echo "[bassclef-source-config-validate.sh] Target: $TARGET_PATH" >&2
|
|
95
|
-
echo "[bassclef-source-config-validate.sh] Probe output (last 5 lines):" >&2
|
|
96
|
-
echo "$AJV_PROBE_OUTPUT" | tail -5 | sed 's/^/ /' >&2
|
|
97
|
-
echo "[bassclef-source-config-validate.sh] To pre-cache: run \`npx -y -p ajv-cli@5 -p ajv-formats@2 ajv --version\` while online." >&2
|
|
98
|
-
echo "[bassclef-source-config-validate.sh] To bypass deliberately: \`SKIP_BASSCLEF_SOURCE_VALIDATE=1 <command>\`" >&2
|
|
99
|
-
echo >&2
|
|
100
|
-
exit 0 # Pass-through; don't block on infrastructure
|
|
101
|
-
fi
|
|
102
|
-
|
|
103
|
-
# === F78 — Synthesize post-edit content per tool ===
|
|
104
|
-
#
|
|
105
|
-
# Write: tool_input.content IS the post-content (current shape OK).
|
|
106
|
-
# Edit: apply tool_input.{old_string, new_string, replace_all} to on-disk
|
|
107
|
-
# content; validate the result.
|
|
108
|
-
# MultiEdit: apply each entry of tool_input.edits[] in order; validate the
|
|
109
|
-
# final result.
|
|
110
|
-
|
|
111
|
-
case "$TOOL_NAME" in
|
|
112
|
-
Write)
|
|
113
|
-
# ajv-cli detects format from file extension; macOS mktemp -t
|
|
114
|
-
# appends random suffix AFTER the template, so .json must be
|
|
115
|
-
# explicitly added via mv to ensure it's the trailing extension.
|
|
116
|
-
TMP_JSON_RAW="$(mktemp -t bassclef-source-validate-XXXXXX)"
|
|
117
|
-
TMP_JSON="${TMP_JSON_RAW}.json"
|
|
118
|
-
mv "$TMP_JSON_RAW" "$TMP_JSON"
|
|
119
|
-
printf '%s' "$EVENT_JSON" | jq -r '.tool_input.content // empty' > "$TMP_JSON"
|
|
120
|
-
VALIDATE_TARGET="$TMP_JSON"
|
|
121
|
-
;;
|
|
122
|
-
Edit|MultiEdit)
|
|
123
|
-
# Edit on non-existent file — the Edit tool will fail with its own
|
|
124
|
-
# error; pass-through quietly.
|
|
125
|
-
if [ ! -f "$TARGET_PATH" ]; then
|
|
126
|
-
exit 0
|
|
127
|
-
fi
|
|
128
|
-
# ajv-cli detects format from file extension; macOS mktemp -t
|
|
129
|
-
# appends random suffix AFTER the template, so .json must be
|
|
130
|
-
# explicitly added via mv to ensure it's the trailing extension.
|
|
131
|
-
TMP_JSON_RAW="$(mktemp -t bassclef-source-validate-XXXXXX)"
|
|
132
|
-
TMP_JSON="${TMP_JSON_RAW}.json"
|
|
133
|
-
mv "$TMP_JSON_RAW" "$TMP_JSON"
|
|
134
|
-
|
|
135
|
-
# Replay edits via python3. Pipes EVENT_JSON to python's stdin;
|
|
136
|
-
# passes target path + tool name as argv. python writes post-edit
|
|
137
|
-
# content to stdout, which bash captures into TMP_JSON.
|
|
138
|
-
#
|
|
139
|
-
# Exit code from python:
|
|
140
|
-
# 0 — replay succeeded; TMP_JSON has post-edit content
|
|
141
|
-
# 2 — old_string not in content; the Edit tool will fail with the
|
|
142
|
-
# same error so pass-through quietly
|
|
143
|
-
# other — unexpected; pass-through (don't block on hook bugs)
|
|
144
|
-
REPLAY_EXIT=0
|
|
145
|
-
printf '%s' "$EVENT_JSON" | python3 -c '
|
|
146
|
-
import json, sys
|
|
147
|
-
event = json.load(sys.stdin)
|
|
148
|
-
target = sys.argv[1]
|
|
149
|
-
tool_name = sys.argv[2]
|
|
150
|
-
with open(target) as f:
|
|
151
|
-
content = f.read()
|
|
152
|
-
if tool_name == "Edit":
|
|
153
|
-
edits = [event["tool_input"]]
|
|
154
|
-
else: # MultiEdit
|
|
155
|
-
edits = event["tool_input"].get("edits", [])
|
|
156
|
-
for e in edits:
|
|
157
|
-
old = e.get("old_string", "")
|
|
158
|
-
new = e.get("new_string", "")
|
|
159
|
-
if old not in content:
|
|
160
|
-
sys.exit(2)
|
|
161
|
-
replace_all = e.get("replace_all", False)
|
|
162
|
-
content = content.replace(old, new) if replace_all else content.replace(old, new, 1)
|
|
163
|
-
sys.stdout.write(content)
|
|
164
|
-
' "$TARGET_PATH" "$TOOL_NAME" > "$TMP_JSON" 2>/dev/null || REPLAY_EXIT=$?
|
|
165
|
-
|
|
166
|
-
if [ "$REPLAY_EXIT" -ne 0 ]; then
|
|
167
|
-
# Replay failed — the underlying tool will fail too. Pass-through.
|
|
168
|
-
exit 0
|
|
169
|
-
fi
|
|
170
|
-
VALIDATE_TARGET="$TMP_JSON"
|
|
171
|
-
;;
|
|
172
|
-
esac
|
|
173
|
-
|
|
174
|
-
# === Validate via ajv-cli ===
|
|
175
|
-
|
|
176
|
-
VALIDATION_OUTPUT="$(npx -y -p ajv-cli@5 -p ajv-formats@2 ajv validate \
|
|
177
|
-
-s "$SCHEMA" \
|
|
178
|
-
-d "$VALIDATE_TARGET" \
|
|
179
|
-
--spec=draft2020 \
|
|
180
|
-
-c ajv-formats 2>&1)"
|
|
181
|
-
EXIT_CODE=$?
|
|
182
|
-
|
|
183
|
-
if [ "$EXIT_CODE" -eq 0 ]; then
|
|
184
|
-
exit 0
|
|
185
|
-
fi
|
|
186
|
-
|
|
187
|
-
# === BLOCK on validation failure ===
|
|
188
|
-
|
|
189
|
-
# Strip noisy npm warnings; surface only ajv's actual error lines.
|
|
190
|
-
# (Network failures already short-circuited above via the F77 probe.)
|
|
191
|
-
ERR=$(echo "$VALIDATION_OUTPUT" | grep -vE '^npm |^npx |^$' | head -10)
|
|
192
|
-
|
|
193
|
-
cat >&2 <<EOF
|
|
194
|
-
|
|
195
|
-
🛑 BLOCKED — .bassclef-source.json validation failed
|
|
196
|
-
|
|
197
|
-
Path: $TARGET_PATH
|
|
198
|
-
Schema: standards/bassclef-source/schemas/bassclef-source.schema.json
|
|
199
|
-
Standard: standards/bassclef-source-config.md
|
|
200
|
-
|
|
201
|
-
ajv-cli error:
|
|
202
|
-
$ERR
|
|
203
|
-
|
|
204
|
-
Resolution paths:
|
|
205
|
-
1. Fix the data to match the schema (preferred)
|
|
206
|
-
— see standards/bassclef-source-config.md § Schema for field reference
|
|
207
|
-
— see scripts/tests/fixtures/bassclef-source/*.json for working examples
|
|
208
|
-
2. If the schema is genuinely wrong, open a PR amending it (rare)
|
|
209
|
-
3. Override with SKIP_BASSCLEF_SOURCE_VALIDATE=1 (logged; rare; not for normal work)
|
|
210
|
-
|
|
211
|
-
Per .claude/rules/blocked-items.md: silence is not deferral.
|
|
212
|
-
|
|
213
|
-
EOF
|
|
214
|
-
|
|
215
|
-
exit 3
|