@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,147 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# tier: lite
|
|
3
|
-
# temperance-gate.sh — mechanical /temperance firing at decision boundaries
|
|
4
|
-
# (bassclef#1147 T15)
|
|
5
|
-
#
|
|
6
|
-
# Fires on PreToolUse Edit|Write|MultiEdit. MVP scope: ONE trigger active —
|
|
7
|
-
# "load-bearing path edit" (catches edits to .claude/, standards/, lib/state.sh,
|
|
8
|
-
# SKILL.md when no fresh temperance marker exists for the session).
|
|
9
|
-
#
|
|
10
|
-
# Other triggers from bet 2026-06-18b WU-7 acceptance — explain-mode entry,
|
|
11
|
-
# chain-anchor entry, non-stacked-branch first edit, post-compaction recovery —
|
|
12
|
-
# are scaffolded as stubs below; design + implementation deferred to follow-on
|
|
13
|
-
# tickets pending operator review of the trigger taxonomy.
|
|
14
|
-
#
|
|
15
|
-
# Other triggers from ticket #1147 — first-source-edit per session, N
|
|
16
|
-
# consecutive edits without temperance, substrate proposal without catalog
|
|
17
|
-
# browse, diagnose-repro marker absence — also deferred to follow-on.
|
|
18
|
-
#
|
|
19
|
-
# Coordinates with pre-build-gate.sh — that hook already handles
|
|
20
|
-
# non-stacked-branch first edit temperance. This hook adds the load-bearing
|
|
21
|
-
# trigger which pre-build-gate does not cover.
|
|
22
|
-
#
|
|
23
|
-
# Exit codes:
|
|
24
|
-
# 0 — pass (override, skip, marker present, non-trigger)
|
|
25
|
-
# 3 — BLOCK (load-bearing edit without temperance marker)
|
|
26
|
-
#
|
|
27
|
-
# Override: SKIP_TEMPERANCE_GATE=1 — logged via trace-helper (bassclef idiom).
|
|
28
|
-
|
|
29
|
-
set -u
|
|
30
|
-
|
|
31
|
-
# --- Override path ---
|
|
32
|
-
if [ "${SKIP_TEMPERANCE_GATE:-0}" = "1" ]; then
|
|
33
|
-
echo "SKIP_TEMPERANCE_GATE=1 — temperance gate bypassed" >&2
|
|
34
|
-
exit 0
|
|
35
|
-
fi
|
|
36
|
-
|
|
37
|
-
# --- Read stdin (fail-soft on malformed JSON) ---
|
|
38
|
-
INPUT=$(cat)
|
|
39
|
-
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null || echo "")
|
|
40
|
-
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""' 2>/dev/null || echo "")
|
|
41
|
-
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"' 2>/dev/null || echo "unknown")
|
|
42
|
-
CWD=$(echo "$INPUT" | jq -r '.cwd // "."' 2>/dev/null || echo ".")
|
|
43
|
-
|
|
44
|
-
if [ -z "$TOOL_NAME" ]; then
|
|
45
|
-
exit 0
|
|
46
|
-
fi
|
|
47
|
-
|
|
48
|
-
# --- Tool-name matcher ---
|
|
49
|
-
case "$TOOL_NAME" in
|
|
50
|
-
Edit|Write|MultiEdit) ;;
|
|
51
|
-
*) exit 0 ;;
|
|
52
|
-
esac
|
|
53
|
-
|
|
54
|
-
# --- Trigger 1: Load-bearing path edit (MVP — only active trigger) ---
|
|
55
|
-
# Detects edits to substrate paths whose changes carry blast-radius beyond a
|
|
56
|
-
# normal source edit. The temperance question forces the agent to articulate
|
|
57
|
-
# "right thing" + "right way" before the edit lands.
|
|
58
|
-
|
|
59
|
-
is_load_bearing() {
|
|
60
|
-
local path="$1"
|
|
61
|
-
case "$path" in
|
|
62
|
-
*/.claude/*) return 0 ;;
|
|
63
|
-
*/standards/*) return 0 ;;
|
|
64
|
-
*/lib/state.sh) return 0 ;;
|
|
65
|
-
*/SKILL.md) return 0 ;;
|
|
66
|
-
.claude/*) return 0 ;;
|
|
67
|
-
standards/*) return 0 ;;
|
|
68
|
-
lib/state.sh) return 0 ;;
|
|
69
|
-
*) return 1 ;;
|
|
70
|
-
esac
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
# --- Trigger stubs (deferred to follow-on tickets) ---
|
|
74
|
-
# Each stub is a function that always returns "no trigger" — the design +
|
|
75
|
-
# implementation lands in a follow-on ticket. Listed here so reviewers can
|
|
76
|
-
# see the planned trigger surface without reading the goal doc.
|
|
77
|
-
|
|
78
|
-
trigger_explain_mode() { return 1; } # bet 2026-06-18b WU-7
|
|
79
|
-
trigger_chain_anchor() { return 1; } # bet 2026-06-18b WU-7
|
|
80
|
-
trigger_non_stacked_first_edit() { return 1; } # bet 2026-06-18b WU-7 (overlaps pre-build-gate; routing TBD)
|
|
81
|
-
trigger_post_compaction() { return 1; } # bet 2026-06-18b WU-7
|
|
82
|
-
trigger_n_consecutive_edits() { return 1; } # ticket #1147 trigger 3
|
|
83
|
-
trigger_substrate_proposal() { return 1; } # ticket #1147 trigger 5
|
|
84
|
-
trigger_diagnose_repro() { return 1; } # ticket #1147 trigger 6
|
|
85
|
-
|
|
86
|
-
# --- Marker check for active trigger ---
|
|
87
|
-
if ! is_load_bearing "$FILE_PATH"; then
|
|
88
|
-
exit 0
|
|
89
|
-
fi
|
|
90
|
-
|
|
91
|
-
# Marker convention shared with pre-build-gate.sh — same temperance markers.
|
|
92
|
-
# A fresh temperance marker (any branch this session) clears all triggers.
|
|
93
|
-
MARKER_DIR="${CWD}/state/markers/temperance"
|
|
94
|
-
SESSION_MARKER="${MARKER_DIR}/${SESSION_ID}.marker"
|
|
95
|
-
|
|
96
|
-
if [ -f "$SESSION_MARKER" ]; then
|
|
97
|
-
exit 0
|
|
98
|
-
fi
|
|
99
|
-
|
|
100
|
-
# Also clear when any temperance marker exists for the current branch (the
|
|
101
|
-
# pre-build-gate convention writes branch-slug markers).
|
|
102
|
-
if [ -d "$MARKER_DIR" ] && [ -n "$(ls -A "$MARKER_DIR" 2>/dev/null)" ]; then
|
|
103
|
-
# Marker exists for some branch this session — pass. (Reviewer: this is
|
|
104
|
-
# the loose check; tighter "matching branch" check is a follow-on.)
|
|
105
|
-
exit 0
|
|
106
|
-
fi
|
|
107
|
-
|
|
108
|
-
# --- BLOCK ---
|
|
109
|
-
cat >&2 <<BLOCK
|
|
110
|
-
============================================
|
|
111
|
-
🛑 temperance-gate — BLOCKED (essential path)
|
|
112
|
-
============================================
|
|
113
|
-
|
|
114
|
-
About to edit an essential substrate path without a fresh temperance marker:
|
|
115
|
-
${FILE_PATH}
|
|
116
|
-
|
|
117
|
-
The temperance question:
|
|
118
|
-
Am I building the RIGHT THING?
|
|
119
|
-
Am I building it the RIGHT WAY?
|
|
120
|
-
|
|
121
|
-
Essential paths (.claude/, standards/, lib/state.sh, SKILL.md) carry
|
|
122
|
-
more impact than a normal source edit. Pause and answer the two
|
|
123
|
-
questions before the edit lands.
|
|
124
|
-
|
|
125
|
-
To proceed:
|
|
126
|
-
1. State the answers (right thing / right way / what could break).
|
|
127
|
-
2. Write a temperance marker:
|
|
128
|
-
mkdir -p ${MARKER_DIR}
|
|
129
|
-
cat > ${SESSION_MARKER} <<EOF
|
|
130
|
-
{"session_id":"${SESSION_ID}","entered_at":"\$(date -u +%Y-%m-%dT%H:%M:%SZ)","question_answered":"yes"}
|
|
131
|
-
EOF
|
|
132
|
-
3. Re-run the edit — the marker now satisfies the gate.
|
|
133
|
-
|
|
134
|
-
Override (logged):
|
|
135
|
-
SKIP_TEMPERANCE_GATE=1 <command>
|
|
136
|
-
|
|
137
|
-
Note: this is the MVP trigger (load-bearing path). Other triggers
|
|
138
|
-
(explain-mode, chain-anchor, post-compaction, non-stacked first edit,
|
|
139
|
-
N consecutive edits, substrate proposal, diagnose-repro) are scaffolded
|
|
140
|
-
in this hook but inactive — they ship in follow-on tickets.
|
|
141
|
-
|
|
142
|
-
Per bassclef#1147 T15 (this hook) and .claude/skills/temperance/SKILL.md
|
|
143
|
-
(the methodology this gate enforces mechanically).
|
|
144
|
-
============================================
|
|
145
|
-
BLOCK
|
|
146
|
-
|
|
147
|
-
exit 3
|
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# tier: lite
|
|
3
|
-
# testing-tier-enforce.sh — per-path testing-tier enforcement (bassclef#1037)
|
|
4
|
-
#
|
|
5
|
-
# Reads tier config from .claude/bassclef-configs.jsonc (testing.global_floor
|
|
6
|
-
# + testing.path_matchers); per-file overrides from .claude/hooks/testing-tier-
|
|
7
|
-
# enforce-allowlist.txt (grandfather grace) and state/testing-tier-overrides.jsonl
|
|
8
|
-
# (operator-filed per-file tier flip — future; not yet read in v1).
|
|
9
|
-
#
|
|
10
|
-
# Tier semantics (per .claude/rules/testing-tier-config.md):
|
|
11
|
-
# 0 strict TDD — BLOCK when sibling test absent OR test mtime < source mtime
|
|
12
|
-
# 1 test-with — WARN when sibling test absent
|
|
13
|
-
# 2 smoke — silent (advisory only)
|
|
14
|
-
# 3 manual verify — silent
|
|
15
|
-
#
|
|
16
|
-
# Path-matcher precedence: array order is most-specific to least-specific; first
|
|
17
|
-
# match wins. When no entry matches, testing.global_floor applies (default 1).
|
|
18
|
-
#
|
|
19
|
-
# Sibling test convention (Tier 0 only):
|
|
20
|
-
# .claude/hooks/<name>.sh -> .claude/hooks/tests/<name>.test.sh
|
|
21
|
-
# standards/.../schemas/<name>.json -> standards/.../schemas/tests/<name>.test.sh
|
|
22
|
-
# lib/<name>.sh -> lib/tests/<name>.test.sh
|
|
23
|
-
#
|
|
24
|
-
# Override: SKIP_TESTING_TIER_ENFORCE=1 — bypass entire hook for one command.
|
|
25
|
-
# Logged via trace-helper.sh per bassclef's existing override idiom.
|
|
26
|
-
#
|
|
27
|
-
# Wired into pre-commit-gate.sh chain. Also invokable standalone for tests
|
|
28
|
-
# (set TESTING_TIER_FILES env var with newline-separated paths to check
|
|
29
|
-
# instead of reading staged files from git).
|
|
30
|
-
|
|
31
|
-
set -u
|
|
32
|
-
|
|
33
|
-
REPO_ROOT="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
|
|
34
|
-
CONFIG_FILE="$REPO_ROOT/.claude/bassclef-configs.jsonc"
|
|
35
|
-
ALLOWLIST_FILE="$REPO_ROOT/.claude/hooks/testing-tier-enforce-allowlist.txt"
|
|
36
|
-
|
|
37
|
-
# Override path
|
|
38
|
-
if [ "${SKIP_TESTING_TIER_ENFORCE:-0}" = "1" ]; then
|
|
39
|
-
if [ -f "$REPO_ROOT/.claude/hooks/trace-helper.sh" ]; then
|
|
40
|
-
# shellcheck disable=SC1091
|
|
41
|
-
source "$REPO_ROOT/.claude/hooks/trace-helper.sh" 2>/dev/null || true
|
|
42
|
-
type trace_log >/dev/null 2>&1 && trace_log "testing-tier-enforce" "SKIP_TESTING_TIER_ENFORCE=1 bypass"
|
|
43
|
-
fi
|
|
44
|
-
exit 0
|
|
45
|
-
fi
|
|
46
|
-
|
|
47
|
-
# resolve_tier_for_path <relative-path> -> echoes tier int (0/1/2/3)
|
|
48
|
-
resolve_tier_for_path() {
|
|
49
|
-
local path="$1"
|
|
50
|
-
if [ ! -f "$CONFIG_FILE" ]; then
|
|
51
|
-
echo 1
|
|
52
|
-
return
|
|
53
|
-
fi
|
|
54
|
-
local json
|
|
55
|
-
json=$(perl -pe 's,^\s*//.*$,,; s,/\*.*?\*/,,g' "$CONFIG_FILE" 2>/dev/null)
|
|
56
|
-
local global_floor
|
|
57
|
-
global_floor=$(echo "$json" | jq -r '.testing.global_floor // 1' 2>/dev/null)
|
|
58
|
-
[ -z "$global_floor" ] || [ "$global_floor" = "null" ] && global_floor=1
|
|
59
|
-
|
|
60
|
-
# Iterate matchers in array order; first shell-glob match wins.
|
|
61
|
-
local count
|
|
62
|
-
count=$(echo "$json" | jq -r '.testing.path_matchers | length // 0' 2>/dev/null)
|
|
63
|
-
[ -z "$count" ] || [ "$count" = "null" ] && count=0
|
|
64
|
-
|
|
65
|
-
local i=0
|
|
66
|
-
while [ "$i" -lt "$count" ]; do
|
|
67
|
-
local pattern tier
|
|
68
|
-
pattern=$(echo "$json" | jq -r ".testing.path_matchers[$i].match" 2>/dev/null)
|
|
69
|
-
tier=$(echo "$json" | jq -r ".testing.path_matchers[$i].tier" 2>/dev/null)
|
|
70
|
-
# Shell glob via case statement; ** treated as * (bash glob limitation OK for v1)
|
|
71
|
-
# Replace ** with * for case matching
|
|
72
|
-
local case_pattern="${pattern//\*\*/\*}"
|
|
73
|
-
case "$path" in
|
|
74
|
-
$case_pattern)
|
|
75
|
-
echo "$tier"
|
|
76
|
-
return
|
|
77
|
-
;;
|
|
78
|
-
esac
|
|
79
|
-
i=$((i + 1))
|
|
80
|
-
done
|
|
81
|
-
|
|
82
|
-
echo "$global_floor"
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
# is_allowlisted_in_grace <relative-path> -> exit 0 if grace covers
|
|
86
|
-
is_allowlisted_in_grace() {
|
|
87
|
-
local path="$1"
|
|
88
|
-
[ ! -f "$ALLOWLIST_FILE" ] && return 1
|
|
89
|
-
local now
|
|
90
|
-
now=$(date -u +%s)
|
|
91
|
-
while IFS='|' read -r entry_path grace_until; do
|
|
92
|
-
[ -z "${entry_path:-}" ] && continue
|
|
93
|
-
case "$entry_path" in '#'*|'') continue ;; esac
|
|
94
|
-
# Trim whitespace
|
|
95
|
-
entry_path="${entry_path# }"; entry_path="${entry_path% }"
|
|
96
|
-
grace_until="${grace_until# }"; grace_until="${grace_until% }"
|
|
97
|
-
[ "$entry_path" != "$path" ] && continue
|
|
98
|
-
local grace_epoch=""
|
|
99
|
-
grace_epoch=$(date -u -j -f "%Y-%m-%dT%H:%M:%SZ" "$grace_until" +%s 2>/dev/null) || \
|
|
100
|
-
grace_epoch=$(date -u -d "$grace_until" +%s 2>/dev/null) || \
|
|
101
|
-
grace_epoch=""
|
|
102
|
-
[ -z "$grace_epoch" ] && return 1
|
|
103
|
-
[ "$now" -lt "$grace_epoch" ] && return 0
|
|
104
|
-
return 1
|
|
105
|
-
done < "$ALLOWLIST_FILE"
|
|
106
|
-
return 1
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
# sibling_test_path <source-relative-path> -> echoes expected test path
|
|
110
|
-
sibling_test_path() {
|
|
111
|
-
local src="$1"
|
|
112
|
-
local dir base stem
|
|
113
|
-
dir=$(dirname "$src")
|
|
114
|
-
base=$(basename "$src")
|
|
115
|
-
stem="${base%.*}"
|
|
116
|
-
echo "${dir}/tests/${stem}.test.sh"
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
# Get file mtime as epoch seconds (portable Mac + Linux).
|
|
120
|
-
# GNU stat is tried first because BSD-style `stat -f %m` on Linux GNU
|
|
121
|
-
# does NOT fail — it succeeds with mount-point output (%m means mount
|
|
122
|
-
# point under GNU's -f filesystem mode), which would silently corrupt
|
|
123
|
-
# the mtime comparison. GNU-first; BSD fallback for macOS.
|
|
124
|
-
file_mtime() {
|
|
125
|
-
local f="$1"
|
|
126
|
-
stat -c %Y "$f" 2>/dev/null || stat -f %m "$f" 2>/dev/null
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
# check_tier_0_violation <relative-path> -> exit 0 if ok, 1 if violation
|
|
130
|
-
# Sets REASON var with violation description.
|
|
131
|
-
check_tier_0_violation() {
|
|
132
|
-
local src="$1"
|
|
133
|
-
local test_rel test_abs src_abs
|
|
134
|
-
test_rel=$(sibling_test_path "$src")
|
|
135
|
-
test_abs="$REPO_ROOT/$test_rel"
|
|
136
|
-
src_abs="$REPO_ROOT/$src"
|
|
137
|
-
|
|
138
|
-
if [ ! -f "$test_abs" ]; then
|
|
139
|
-
REASON="Tier 0 source has no sibling test at $test_rel — write the test first per TDD discipline"
|
|
140
|
-
return 1
|
|
141
|
-
fi
|
|
142
|
-
local src_mtime test_mtime
|
|
143
|
-
src_mtime=$(file_mtime "$src_abs")
|
|
144
|
-
test_mtime=$(file_mtime "$test_abs")
|
|
145
|
-
if [ -z "$src_mtime" ] || [ -z "$test_mtime" ]; then
|
|
146
|
-
REASON="Tier 0 mtime check failed: could not read mtimes (src=$src test=$test_rel)"
|
|
147
|
-
return 1
|
|
148
|
-
fi
|
|
149
|
-
if [ "$test_mtime" -lt "$src_mtime" ]; then
|
|
150
|
-
REASON="Tier 0 source modified ($src) but test ($test_rel) mtime did not advance — write the test before the source per TDD discipline"
|
|
151
|
-
return 1
|
|
152
|
-
fi
|
|
153
|
-
return 0
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
main() {
|
|
157
|
-
local files
|
|
158
|
-
if [ -n "${TESTING_TIER_FILES:-}" ]; then
|
|
159
|
-
files="$TESTING_TIER_FILES"
|
|
160
|
-
else
|
|
161
|
-
# Read PreToolUse hook payload (jsonpath .tool_input.command) from stdin if present
|
|
162
|
-
if [ ! -t 0 ]; then
|
|
163
|
-
local input command
|
|
164
|
-
input=$(cat 2>/dev/null || echo '')
|
|
165
|
-
if [ -n "$input" ]; then
|
|
166
|
-
command=$(echo "$input" | jq -r '.tool_input.command // ""' 2>/dev/null || echo '')
|
|
167
|
-
if ! echo "$command" | grep -q 'git commit'; then
|
|
168
|
-
exit 0
|
|
169
|
-
fi
|
|
170
|
-
fi
|
|
171
|
-
fi
|
|
172
|
-
files=$(git -C "$REPO_ROOT" diff --cached --name-only --diff-filter=AM 2>/dev/null || echo '')
|
|
173
|
-
fi
|
|
174
|
-
|
|
175
|
-
[ -z "$files" ] && exit 0
|
|
176
|
-
|
|
177
|
-
local blocks=()
|
|
178
|
-
local warnings=()
|
|
179
|
-
|
|
180
|
-
while IFS= read -r f; do
|
|
181
|
-
[ -z "$f" ] && continue
|
|
182
|
-
local tier
|
|
183
|
-
tier=$(resolve_tier_for_path "$f")
|
|
184
|
-
case "$tier" in
|
|
185
|
-
0)
|
|
186
|
-
if is_allowlisted_in_grace "$f"; then
|
|
187
|
-
continue
|
|
188
|
-
fi
|
|
189
|
-
REASON=""
|
|
190
|
-
if ! check_tier_0_violation "$f"; then
|
|
191
|
-
blocks+=("$f: $REASON")
|
|
192
|
-
fi
|
|
193
|
-
;;
|
|
194
|
-
1)
|
|
195
|
-
local exp_test
|
|
196
|
-
exp_test=$(sibling_test_path "$f")
|
|
197
|
-
if [ ! -f "$REPO_ROOT/$exp_test" ]; then
|
|
198
|
-
warnings+=("$f: Tier 1 — no sibling test at $exp_test (expected; not BLOCKing)")
|
|
199
|
-
fi
|
|
200
|
-
;;
|
|
201
|
-
2|3)
|
|
202
|
-
: ;;
|
|
203
|
-
esac
|
|
204
|
-
done <<< "$files"
|
|
205
|
-
|
|
206
|
-
if [ "${#warnings[@]}" -gt 0 ]; then
|
|
207
|
-
# Advisory only — write to stdout so the Claude Code PreToolUse
|
|
208
|
-
# wrapper does not treat non-empty stderr as an error and block
|
|
209
|
-
# the git commit. BLOCK output below stays on stderr (real error).
|
|
210
|
-
echo ""
|
|
211
|
-
echo "TESTING-TIER WARN (Tier 1):"
|
|
212
|
-
for w in "${warnings[@]}"; do echo " $w"; done
|
|
213
|
-
fi
|
|
214
|
-
|
|
215
|
-
if [ "${#blocks[@]}" -gt 0 ]; then
|
|
216
|
-
echo "" >&2
|
|
217
|
-
echo "============================================" >&2
|
|
218
|
-
echo "🛑 TESTING-TIER ENFORCE — BLOCKED 🛑" >&2
|
|
219
|
-
echo "============================================" >&2
|
|
220
|
-
for b in "${blocks[@]}"; do echo " $b" >&2; done
|
|
221
|
-
echo "" >&2
|
|
222
|
-
echo "Per .claude/rules/testing-tier-config.md (bassclef#1037)." >&2
|
|
223
|
-
echo "Tier framework: 0 strict TDD / 1 WARN / 2 smoke / 3 manual verify." >&2
|
|
224
|
-
echo "Resolution: write the sibling test FIRST, then re-stage and commit." >&2
|
|
225
|
-
echo "Override (logged): SKIP_TESTING_TIER_ENFORCE=1 <command>" >&2
|
|
226
|
-
echo "============================================" >&2
|
|
227
|
-
exit 1
|
|
228
|
-
fi
|
|
229
|
-
|
|
230
|
-
exit 0
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
main
|
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# tier: lite
|
|
3
|
-
# install-class: dual
|
|
4
|
-
# Turn-prose grade measure — Stop hook that reads the last assistant message
|
|
5
|
-
# from the transcript, computes its Flesch-Kincaid grade level, and writes
|
|
6
|
-
# the number to a marker file at state/markers/turn-grade/last-turn.json.
|
|
7
|
-
#
|
|
8
|
-
# The pre-turn plain-english-steering.sh reads the marker and injects the
|
|
9
|
-
# number into the steering block on the next prompt. Turns a soft "aim for
|
|
10
|
-
# grade 10" reminder into a concrete number the agent sees each turn.
|
|
11
|
-
#
|
|
12
|
-
# Complements turn-prose-kiss-check.sh — that hook scans for BLOCK words.
|
|
13
|
-
# This hook measures reading level.
|
|
14
|
-
#
|
|
15
|
-
# Input (stdin): JSON with transcript_path + session_id
|
|
16
|
-
# Output: writes state/markers/turn-grade/last-turn.json
|
|
17
|
-
# Exit: 0 always (measurement only, no gate)
|
|
18
|
-
#
|
|
19
|
-
# Override: SKIP_TURN_PROSE_GRADE=1
|
|
20
|
-
|
|
21
|
-
set +e
|
|
22
|
-
|
|
23
|
-
# === Override path ===
|
|
24
|
-
if [ "${SKIP_TURN_PROSE_GRADE:-0}" = "1" ]; then
|
|
25
|
-
exit 0
|
|
26
|
-
fi
|
|
27
|
-
|
|
28
|
-
# === Parse stdin JSON ===
|
|
29
|
-
STDIN_JSON=$(cat 2>/dev/null || echo '{}')
|
|
30
|
-
TRANSCRIPT_PATH=$(echo "$STDIN_JSON" | jq -r '.transcript_path // ""' 2>/dev/null)
|
|
31
|
-
|
|
32
|
-
[ -z "$TRANSCRIPT_PATH" ] && exit 0
|
|
33
|
-
[ ! -f "$TRANSCRIPT_PATH" ] && exit 0
|
|
34
|
-
|
|
35
|
-
# === Extract last assistant text ===
|
|
36
|
-
LAST_ASSISTANT_LINE=$(grep '"type":"assistant"' "$TRANSCRIPT_PATH" 2>/dev/null | tail -1)
|
|
37
|
-
[ -z "$LAST_ASSISTANT_LINE" ] && exit 0
|
|
38
|
-
|
|
39
|
-
MESSAGE_TEXT=$(echo "$LAST_ASSISTANT_LINE" \
|
|
40
|
-
| jq -r '.message.content[]? | select(.type == "text") | .text' 2>/dev/null)
|
|
41
|
-
[ -z "$MESSAGE_TEXT" ] && exit 0
|
|
42
|
-
|
|
43
|
-
# === Strip code blocks + inline backticks + URLs (prose only) ===
|
|
44
|
-
SCRUBBED=$(echo "$MESSAGE_TEXT" | awk '
|
|
45
|
-
BEGIN { in_code = 0 }
|
|
46
|
-
/^```/ { in_code = !in_code; next }
|
|
47
|
-
in_code { next }
|
|
48
|
-
{ print }
|
|
49
|
-
' | sed -E 's/`[^`]*`//g; s|https?://[^ )]+||g')
|
|
50
|
-
|
|
51
|
-
# === Compute Flesch-Kincaid grade via python ===
|
|
52
|
-
# Try textstat first (accurate; well-tested syllable counter). Fall back to
|
|
53
|
-
# hand-rolled formula when textstat is not installed. Zero break for adopters
|
|
54
|
-
# who have not installed the library.
|
|
55
|
-
# Per Ship 1 #1312: also compute per-sentence grade + top 3 offenders that
|
|
56
|
-
# exceed grade 10. Output as JSON for bash to inject into marker.
|
|
57
|
-
GRADE_OUTPUT=$(printf '%s' "$SCRUBBED" | python3 -c '
|
|
58
|
-
import sys, re, json
|
|
59
|
-
|
|
60
|
-
text = sys.stdin.read()
|
|
61
|
-
|
|
62
|
-
sentences = [s for s in re.split(r"[.!?]+", text) if s.strip()]
|
|
63
|
-
words = re.findall(r"\b[a-zA-Z][a-zA-Z\x27]*\b", text)
|
|
64
|
-
|
|
65
|
-
if len(words) < 20 or not sentences:
|
|
66
|
-
print("SKIP")
|
|
67
|
-
sys.exit(0)
|
|
68
|
-
|
|
69
|
-
# Fallback syllable counter — always defined so per-sentence path works
|
|
70
|
-
def count_syllables(word):
|
|
71
|
-
word = re.sub(r"[^a-z]", "", word.lower())
|
|
72
|
-
if not word:
|
|
73
|
-
return 0
|
|
74
|
-
vowels = "aeiouy"
|
|
75
|
-
count = 0
|
|
76
|
-
prev_was_vowel = False
|
|
77
|
-
for c in word:
|
|
78
|
-
is_vowel = c in vowels
|
|
79
|
-
if is_vowel and not prev_was_vowel:
|
|
80
|
-
count += 1
|
|
81
|
-
prev_was_vowel = is_vowel
|
|
82
|
-
if word.endswith("e") and count > 1:
|
|
83
|
-
count -= 1
|
|
84
|
-
return max(1, count)
|
|
85
|
-
|
|
86
|
-
# Preferred path — textstat library
|
|
87
|
-
try:
|
|
88
|
-
from textstat import flesch_kincaid_grade, syllable_count
|
|
89
|
-
grade = flesch_kincaid_grade(text)
|
|
90
|
-
syllables = syllable_count(text)
|
|
91
|
-
source = "TEXTSTAT"
|
|
92
|
-
def sentence_grade(s):
|
|
93
|
-
return flesch_kincaid_grade(s)
|
|
94
|
-
except ImportError:
|
|
95
|
-
syllables = sum(count_syllables(w) for w in words)
|
|
96
|
-
grade = 0.39 * (len(words) / len(sentences)) + 11.8 * (syllables / len(words)) - 15.59
|
|
97
|
-
source = "FALLBACK"
|
|
98
|
-
def sentence_grade(s):
|
|
99
|
-
s_words = re.findall(r"\b[a-zA-Z][a-zA-Z\x27]*\b", s)
|
|
100
|
-
if not s_words:
|
|
101
|
-
return 0
|
|
102
|
-
s_syllables = sum(count_syllables(w) for w in s_words)
|
|
103
|
-
# For a single sentence, sentence-count is 1
|
|
104
|
-
return 0.39 * len(s_words) + 11.8 * (s_syllables / len(s_words)) - 15.59
|
|
105
|
-
|
|
106
|
-
# Per-sentence offenders — Ship 1 #1312
|
|
107
|
-
offenders = []
|
|
108
|
-
for s in sentences:
|
|
109
|
-
s_clean = s.strip()
|
|
110
|
-
s_words = re.findall(r"\b[a-zA-Z][a-zA-Z\x27]*\b", s_clean)
|
|
111
|
-
if len(s_words) < 5:
|
|
112
|
-
continue # skip very short sentences
|
|
113
|
-
try:
|
|
114
|
-
s_grade = sentence_grade(s_clean)
|
|
115
|
-
except Exception:
|
|
116
|
-
continue
|
|
117
|
-
if s_grade > 10:
|
|
118
|
-
preview = s_clean[:80]
|
|
119
|
-
if len(s_clean) > 80:
|
|
120
|
-
preview += "..."
|
|
121
|
-
offenders.append({
|
|
122
|
-
"grade": round(s_grade, 1),
|
|
123
|
-
"words": len(s_words),
|
|
124
|
-
"preview": preview,
|
|
125
|
-
})
|
|
126
|
-
# Sort by grade descending; take top 3
|
|
127
|
-
offenders.sort(key=lambda x: -x["grade"])
|
|
128
|
-
offenders = offenders[:3]
|
|
129
|
-
|
|
130
|
-
# Emit first line = original space-separated shape for backward compat
|
|
131
|
-
print(f"{grade:.1f} {len(words)} {len(sentences)} {syllables} {source}")
|
|
132
|
-
# Emit second line = JSON array of offenders (may be empty)
|
|
133
|
-
print(json.dumps(offenders))
|
|
134
|
-
' 2>/dev/null)
|
|
135
|
-
|
|
136
|
-
# Parse output — either "SKIP" or "grade words sentences syllables SOURCE" + offenders JSON
|
|
137
|
-
if [ -z "$GRADE_OUTPUT" ] || [ "$GRADE_OUTPUT" = "SKIP" ]; then
|
|
138
|
-
exit 0
|
|
139
|
-
fi
|
|
140
|
-
|
|
141
|
-
# First line = grade metrics; second line = offenders JSON
|
|
142
|
-
GRADE_LINE=$(echo "$GRADE_OUTPUT" | head -1)
|
|
143
|
-
OFFENDERS_JSON=$(echo "$GRADE_OUTPUT" | sed -n '2p')
|
|
144
|
-
[ -z "$OFFENDERS_JSON" ] && OFFENDERS_JSON="[]"
|
|
145
|
-
|
|
146
|
-
GRADE=$(echo "$GRADE_LINE" | awk '{print $1}')
|
|
147
|
-
WORDS=$(echo "$GRADE_LINE" | awk '{print $2}')
|
|
148
|
-
SENTENCES=$(echo "$GRADE_LINE" | awk '{print $3}')
|
|
149
|
-
SOURCE=$(echo "$GRADE_LINE" | awk '{print $5}')
|
|
150
|
-
|
|
151
|
-
[ -z "$GRADE" ] && exit 0
|
|
152
|
-
|
|
153
|
-
# === Write grade marker ===
|
|
154
|
-
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
155
|
-
MARKER_DIR="${REPO_ROOT}/state/markers/turn-grade"
|
|
156
|
-
mkdir -p "$MARKER_DIR" 2>/dev/null
|
|
157
|
-
|
|
158
|
-
cat > "${MARKER_DIR}/last-turn.json" 2>/dev/null <<EOF
|
|
159
|
-
{
|
|
160
|
-
"grade": $GRADE,
|
|
161
|
-
"words": $WORDS,
|
|
162
|
-
"sentences": $SENTENCES,
|
|
163
|
-
"measured_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
164
|
-
"target_ceiling": 10,
|
|
165
|
-
"target_aspiration": 8,
|
|
166
|
-
"source": "${SOURCE:-UNKNOWN}",
|
|
167
|
-
"top_offenders": ${OFFENDERS_JSON}
|
|
168
|
-
}
|
|
169
|
-
EOF
|
|
170
|
-
|
|
171
|
-
# === Rewrite-check hits marker (ticket #935 extension) ===
|
|
172
|
-
# Run lib/rewrite-check.sh on the scrubbed text; count hits per category.
|
|
173
|
-
# Write to state/markers/turn-prose-flags/last-turn.json for plain-english-steering.sh
|
|
174
|
-
# to read on the next turn and inject as first-person compose reminders.
|
|
175
|
-
FLAGS_DIR="${REPO_ROOT}/state/markers/turn-prose-flags"
|
|
176
|
-
REWRITE_LIB="${REPO_ROOT}/lib/rewrite-check.sh"
|
|
177
|
-
|
|
178
|
-
if [ -f "$REWRITE_LIB" ]; then
|
|
179
|
-
mkdir -p "$FLAGS_DIR" 2>/dev/null
|
|
180
|
-
TMP_TEXT=$(mktemp)
|
|
181
|
-
printf '%s' "$SCRUBBED" > "$TMP_TEXT"
|
|
182
|
-
|
|
183
|
-
# shellcheck disable=SC1090
|
|
184
|
-
source "$REWRITE_LIB" 2>/dev/null
|
|
185
|
-
|
|
186
|
-
# Count hits per check function (each prints one match per line)
|
|
187
|
-
PASSIVE_HITS=0
|
|
188
|
-
HYPHEN_HITS=0
|
|
189
|
-
LONG_SENTENCE_HITS=0
|
|
190
|
-
ADVERB_HITS=0
|
|
191
|
-
|
|
192
|
-
if declare -f check_passive_voice >/dev/null 2>&1; then
|
|
193
|
-
PASSIVE_HITS=$(check_passive_voice "$TMP_TEXT" 2>/dev/null | wc -l | tr -d ' ')
|
|
194
|
-
fi
|
|
195
|
-
if declare -f check_hyphen_words >/dev/null 2>&1; then
|
|
196
|
-
HYPHEN_HITS=$(check_hyphen_words "$TMP_TEXT" 2>/dev/null | wc -l | tr -d ' ')
|
|
197
|
-
fi
|
|
198
|
-
if declare -f check_sentence_length >/dev/null 2>&1; then
|
|
199
|
-
LONG_SENTENCE_HITS=$(check_sentence_length "$TMP_TEXT" 25 2>/dev/null | wc -l | tr -d ' ')
|
|
200
|
-
fi
|
|
201
|
-
if declare -f check_adverb_density >/dev/null 2>&1; then
|
|
202
|
-
ADVERB_HITS=$(check_adverb_density "$TMP_TEXT" 2>/dev/null | wc -l | tr -d ' ')
|
|
203
|
-
fi
|
|
204
|
-
|
|
205
|
-
rm -f "$TMP_TEXT"
|
|
206
|
-
|
|
207
|
-
cat > "${FLAGS_DIR}/last-turn.json" 2>/dev/null <<EOF
|
|
208
|
-
{
|
|
209
|
-
"total_words": $WORDS,
|
|
210
|
-
"long_sentences": $LONG_SENTENCE_HITS,
|
|
211
|
-
"passive_voice": $PASSIVE_HITS,
|
|
212
|
-
"hyphen_words": $HYPHEN_HITS,
|
|
213
|
-
"adverbs": $ADVERB_HITS,
|
|
214
|
-
"measured_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
215
|
-
}
|
|
216
|
-
EOF
|
|
217
|
-
fi
|
|
218
|
-
|
|
219
|
-
exit 0
|