@thebassclef/lite 1.0.4 → 1.1.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 +65 -0
- package/dist/cli.js +65 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lite/.claude/agents/architect.md +70 -0
- package/dist/lite/.claude/agents/builder.md +114 -0
- package/dist/lite/.claude/agents/designer.md +156 -0
- package/dist/lite/.claude/agents/reviewer.md +88 -0
- package/dist/lite/.claude/hooks/bassclef-sync.sh +85 -27
- package/dist/lite/.claude/hooks/longrun-prep-compounding-axis-check.sh +492 -0
- package/dist/lite/.claude/hooks/longrun-prep-render-check.sh +236 -0
- package/dist/lite/.claude/hooks/persona-name-scrub-check.sh +105 -0
- package/dist/lite/.claude/hooks/skill-step-list-check.sh +171 -0
- package/dist/lite/.claude/luminaries/alan-cooper.md +170 -0
- package/dist/lite/.claude/luminaries/alistair-cockburn.md +140 -0
- package/dist/lite/.claude/luminaries/amazon-pr-faq.md +34 -0
- package/dist/lite/.claude/luminaries/andreas-zeller.md +74 -0
- package/dist/lite/.claude/luminaries/ash-maurya.md +121 -0
- package/dist/lite/.claude/luminaries/bill-buxton.md +210 -0
- package/dist/lite/.claude/luminaries/charles-sanders-peirce.md +150 -0
- package/dist/lite/.claude/luminaries/david-ogilvy.md +192 -0
- package/dist/lite/.claude/luminaries/don-norman.md +173 -0
- package/dist/lite/.claude/luminaries/edward-tufte.md +179 -0
- package/dist/lite/.claude/luminaries/eric-evans.md +160 -0
- package/dist/lite/.claude/luminaries/frederick-brooks.md +50 -0
- package/dist/lite/.claude/luminaries/gang-of-four.md +157 -0
- package/dist/lite/.claude/luminaries/glenford-myers.md +230 -0
- package/dist/lite/.claude/luminaries/hunt-thomas.md +115 -0
- package/dist/lite/.claude/luminaries/hyrum-wright.md +94 -0
- package/dist/lite/.claude/luminaries/jason-fried-dhh.md +46 -0
- package/dist/lite/.claude/luminaries/jesse-james-garrett.md +154 -0
- package/dist/lite/.claude/luminaries/john-ousterhout.md +94 -0
- package/dist/lite/.claude/luminaries/karl-popper.md +132 -0
- package/dist/lite/.claude/luminaries/kent-beck.md +168 -0
- package/dist/lite/.claude/luminaries/linus-torvalds.md +218 -0
- package/dist/lite/.claude/luminaries/martin-fowler.md +164 -0
- package/dist/lite/.claude/luminaries/michael-feathers.md +125 -0
- package/dist/lite/.claude/luminaries/michael-nygard.md +118 -0
- package/dist/lite/.claude/luminaries/robert-c-martin.md +164 -0
- package/dist/lite/.claude/luminaries/saltzer-schroeder.md +194 -0
- package/dist/lite/.claude/luminaries/sophia-prater.md +193 -0
- package/dist/lite/.claude/luminaries/stephen-toulmin.md +131 -0
- package/dist/lite/.claude/luminaries/tony-hoare.md +170 -0
- package/dist/lite/.claude/luminaries/vaughn-vernon.md +50 -0
- package/dist/lite/.claude/luminaries/w-edwards-deming.md +158 -0
- package/dist/lite/.claude/rules/accessor-library-discipline.md +110 -0
- package/dist/lite/.claude/rules/adr-discipline.md +96 -0
- package/dist/lite/.claude/rules/api-conventions.md +115 -0
- package/dist/lite/.claude/rules/artifact-ingestion.md +137 -0
- package/dist/lite/.claude/rules/assert-only-after-verify.md +111 -0
- package/dist/lite/.claude/rules/blocked-items.md +139 -0
- package/dist/lite/.claude/rules/bootstrap-pair-discipline.md +106 -0
- package/dist/lite/.claude/rules/branching.md +28 -0
- package/dist/lite/.claude/rules/cold-adopter-harness-discipline.md +93 -0
- package/dist/lite/.claude/rules/commit-conventions.md +22 -0
- package/dist/lite/.claude/rules/compounding-axis-fresh-analysis.md +173 -0
- package/dist/lite/.claude/rules/compounding-sequence-fresh-analysis.md +173 -0
- package/dist/lite/.claude/rules/context-engineering.md +155 -0
- package/dist/lite/.claude/rules/context-management.md +85 -0
- package/dist/lite/.claude/rules/defensive-bash.md +52 -0
- package/dist/lite/.claude/rules/deferred-actions.md +233 -0
- package/dist/lite/.claude/rules/destructive-operations.md +69 -0
- package/dist/lite/.claude/rules/diagnosis.md +38 -0
- package/dist/lite/.claude/rules/github-issue-flash-tweet.md +114 -0
- package/dist/lite/.claude/rules/guardrails.md +73 -0
- package/dist/lite/.claude/rules/hook-wire-on-author.md +77 -0
- package/dist/lite/.claude/rules/identifier-leak-prevention.md +73 -0
- package/dist/lite/.claude/rules/iteration-bet-brief-completeness.md +43 -0
- package/dist/lite/.claude/rules/lite-manifest-schema-change-discipline.md +79 -0
- package/dist/lite/.claude/rules/longrun-prep-plan-doc-compression.md +77 -0
- package/dist/lite/.claude/rules/loop-discipline.md +71 -0
- package/dist/lite/.claude/rules/manual-prod-approval.md +100 -0
- package/dist/lite/.claude/rules/marker-enrichment-discipline.md +72 -0
- package/dist/lite/.claude/rules/mobile-ephemeral-session.md +109 -0
- package/dist/lite/.claude/rules/new-dependency-check.md +44 -0
- package/dist/lite/.claude/rules/oo-ad-entry-point.md +95 -0
- package/dist/lite/.claude/rules/operator-facing-prose.md +177 -0
- package/dist/lite/.claude/rules/option-label-discipline.md +82 -0
- package/dist/lite/.claude/rules/pattern-annotation.md +83 -0
- package/dist/lite/.claude/rules/persona-name-leak-scrub.md +90 -0
- package/dist/lite/.claude/rules/plain-english-discipline.md +132 -0
- package/dist/lite/.claude/rules/plan-enumeration-needs-value-props.md +177 -0
- package/dist/lite/.claude/rules/pr-body-shape.md +195 -0
- package/dist/lite/.claude/rules/pr-strategy.md +142 -0
- package/dist/lite/.claude/rules/pr-title-shape.md +90 -0
- package/dist/lite/.claude/rules/prototype-workflow.md +53 -0
- package/dist/lite/.claude/rules/reserved-skill-names.md +103 -0
- package/dist/lite/.claude/rules/schema-management.md +42 -0
- package/dist/lite/.claude/rules/sdlc-gates.md +149 -0
- package/dist/lite/.claude/rules/security.md +37 -0
- package/dist/lite/.claude/rules/session-artifacts.md +236 -0
- package/dist/lite/.claude/rules/skill-composition-declarations.md +105 -0
- package/dist/lite/.claude/rules/skill-description-clarity.md +196 -0
- package/dist/lite/.claude/rules/skill-procedure-step-list.md +119 -0
- package/dist/lite/.claude/rules/sprint-prep-preset-picker.md +76 -0
- package/dist/lite/.claude/rules/state-schema-validation.md +156 -0
- package/dist/lite/.claude/rules/stuck-signal-diagnostic.md +111 -0
- package/dist/lite/.claude/rules/substrate-config-schema.md +85 -0
- package/dist/lite/.claude/rules/test-list-discipline.md +129 -0
- package/dist/lite/.claude/rules/test-sufficiency.md +186 -0
- package/dist/lite/.claude/rules/testing-tier-config.md +125 -0
- package/dist/lite/.claude/rules/testing.md +38 -0
- package/dist/lite/.claude/rules/turn-estimate-grounding.md +82 -0
- package/dist/lite/.claude/rules/visual-hierarchy.md +427 -0
- package/dist/lite/.claude/rules/we-dont-break-adopters.md +103 -0
- package/dist/lite/.claude/rules/whereami-load-bearing.md +184 -0
- package/dist/lite/.claude/rules/writing-craft-discipline.md +72 -0
- package/dist/lite/.claude/rules/wu-sequencing-compounds.md +119 -0
- package/dist/lite/.claude/skills/build/SKILL.md +640 -0
- package/dist/lite/.claude/skills/chronicle/SKILL.md +55 -0
- package/dist/lite/.claude/skills/clean-artifacts/SKILL.md +249 -0
- package/dist/lite/.claude/skills/decompose/SKILL.md +280 -0
- package/dist/lite/.claude/skills/diagnose/SKILL.md +297 -0
- package/dist/lite/.claude/skills/feynman/SKILL.md +90 -0
- package/dist/lite/.claude/skills/howdoi/SKILL.md +105 -0
- package/dist/lite/.claude/skills/ia-model/SKILL.md +108 -0
- package/dist/lite/.claude/skills/interaction-design/SKILL.md +112 -0
- package/dist/lite/.claude/skills/interpret-input/SKILL.md +180 -0
- package/dist/lite/.claude/skills/journal/SKILL.md +209 -0
- package/dist/lite/.claude/skills/kiss/SKILL.md +449 -0
- package/dist/lite/.claude/skills/launch/SKILL.md +915 -0
- package/dist/lite/.claude/skills/lean-canvas/SKILL.md +332 -0
- package/dist/lite/.claude/skills/longrun/SKILL.md +512 -0
- package/dist/lite/.claude/skills/luminary/SKILL.md +481 -0
- package/dist/lite/.claude/skills/ogilvy-writing-audit/SKILL.md +177 -0
- package/dist/lite/.claude/skills/onboard-repo/SKILL.md +1640 -0
- package/dist/lite/.claude/skills/pattern-review/SKILL.md +99 -0
- package/dist/lite/.claude/skills/personas/SKILL.md +207 -0
- package/dist/lite/.claude/skills/promote/SKILL.md +283 -0
- package/dist/lite/.claude/skills/requirement/SKILL.md +98 -0
- package/dist/lite/.claude/skills/retro/SKILL.md +117 -0
- package/dist/lite/.claude/skills/riff/SKILL.md +114 -0
- package/dist/lite/.claude/skills/roadmap-reconcile/SKILL.md +163 -0
- package/dist/lite/.claude/skills/session-end/SKILL.md +309 -0
- package/dist/lite/.claude/skills/session-log/SKILL.md +299 -0
- package/dist/lite/.claude/skills/skills/SKILL.md +228 -0
- package/dist/lite/.claude/skills/spec/SKILL.md +105 -0
- package/dist/lite/.claude/skills/sprint/SKILL.md +418 -0
- package/dist/lite/.claude/skills/stage/SKILL.md +384 -0
- package/dist/lite/.claude/skills/state-a-problem/SKILL.md +185 -0
- package/dist/lite/.claude/skills/temperance/SKILL.md +108 -0
- package/dist/lite/.claude/skills/use-case/SKILL.md +417 -0
- package/dist/lite/.claude/skills/user-stories/SKILL.md +268 -0
- package/dist/lite/.claude/skills/value-prop/SKILL.md +251 -0
- package/dist/lite/.claude/skills/verify/SKILL.md +160 -0
- package/dist/lite/.claude/skills/visual-review/SKILL.md +503 -0
- package/dist/lite/.claude/skills/whats-the-plan/SKILL.md +202 -0
- package/dist/lite/.claude/skills/whereami/SKILL.md +307 -0
- package/dist/lite/AGENTS.md +79 -0
- package/dist/lite/CLAUDE-lite.md +85 -0
- package/dist/lite/CODE_OF_CONDUCT.md +28 -0
- package/dist/lite/CONTRIBUTING.md +177 -0
- package/dist/lite/README.md +173 -0
- package/dist/lite/SECURITY.md +19 -0
- package/dist/lite/architecture/decisions/ADR-029-release-pipeline.md +79 -0
- package/dist/lite/architecture/decisions/ADR-031-non-breaking-changes-adopter-discipline.md +139 -0
- package/dist/lite/architecture/decisions/ADR-032-adopter-sync-dispatcher-architecture.md +192 -0
- package/dist/lite/architecture/decisions/ADR-039-release-tagging-scheme.md +145 -0
- package/dist/lite/architecture/decisions/ADR-040-planning-skill-vocabulary-and-lite-profile.md +155 -0
- package/dist/lite/architecture/decisions/ADR-044-unified-skill-body-template.md +162 -0
- package/dist/lite/lib/clean-artifacts-sweep.sh +112 -0
- package/dist/lite/lib/code-comment-discipline.sh +144 -0
- package/dist/lite/lib/composer-preflight.sh +459 -0
- package/dist/lite/lib/deferred-actions.sh +146 -0
- package/dist/lite/lib/hook-header-parser.sh +61 -0
- package/dist/lite/lib/hook-heartbeat.sh +152 -0
- package/dist/lite/lib/hook-inject.sh +255 -0
- package/dist/lite/lib/hook-installer.sh +195 -0
- package/dist/lite/lib/luminary-pick.sh +96 -0
- package/dist/lite/lib/output-discipline.sh +143 -0
- package/dist/lite/lib/prose-scan-boundary.sh +171 -0
- package/dist/lite/lib/rewrite-check.sh +214 -0
- package/dist/lite/lib/session-rescue.sh +151 -0
- package/dist/lite/lib/source-graph-walker.sh +277 -0
- package/dist/lite/lib/state.sh +1372 -0
- package/dist/lite/lib/telemetry.sh +205 -0
- package/dist/lite/lib/tier-check.sh +187 -0
- package/dist/lite/lib/tier-dependency-audit.sh +1088 -0
- package/dist/lite/presence/install/bassclef-hook-connect.sh +178 -0
- package/dist/lite/presence/install/bassclef-sync.dispatcher.template.sh +841 -0
- package/dist/lite/presence/install/bassclef-sync.template.sh +2278 -0
- package/dist/lite/presence/install/schedule-auto-save.cron.sh +88 -0
- package/dist/lite/presence/install/schedule-auto-save.taskscheduler.md +122 -0
- package/dist/lite/scripts/aggregate-telemetry.sh +217 -0
- package/dist/lite/scripts/analyze-tier-dependencies.sh +239 -0
- package/dist/lite/scripts/generate-lite-manifest.sh +505 -0
- package/dist/lite/scripts/generate-tier-manifest.sh +28 -0
- package/dist/lite/scripts/intent-drift-check.sh +456 -0
- package/dist/lite/scripts/lite-manifest-drift-check.sh +146 -0
- package/dist/lite/scripts/render-lite-manifest-doc.sh +150 -0
- package/dist/lite/standards/adr-template.md +86 -0
- package/dist/lite/standards/api-conventions/nextjs.md +84 -0
- package/dist/lite/standards/artifact-composition.md +209 -0
- package/dist/lite/standards/bash-hook-safety.md +246 -0
- package/dist/lite/standards/bassclef-configs-schema.md +232 -0
- package/dist/lite/standards/bassclef-evolution.md +143 -0
- package/dist/lite/standards/bassclef-internal-jargon.md +244 -0
- package/dist/lite/standards/bassclef-managed-sentinel.md +96 -0
- package/dist/lite/standards/bassclef-source-config.md +228 -0
- package/dist/lite/standards/branch-stacking.md +408 -0
- package/dist/lite/standards/code-safety-principles.md +176 -0
- package/dist/lite/standards/composer-prerequisites.md +155 -0
- package/dist/lite/standards/deferred-actions-schema.md +204 -0
- package/dist/lite/standards/dependency-discipline/cargo.md +39 -0
- package/dist/lite/standards/dependency-discipline/gem.md +43 -0
- package/dist/lite/standards/dependency-discipline/go-mod.md +41 -0
- package/dist/lite/standards/dependency-discipline/npm.md +42 -0
- package/dist/lite/standards/dependency-discipline/pip.md +42 -0
- package/dist/lite/standards/deployment-topology/ec2-tailscale.md +225 -0
- package/dist/lite/standards/deployment-topology.md +69 -0
- package/dist/lite/standards/docs-sync-allowlist.md +76 -0
- package/dist/lite/standards/domain-and-dns.md +145 -0
- package/dist/lite/standards/frontend-stack.md +67 -0
- package/dist/lite/standards/frontmatter-schema.md +154 -0
- package/dist/lite/standards/graceful-exit.md +227 -0
- package/dist/lite/standards/hook-idempotency.md +102 -0
- package/dist/lite/standards/hook-injection-discipline.md +202 -0
- package/dist/lite/standards/hook-install-class.md +215 -0
- package/dist/lite/standards/input-handler-interface.md +152 -0
- package/dist/lite/standards/lite-manifest-schema-changes.md +180 -0
- package/dist/lite/standards/lite-manifest.json +2797 -0
- package/dist/lite/standards/luminary-matching.md +105 -0
- package/dist/lite/standards/luminary-problem-patterns.md +481 -0
- package/dist/lite/standards/migration-discipline/active-record.md +50 -0
- package/dist/lite/standards/migration-discipline/alembic.md +43 -0
- package/dist/lite/standards/migration-discipline/gorm.md +50 -0
- package/dist/lite/standards/migration-discipline/prisma.md +53 -0
- package/dist/lite/standards/migration-discipline/sqlalchemy.md +51 -0
- package/dist/lite/standards/mobile-ephemeral-session.md +167 -0
- package/dist/lite/standards/model-routing-discipline.md +160 -0
- package/dist/lite/standards/ogilvy-writing-rules.md +225 -0
- package/dist/lite/standards/opener-discipline.md +96 -0
- package/dist/lite/standards/operator-facing-prose-discipline.md +201 -0
- package/dist/lite/standards/persona-schema.md +229 -0
- package/dist/lite/standards/pluggable-luminaries.md +323 -0
- package/dist/lite/standards/pr-body-discipline.md +115 -0
- package/dist/lite/standards/preview-state-schema.md +189 -0
- package/dist/lite/standards/project-directory-layout.md +276 -0
- package/dist/lite/standards/release-tagging.md +137 -0
- package/dist/lite/standards/reserved-skill-names.md +120 -0
- package/dist/lite/standards/scannable-multi-option-output.md +265 -0
- package/dist/lite/standards/sdlc-compliance.md +286 -0
- package/dist/lite/standards/sdlc-gates/typescript.md +57 -0
- package/dist/lite/standards/secrets-lifecycle.md +210 -0
- package/dist/lite/standards/security-scanner-adapter.md +145 -0
- package/dist/lite/standards/session-board.md +256 -0
- package/dist/lite/standards/skill-output-discipline.md +121 -0
- package/dist/lite/standards/state-spine-contract.md +255 -0
- package/dist/lite/standards/state-spine.md +511 -0
- package/dist/lite/standards/steering-hints/kiss-words.md +11 -0
- package/dist/lite/standards/substrate-config-schema.md +267 -0
- package/dist/lite/standards/tech-stack-config.md +109 -0
- package/dist/lite/standards/tier-dependency-analysis.md +167 -0
- package/dist/lite/standards/tier-runtime-deps/lite.md +57 -0
- package/dist/lite/standards/tier-tag-schema.md +155 -0
- package/dist/lite/standards/two-layer-config.md +99 -0
- package/dist/lite/standards/use-case-format.md +292 -0
- package/dist/lite/standards/user-story-invest.md +268 -0
- package/dist/lite/standards/velocity-and-appetite.md +229 -0
- package/dist/lite/standards/voice-input-pattern.md +119 -0
- package/dist/lite/standards/whereami-schema.md +301 -0
- package/dist/lite/standards/worktree-management.md +211 -0
- package/dist/lite/standards/writing-guide.md +213 -0
- package/dist/lite/templates/chronicle-template.md +75 -0
- package/dist/lite/templates/deferred-action-template.md +45 -0
- package/dist/lite/templates/memory-proposal-template.md +77 -0
- package/dist/lite/templates/persona-template.md +200 -0
- package/dist/lite/templates/pr-faq.md +45 -0
- package/dist/lite/templates/secret-rotation-template.md +162 -0
- package/dist/lite/templates/spec-template.md +131 -0
- package/dist/lite/templates/use-case-template.md +194 -0
- package/dist/lite/templates/user-story-template.md +107 -0
- package/dist/lite/templates/whereami-template.md +101 -0
- package/package.json +1 -1
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# intent-drift-check.sh — measure intent drift after an artifact body rewrite.
|
|
4
|
+
#
|
|
5
|
+
# Reads a saved embedding from .claude/embeddings/<type>/<slug>.json where
|
|
6
|
+
# <type> is one of skills/rules/standards/hooks. Re-embeds the current source
|
|
7
|
+
# body via Voyage. Computes cosine similarity. Compares against a threshold.
|
|
8
|
+
# Reports PASS/FAIL.
|
|
9
|
+
#
|
|
10
|
+
# The threshold varies per change class. The class captures author intent —
|
|
11
|
+
# preserve, simplify, extend, restructure, or material. Different classes
|
|
12
|
+
# expect different natural cosine drops per Mikolov 2013 embedding-space
|
|
13
|
+
# behavior. See bet 24a Path 1 fold-in for the threshold table.
|
|
14
|
+
#
|
|
15
|
+
# Closes bassclef-upstream#783 — makes /loop step 5 (INTENT CHECK) measurable
|
|
16
|
+
# rather than subjective. Per Feathers characterization + Beck TDD rhythm.
|
|
17
|
+
# Bet 24a Path 1 extends coverage from skills to rules + standards + hooks
|
|
18
|
+
# with change-class-aware thresholds.
|
|
19
|
+
#
|
|
20
|
+
# Usage:
|
|
21
|
+
# scripts/intent-drift-check.sh --skill <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
22
|
+
# scripts/intent-drift-check.sh --rule <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
23
|
+
# scripts/intent-drift-check.sh --standard <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
24
|
+
# scripts/intent-drift-check.sh --hook <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
25
|
+
# scripts/intent-drift-check.sh --luminary <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
26
|
+
# scripts/intent-drift-check.sh --adr <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
27
|
+
# scripts/intent-drift-check.sh --persona <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
28
|
+
# scripts/intent-drift-check.sh --template <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
29
|
+
# scripts/intent-drift-check.sh --help
|
|
30
|
+
#
|
|
31
|
+
# Change classes (drives default threshold):
|
|
32
|
+
# preserve — typo, style, formatting; threshold 0.95
|
|
33
|
+
# simplify — /kiss rewrite, grade lift; threshold 0.90 (default)
|
|
34
|
+
# extend — new subsection, added content; threshold 0.70
|
|
35
|
+
# restructure — reorder, section merge; threshold 0.65
|
|
36
|
+
# material — rescope, breaking change; no threshold (always PASS-LOGGED)
|
|
37
|
+
#
|
|
38
|
+
# Exits:
|
|
39
|
+
# 0 — cosine at or above threshold (intent preserved) OR material class
|
|
40
|
+
# 1 — cosine below threshold (intent drift detected; rewrite failed)
|
|
41
|
+
# 2 — precondition failure (missing artifact, missing vector, missing keys,
|
|
42
|
+
# or mutually exclusive artifact flags supplied)
|
|
43
|
+
#
|
|
44
|
+
# Env:
|
|
45
|
+
# VOYAGE_API_KEY — required for live mode
|
|
46
|
+
# ANTHROPIC_API_KEY — checked via intent-matcher.sh (required for live mode)
|
|
47
|
+
# INTENT_DRIFT_STUB=1 — skip Voyage call; return cosine 1.0 (for tests)
|
|
48
|
+
#
|
|
49
|
+
# Per @luminary michael-feathers + @luminary kent-beck + @luminary tomas-mikolov + ADR-042
|
|
50
|
+
|
|
51
|
+
set -o pipefail
|
|
52
|
+
|
|
53
|
+
usage() {
|
|
54
|
+
cat <<'EOF'
|
|
55
|
+
Usage:
|
|
56
|
+
intent-drift-check.sh --skill <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
57
|
+
intent-drift-check.sh --rule <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
58
|
+
intent-drift-check.sh --standard <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
59
|
+
intent-drift-check.sh --hook <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
60
|
+
intent-drift-check.sh --luminary <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
61
|
+
intent-drift-check.sh --adr <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
62
|
+
intent-drift-check.sh --persona <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
63
|
+
intent-drift-check.sh --template <slug> [--change-class <c>] [--threshold <f>] [--json]
|
|
64
|
+
|
|
65
|
+
Only one artifact flag at a time. Combining them fails with exit 2.
|
|
66
|
+
|
|
67
|
+
Measures intent drift after an artifact body rewrite. Reads saved vector from
|
|
68
|
+
.claude/embeddings/<type>/<slug>.json, re-embeds the current source body, and
|
|
69
|
+
computes cosine similarity against a change-class-aware threshold.
|
|
70
|
+
|
|
71
|
+
Change classes (drives default threshold when --threshold is omitted):
|
|
72
|
+
preserve Threshold 0.95 Typo/style/formatting; meaning identical
|
|
73
|
+
simplify Threshold 0.90 Grade lift, /kiss rewrite; meaning held (default)
|
|
74
|
+
extend Threshold 0.70 New subsection/content added
|
|
75
|
+
restructure Threshold 0.65 Reorder/merge; same meaning, new shape
|
|
76
|
+
material Log only Rescope/breaking; always PASS-LOGGED
|
|
77
|
+
|
|
78
|
+
Options:
|
|
79
|
+
--skill <slug> Skill slug (e.g., state-a-problem)
|
|
80
|
+
--rule <slug> Rule slug (e.g., loop-discipline)
|
|
81
|
+
--standard <slug> Standard slug (e.g., loop-discipline)
|
|
82
|
+
--hook <slug> Hook slug without .sh extension (e.g., pre-commit-gate)
|
|
83
|
+
--luminary <slug> Luminary slug (e.g., alan-cooper); source at .claude/luminaries/<slug>.md
|
|
84
|
+
--adr <slug> ADR slug (e.g., ADR-044-unified-skill-body-template); source at architecture/decisions/<slug>.md
|
|
85
|
+
--persona <slug> Persona slug (e.g., sam); source at docs/personas/<slug>.md
|
|
86
|
+
--template <slug> Template slug (e.g., chronicle-template); source at templates/<slug>.md
|
|
87
|
+
--change-class <c> One of: preserve, simplify, extend, restructure, material
|
|
88
|
+
--threshold <f> Override the class-derived threshold
|
|
89
|
+
--json Emit JSON output with cosine + verdict + change_class fields
|
|
90
|
+
--help Show this usage
|
|
91
|
+
|
|
92
|
+
Exit codes:
|
|
93
|
+
0 cosine >= threshold (intent preserved) OR material class
|
|
94
|
+
1 cosine < threshold (intent drift detected)
|
|
95
|
+
2 precondition failure (missing artifact, missing vector, mutually exclusive flags)
|
|
96
|
+
|
|
97
|
+
Examples:
|
|
98
|
+
scripts/intent-drift-check.sh --skill state-a-problem
|
|
99
|
+
scripts/intent-drift-check.sh --rule loop-discipline --change-class extend
|
|
100
|
+
scripts/intent-drift-check.sh --standard loop-discipline --change-class extend --json
|
|
101
|
+
EOF
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# === Arg parse ===
|
|
105
|
+
SKILL=""
|
|
106
|
+
RULE=""
|
|
107
|
+
STANDARD=""
|
|
108
|
+
HOOK=""
|
|
109
|
+
LUMINARY=""
|
|
110
|
+
ADR=""
|
|
111
|
+
PERSONA=""
|
|
112
|
+
TEMPLATE=""
|
|
113
|
+
CHANGE_CLASS=""
|
|
114
|
+
THRESHOLD=""
|
|
115
|
+
JSON_OUT=0
|
|
116
|
+
|
|
117
|
+
while [ $# -gt 0 ]; do
|
|
118
|
+
case "$1" in
|
|
119
|
+
--skill) SKILL="$2"; shift 2 ;;
|
|
120
|
+
--rule) RULE="$2"; shift 2 ;;
|
|
121
|
+
--standard) STANDARD="$2"; shift 2 ;;
|
|
122
|
+
--hook) HOOK="$2"; shift 2 ;;
|
|
123
|
+
--luminary) LUMINARY="$2"; shift 2 ;;
|
|
124
|
+
--adr) ADR="$2"; shift 2 ;;
|
|
125
|
+
--persona) PERSONA="$2"; shift 2 ;;
|
|
126
|
+
--template) TEMPLATE="$2"; shift 2 ;;
|
|
127
|
+
--change-class) CHANGE_CLASS="$2"; shift 2 ;;
|
|
128
|
+
--threshold) THRESHOLD="$2"; shift 2 ;;
|
|
129
|
+
--json) JSON_OUT=1; shift ;;
|
|
130
|
+
--help|-h) usage; exit 0 ;;
|
|
131
|
+
*) echo "Unknown arg: $1" >&2; usage >&2; exit 2 ;;
|
|
132
|
+
esac
|
|
133
|
+
done
|
|
134
|
+
|
|
135
|
+
# === Mutual exclusion — only one artifact flag at a time ===
|
|
136
|
+
FLAG_COUNT=0
|
|
137
|
+
[ -n "$SKILL" ] && FLAG_COUNT=$((FLAG_COUNT + 1))
|
|
138
|
+
[ -n "$RULE" ] && FLAG_COUNT=$((FLAG_COUNT + 1))
|
|
139
|
+
[ -n "$STANDARD" ] && FLAG_COUNT=$((FLAG_COUNT + 1))
|
|
140
|
+
[ -n "$HOOK" ] && FLAG_COUNT=$((FLAG_COUNT + 1))
|
|
141
|
+
[ -n "$LUMINARY" ] && FLAG_COUNT=$((FLAG_COUNT + 1))
|
|
142
|
+
[ -n "$ADR" ] && FLAG_COUNT=$((FLAG_COUNT + 1))
|
|
143
|
+
[ -n "$PERSONA" ] && FLAG_COUNT=$((FLAG_COUNT + 1))
|
|
144
|
+
[ -n "$TEMPLATE" ] && FLAG_COUNT=$((FLAG_COUNT + 1))
|
|
145
|
+
|
|
146
|
+
if [ "$FLAG_COUNT" -eq 0 ]; then
|
|
147
|
+
usage >&2
|
|
148
|
+
exit 2
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
if [ "$FLAG_COUNT" -gt 1 ]; then
|
|
152
|
+
echo "ERROR: --skill, --rule, --standard, --hook, --luminary, --adr, --persona, --template are mutually exclusive; only one at a time" >&2
|
|
153
|
+
usage >&2
|
|
154
|
+
exit 2
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
# === Change-class routing ===
|
|
158
|
+
# Default class is simplify (matches pre-fold-in behavior for skill body rewrites).
|
|
159
|
+
# Explicit --threshold overrides the class-derived value.
|
|
160
|
+
if [ -z "$CHANGE_CLASS" ]; then
|
|
161
|
+
CHANGE_CLASS="simplify"
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
# Validate class + set default threshold per bet 24a Path 1 table.
|
|
165
|
+
CLASS_DEFAULT_THRESHOLD=""
|
|
166
|
+
IS_MATERIAL=0
|
|
167
|
+
case "$CHANGE_CLASS" in
|
|
168
|
+
preserve) CLASS_DEFAULT_THRESHOLD="0.95" ;;
|
|
169
|
+
simplify) CLASS_DEFAULT_THRESHOLD="0.90" ;;
|
|
170
|
+
extend) CLASS_DEFAULT_THRESHOLD="0.70" ;;
|
|
171
|
+
restructure) CLASS_DEFAULT_THRESHOLD="0.65" ;;
|
|
172
|
+
material) IS_MATERIAL=1; CLASS_DEFAULT_THRESHOLD="0.00" ;;
|
|
173
|
+
*) echo "ERROR: unknown --change-class: '$CHANGE_CLASS'" >&2
|
|
174
|
+
echo " Valid values: preserve, simplify, extend, restructure, material" >&2
|
|
175
|
+
exit 2 ;;
|
|
176
|
+
esac
|
|
177
|
+
|
|
178
|
+
# Explicit --threshold overrides the class default.
|
|
179
|
+
if [ -z "$THRESHOLD" ]; then
|
|
180
|
+
THRESHOLD="$CLASS_DEFAULT_THRESHOLD"
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
# === Locate repo root ===
|
|
184
|
+
if [ -n "${CLAUDE_PROJECT_DIR:-}" ] && [ -d "$CLAUDE_PROJECT_DIR/.claude" ]; then
|
|
185
|
+
REPO_ROOT="$CLAUDE_PROJECT_DIR"
|
|
186
|
+
else
|
|
187
|
+
REPO_ROOT="$(pwd)"
|
|
188
|
+
fi
|
|
189
|
+
|
|
190
|
+
# === Route to artifact type ===
|
|
191
|
+
ARTIFACT_TYPE=""
|
|
192
|
+
ARTIFACT_SLUG=""
|
|
193
|
+
VECTOR_PATH=""
|
|
194
|
+
SOURCE_PATH=""
|
|
195
|
+
|
|
196
|
+
if [ -n "$SKILL" ]; then
|
|
197
|
+
ARTIFACT_TYPE="skill"
|
|
198
|
+
ARTIFACT_SLUG="$SKILL"
|
|
199
|
+
VECTOR_PATH="$REPO_ROOT/.claude/embeddings/skills/${SKILL}.json"
|
|
200
|
+
SOURCE_PATH="$REPO_ROOT/.claude/skills/${SKILL}/SKILL.md"
|
|
201
|
+
elif [ -n "$RULE" ]; then
|
|
202
|
+
ARTIFACT_TYPE="rule"
|
|
203
|
+
ARTIFACT_SLUG="$RULE"
|
|
204
|
+
VECTOR_PATH="$REPO_ROOT/.claude/embeddings/rules/${RULE}.json"
|
|
205
|
+
SOURCE_PATH="$REPO_ROOT/.claude/rules/${RULE}.md"
|
|
206
|
+
elif [ -n "$STANDARD" ]; then
|
|
207
|
+
ARTIFACT_TYPE="standard"
|
|
208
|
+
ARTIFACT_SLUG="$STANDARD"
|
|
209
|
+
VECTOR_PATH="$REPO_ROOT/.claude/embeddings/standards/${STANDARD}.json"
|
|
210
|
+
SOURCE_PATH="$REPO_ROOT/standards/${STANDARD}.md"
|
|
211
|
+
elif [ -n "$HOOK" ]; then
|
|
212
|
+
ARTIFACT_TYPE="hook"
|
|
213
|
+
ARTIFACT_SLUG="$HOOK"
|
|
214
|
+
VECTOR_PATH="$REPO_ROOT/.claude/embeddings/hooks/${HOOK}.json"
|
|
215
|
+
SOURCE_PATH="$REPO_ROOT/.claude/hooks/${HOOK}.sh"
|
|
216
|
+
elif [ -n "$LUMINARY" ]; then
|
|
217
|
+
ARTIFACT_TYPE="luminary"
|
|
218
|
+
ARTIFACT_SLUG="$LUMINARY"
|
|
219
|
+
VECTOR_PATH="$REPO_ROOT/.claude/embeddings/luminaries/${LUMINARY}.json"
|
|
220
|
+
SOURCE_PATH="$REPO_ROOT/.claude/luminaries/${LUMINARY}.md"
|
|
221
|
+
elif [ -n "$ADR" ]; then
|
|
222
|
+
ARTIFACT_TYPE="adr"
|
|
223
|
+
ARTIFACT_SLUG="$ADR"
|
|
224
|
+
VECTOR_PATH="$REPO_ROOT/.claude/embeddings/adrs/${ADR}.json"
|
|
225
|
+
SOURCE_PATH="$REPO_ROOT/architecture/decisions/${ADR}.md"
|
|
226
|
+
elif [ -n "$PERSONA" ]; then
|
|
227
|
+
ARTIFACT_TYPE="persona"
|
|
228
|
+
ARTIFACT_SLUG="$PERSONA"
|
|
229
|
+
VECTOR_PATH="$REPO_ROOT/.claude/embeddings/personas/${PERSONA}.json"
|
|
230
|
+
SOURCE_PATH="$REPO_ROOT/docs/personas/${PERSONA}.md"
|
|
231
|
+
elif [ -n "$TEMPLATE" ]; then
|
|
232
|
+
ARTIFACT_TYPE="template"
|
|
233
|
+
ARTIFACT_SLUG="$TEMPLATE"
|
|
234
|
+
VECTOR_PATH="$REPO_ROOT/.claude/embeddings/templates/${TEMPLATE}.json"
|
|
235
|
+
SOURCE_PATH="$REPO_ROOT/templates/${TEMPLATE}.md"
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
# === Preconditions ===
|
|
239
|
+
if [ ! -f "$VECTOR_PATH" ]; then
|
|
240
|
+
echo "ERROR: saved vector not found at $VECTOR_PATH" >&2
|
|
241
|
+
echo " Run scripts/embed-lite-corpus.sh to generate baseline vectors first." >&2
|
|
242
|
+
exit 2
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
if [ ! -f "$SOURCE_PATH" ]; then
|
|
246
|
+
echo "ERROR: source file not found at $SOURCE_PATH" >&2
|
|
247
|
+
exit 2
|
|
248
|
+
fi
|
|
249
|
+
|
|
250
|
+
# === Extract current body — one function per artifact shape ===
|
|
251
|
+
#
|
|
252
|
+
# EXTRACT_SAMPLE_BYTES is the byte window sampled from each artifact body.
|
|
253
|
+
# Voyage-3-lite context window is 32K tokens (~128KB in ASCII). 30000 chars
|
|
254
|
+
# ≈ 7500 tokens fits well under limit and covers 99% of SKILL bodies.
|
|
255
|
+
# MUST stay in sync with embed-lite-corpus.sh + embed-substrate-corpus.sh —
|
|
256
|
+
# cosine comparison only means anything when the drift-check samples the
|
|
257
|
+
# same text shape the baseline was embedded on. Per goal 2026-08-16g cure
|
|
258
|
+
# of chunk 2 coverage gap where L321 edit was invisible to 1500-char window.
|
|
259
|
+
EXTRACT_SAMPLE_BYTES="${EXTRACT_SAMPLE_BYTES:-30000}"
|
|
260
|
+
|
|
261
|
+
# === Extractors aligned with baseline generators per #1244 cure ===
|
|
262
|
+
# The functions below MUST emit byte-for-byte identical text to the baseline
|
|
263
|
+
# generator for the same file. Sources of truth:
|
|
264
|
+
# skill → scripts/embed-lite-corpus.sh:_extract_skill_text
|
|
265
|
+
# persona → scripts/embed-lite-corpus.sh:_extract_persona_text
|
|
266
|
+
# hook → scripts/embed-substrate-corpus.sh:_extract_hook_text
|
|
267
|
+
# rule → scripts/embed-substrate-corpus.sh:_extract_rule_text
|
|
268
|
+
# standard → scripts/embed-substrate-corpus.sh:_extract_standard_text (= rule shape)
|
|
269
|
+
# adr → scripts/embed-substrate-corpus.sh:_extract_adr_text
|
|
270
|
+
# template → scripts/embed-substrate-corpus.sh:_extract_template_text
|
|
271
|
+
# luminary → scripts/intent-matcher.sh:_intent_matcher_extract_luminary_text (deferred; falls back to rule shape)
|
|
272
|
+
# Tier 0 test at .claude/hooks/tests/intent-drift-extractor-alignment.test.sh
|
|
273
|
+
# pins baseline_extractor(fixture) == checker_extractor(fixture) per type.
|
|
274
|
+
|
|
275
|
+
extract_skill_text() {
|
|
276
|
+
# ALIGNED with embed-lite-corpus.sh:_extract_skill_text
|
|
277
|
+
local file="$1"
|
|
278
|
+
local desc body
|
|
279
|
+
desc=$(awk '/^description:/ { sub("^description:[[:space:]]*",""); gsub(/^"/,""); gsub(/"$/,""); print; exit }' "$file")
|
|
280
|
+
body=$(awk 'BEGIN { count=0 } /^---$/ { count++; next } count >= 2 { print }' "$file" | head -c "$EXTRACT_SAMPLE_BYTES")
|
|
281
|
+
printf 'Description: %s\n\n%s\n' "$desc" "$body" | LC_ALL=C tr -d '\000-\010\013\014\016-\037'
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
extract_hook_text() {
|
|
285
|
+
# ALIGNED with embed-substrate-corpus.sh:_extract_hook_text (per #1244)
|
|
286
|
+
# Baseline emits: Docstring: <stripped-#-header-first-20>\n\n<body-without-#-lines>
|
|
287
|
+
local file="$1"
|
|
288
|
+
local docstring body
|
|
289
|
+
docstring=$(awk '/^#/ { sub("^#[[:space:]]*",""); print } !/^#/ { exit }' "$file" | head -20)
|
|
290
|
+
body=$(awk '!/^#/ { print }' "$file" | head -c "$EXTRACT_SAMPLE_BYTES")
|
|
291
|
+
printf 'Docstring: %s\n\n%s\n' "$docstring" "$body" | LC_ALL=C tr -d '\000-\010\013\014\016-\037'
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
extract_rule_text() {
|
|
295
|
+
# ALIGNED with embed-substrate-corpus.sh:_extract_rule_text (per #1244)
|
|
296
|
+
# Baseline emits: Title: <first-H1>\n\n<body-after-2nd----->
|
|
297
|
+
local file="$1"
|
|
298
|
+
local title body
|
|
299
|
+
title=$(grep -m1 '^# ' "$file" | sed 's/^# //')
|
|
300
|
+
body=$(awk 'BEGIN { count=0 } /^---$/ { count++; next } count >= 2 { print }' "$file" | head -c "$EXTRACT_SAMPLE_BYTES")
|
|
301
|
+
# Rules may not have frontmatter — fall back to whole file
|
|
302
|
+
if [ -z "$body" ]; then
|
|
303
|
+
body=$(head -c "$EXTRACT_SAMPLE_BYTES" "$file")
|
|
304
|
+
fi
|
|
305
|
+
printf 'Title: %s\n\n%s\n' "$title" "$body" | LC_ALL=C tr -d '\000-\010\013\014\016-\037'
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
extract_standard_text() {
|
|
309
|
+
# ALIGNED with embed-substrate-corpus.sh:_extract_standard_text (= rule shape)
|
|
310
|
+
extract_rule_text "$1"
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
extract_adr_text() {
|
|
314
|
+
# ALIGNED with embed-substrate-corpus.sh:_extract_adr_text (per #1244)
|
|
315
|
+
# Baseline emits: ADR Title: <from-frontmatter>\n\n<body-after-2nd----->
|
|
316
|
+
local file="$1"
|
|
317
|
+
local title body
|
|
318
|
+
title=$(awk '/^title:/ { sub("^title:[[:space:]]*",""); gsub(/^"/,""); gsub(/"$/,""); print; exit }' "$file")
|
|
319
|
+
body=$(awk 'BEGIN { count=0 } /^---$/ { count++; next } count >= 2 { print }' "$file" | head -c "$EXTRACT_SAMPLE_BYTES")
|
|
320
|
+
printf 'ADR Title: %s\n\n%s\n' "$title" "$body" | LC_ALL=C tr -d '\000-\010\013\014\016-\037'
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
extract_persona_text() {
|
|
324
|
+
# ALIGNED with embed-lite-corpus.sh:_extract_persona_text (per #1244)
|
|
325
|
+
# Baseline emits: Role: <from-frontmatter>\n\n<body-after-2nd----->
|
|
326
|
+
local file="$1"
|
|
327
|
+
local role body
|
|
328
|
+
role=$(awk '/^role:/ { sub("^role:[[:space:]]*",""); print; exit }' "$file")
|
|
329
|
+
body=$(awk 'BEGIN { count=0 } /^---$/ { count++; next } count >= 2 { print }' "$file" | head -c "$EXTRACT_SAMPLE_BYTES")
|
|
330
|
+
printf 'Role: %s\n\n%s\n' "$role" "$body" | LC_ALL=C tr -d '\000-\010\013\014\016-\037'
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
extract_template_text() {
|
|
334
|
+
# ALIGNED with embed-substrate-corpus.sh:_extract_template_text (per #1244)
|
|
335
|
+
# Baseline emits: Template: <basename>\n\n<body-raw>
|
|
336
|
+
local file="$1"
|
|
337
|
+
local name body
|
|
338
|
+
name=$(basename "$file")
|
|
339
|
+
body=$(head -c "$EXTRACT_SAMPLE_BYTES" "$file")
|
|
340
|
+
printf 'Template: %s\n\n%s\n' "$name" "$body" | LC_ALL=C tr -d '\000-\010\013\014\016-\037'
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
extract_luminary_text() {
|
|
344
|
+
# NOT YET ALIGNED — deferred per #1244 out-of-scope note. Luminary baseline
|
|
345
|
+
# generator is intent-matcher.sh:_intent_matcher_extract_luminary_text which
|
|
346
|
+
# uses a complex frontmatter extractor (name + primary_domain + signature +
|
|
347
|
+
# solves_problems + anti_patterns). No luminaries in chunks 1a/1b/2/3 swept
|
|
348
|
+
# scope so false-positives here have not surfaced. Follow-on ticket owed if
|
|
349
|
+
# luminary intent-drift becomes load-bearing.
|
|
350
|
+
# For now, keep prior rule-shape fallback so calls do not fail.
|
|
351
|
+
local file="$1"
|
|
352
|
+
local body
|
|
353
|
+
if head -n 1 "$file" | grep -q '^---$'; then
|
|
354
|
+
body=$(awk 'BEGIN { count=0 } /^---$/ { count++; next } count >= 2 { print }' "$file" | head -c "$EXTRACT_SAMPLE_BYTES")
|
|
355
|
+
else
|
|
356
|
+
body=$(head -c "$EXTRACT_SAMPLE_BYTES" "$file")
|
|
357
|
+
fi
|
|
358
|
+
printf '%s\n' "$body" | LC_ALL=C tr -d '\000-\010\013\014\016-\037'
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
case "$ARTIFACT_TYPE" in
|
|
362
|
+
skill) CURRENT_TEXT=$(extract_skill_text "$SOURCE_PATH") ;;
|
|
363
|
+
hook) CURRENT_TEXT=$(extract_hook_text "$SOURCE_PATH") ;;
|
|
364
|
+
rule) CURRENT_TEXT=$(extract_rule_text "$SOURCE_PATH") ;;
|
|
365
|
+
standard) CURRENT_TEXT=$(extract_standard_text "$SOURCE_PATH") ;;
|
|
366
|
+
adr) CURRENT_TEXT=$(extract_adr_text "$SOURCE_PATH") ;;
|
|
367
|
+
persona) CURRENT_TEXT=$(extract_persona_text "$SOURCE_PATH") ;;
|
|
368
|
+
template) CURRENT_TEXT=$(extract_template_text "$SOURCE_PATH") ;;
|
|
369
|
+
luminary) CURRENT_TEXT=$(extract_luminary_text "$SOURCE_PATH") ;;
|
|
370
|
+
esac
|
|
371
|
+
|
|
372
|
+
# === Read saved vector ===
|
|
373
|
+
SAVED_EMB=$(jq -c '.embedding' "$VECTOR_PATH" 2>/dev/null)
|
|
374
|
+
if [ -z "$SAVED_EMB" ] || [ "$SAVED_EMB" = "null" ]; then
|
|
375
|
+
echo "ERROR: saved vector at $VECTOR_PATH has no .embedding field" >&2
|
|
376
|
+
exit 2
|
|
377
|
+
fi
|
|
378
|
+
|
|
379
|
+
# === STUB mode — skip Voyage, return cosine 1.0 ===
|
|
380
|
+
if [ "${INTENT_DRIFT_STUB:-0}" = "1" ]; then
|
|
381
|
+
COSINE="1.0"
|
|
382
|
+
MODE="STUB"
|
|
383
|
+
else
|
|
384
|
+
# === Live mode — re-embed via intent-matcher.sh ===
|
|
385
|
+
# shellcheck source=scripts/intent-matcher.sh
|
|
386
|
+
source "$REPO_ROOT/scripts/intent-matcher.sh"
|
|
387
|
+
|
|
388
|
+
if ! intent_matcher_is_live; then
|
|
389
|
+
echo "ERROR: intent-matcher.sh reports STUB (missing VOYAGE_API_KEY or ANTHROPIC_API_KEY)" >&2
|
|
390
|
+
echo " Set INTENT_DRIFT_STUB=1 to skip Voyage call for testing." >&2
|
|
391
|
+
exit 2
|
|
392
|
+
fi
|
|
393
|
+
|
|
394
|
+
TMP_EMB=$(mktemp)
|
|
395
|
+
trap "rm -f '$TMP_EMB'" EXIT
|
|
396
|
+
|
|
397
|
+
if ! _intent_matcher_live_embed "$CURRENT_TEXT" "$TMP_EMB"; then
|
|
398
|
+
echo "ERROR: Voyage embed call failed" >&2
|
|
399
|
+
exit 2
|
|
400
|
+
fi
|
|
401
|
+
|
|
402
|
+
CURRENT_EMB=$(jq -c '.embedding' "$TMP_EMB")
|
|
403
|
+
COSINE=$(intent_matcher_cosine "$SAVED_EMB" "$CURRENT_EMB")
|
|
404
|
+
MODE="LIVE"
|
|
405
|
+
fi
|
|
406
|
+
|
|
407
|
+
# === Compare against threshold ===
|
|
408
|
+
# Material class always passes (log only). Other classes compare cosine.
|
|
409
|
+
if [ "$IS_MATERIAL" -eq 1 ]; then
|
|
410
|
+
VERDICT="PASS-LOGGED"
|
|
411
|
+
else
|
|
412
|
+
# jq for numeric compare (bash arithmetic is int-only)
|
|
413
|
+
VERDICT=$(jq -n --argjson cos "$COSINE" --argjson thr "$THRESHOLD" \
|
|
414
|
+
'if $cos >= $thr then "PASS" else "FAIL" end')
|
|
415
|
+
VERDICT=$(echo "$VERDICT" | tr -d '"')
|
|
416
|
+
fi
|
|
417
|
+
|
|
418
|
+
# === Output ===
|
|
419
|
+
if [ "$JSON_OUT" -eq 1 ]; then
|
|
420
|
+
jq -n \
|
|
421
|
+
--arg artifact_type "$ARTIFACT_TYPE" \
|
|
422
|
+
--arg artifact_slug "$ARTIFACT_SLUG" \
|
|
423
|
+
--arg change_class "$CHANGE_CLASS" \
|
|
424
|
+
--argjson cosine "$COSINE" \
|
|
425
|
+
--argjson threshold "$THRESHOLD" \
|
|
426
|
+
--arg verdict "$VERDICT" \
|
|
427
|
+
--arg mode "$MODE" \
|
|
428
|
+
'{
|
|
429
|
+
artifact_type: $artifact_type,
|
|
430
|
+
artifact_slug: $artifact_slug,
|
|
431
|
+
change_class: $change_class,
|
|
432
|
+
cosine: $cosine,
|
|
433
|
+
threshold: $threshold,
|
|
434
|
+
verdict: $verdict,
|
|
435
|
+
mode: $mode
|
|
436
|
+
}'
|
|
437
|
+
else
|
|
438
|
+
echo "Artifact: $ARTIFACT_TYPE/$ARTIFACT_SLUG"
|
|
439
|
+
echo "Change class: $CHANGE_CLASS"
|
|
440
|
+
if [ "$IS_MATERIAL" -eq 1 ]; then
|
|
441
|
+
echo "Cosine: $COSINE (material class — log only, no threshold)"
|
|
442
|
+
else
|
|
443
|
+
echo "Cosine: $COSINE (threshold $THRESHOLD)"
|
|
444
|
+
fi
|
|
445
|
+
echo "Verdict: $VERDICT ($MODE)"
|
|
446
|
+
fi
|
|
447
|
+
|
|
448
|
+
# === Exit code ===
|
|
449
|
+
# Material class always exits 0 regardless of cosine.
|
|
450
|
+
if [ "$IS_MATERIAL" -eq 1 ]; then
|
|
451
|
+
exit 0
|
|
452
|
+
elif [ "$VERDICT" = "PASS" ]; then
|
|
453
|
+
exit 0
|
|
454
|
+
else
|
|
455
|
+
exit 1
|
|
456
|
+
fi
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tier: lite
|
|
3
|
+
# Detect drift between committed lite-manifest.json and current substrate state.
|
|
4
|
+
#
|
|
5
|
+
# Runs the generator against current source; compares entries[] against
|
|
6
|
+
# the committed manifest. If they differ, exit 1 with a diff and cure
|
|
7
|
+
# instructions. Called by CI workflow .github/workflows/lite-manifest-drift-check.yml
|
|
8
|
+
# on every PR touching lite frontmatter or the manifest itself.
|
|
9
|
+
#
|
|
10
|
+
# Per bassclef-upstream#1020 Step 5. Session 2026-08-01d option b.
|
|
11
|
+
#
|
|
12
|
+
# Cures per pre-mortem risk ledger docs/risk-ledgers/2026-08-01c-lite-manifest.md:
|
|
13
|
+
# F2.4 + F2.5 — catches tier: lite edit without manifest bump
|
|
14
|
+
# F-b.N1 — generator + drift-check bump together (same PR); no false-fail
|
|
15
|
+
# F-b.N2 — 60s workflow timeout enforced at CI layer (this script itself is fast)
|
|
16
|
+
#
|
|
17
|
+
# Compares .entries[] only. Does NOT compare .generated_at or .upstream_commit
|
|
18
|
+
# — those always differ between runs; comparing them would fire on every PR.
|
|
19
|
+
#
|
|
20
|
+
# Usage:
|
|
21
|
+
# scripts/lite-manifest-drift-check.sh # check against ./lite-manifest.json
|
|
22
|
+
# scripts/lite-manifest-drift-check.sh --root PATH # override repo root (test harness)
|
|
23
|
+
# scripts/lite-manifest-drift-check.sh --manifest P # check against alternative manifest
|
|
24
|
+
#
|
|
25
|
+
# Exit codes:
|
|
26
|
+
# 0 — no drift; manifest matches current substrate
|
|
27
|
+
# 1 — drift detected OR missing dependency
|
|
28
|
+
# 2 — manifest file missing
|
|
29
|
+
|
|
30
|
+
set -euo pipefail
|
|
31
|
+
|
|
32
|
+
REPO_ROOT_OVERRIDE=""
|
|
33
|
+
MANIFEST_OVERRIDE=""
|
|
34
|
+
TARGET_TIER="lite" # per #1295 — extended to check lite/standard/ultra manifests
|
|
35
|
+
|
|
36
|
+
while [[ $# -gt 0 ]]; do
|
|
37
|
+
case "$1" in
|
|
38
|
+
--root) REPO_ROOT_OVERRIDE="${2:-}"; shift 2 ;;
|
|
39
|
+
--manifest) MANIFEST_OVERRIDE="${2:-}"; shift 2 ;;
|
|
40
|
+
--tier) TARGET_TIER="${2:-}"; shift 2 ;;
|
|
41
|
+
-h|--help)
|
|
42
|
+
grep -E '^# ' "$0" | sed 's/^# \?//'
|
|
43
|
+
exit 0
|
|
44
|
+
;;
|
|
45
|
+
*) echo "Unknown arg: $1 (use --help)" >&2; exit 1 ;;
|
|
46
|
+
esac
|
|
47
|
+
done
|
|
48
|
+
|
|
49
|
+
case "$TARGET_TIER" in
|
|
50
|
+
lite|standard|ultra|standard-pro) ;;
|
|
51
|
+
*) echo "tier-manifest-drift-check: invalid --tier '$TARGET_TIER' (allowed: lite, standard, ultra, standard-pro)" >&2; exit 1 ;;
|
|
52
|
+
esac
|
|
53
|
+
|
|
54
|
+
if [[ -n "$REPO_ROOT_OVERRIDE" ]]; then
|
|
55
|
+
REPO_ROOT="$REPO_ROOT_OVERRIDE"
|
|
56
|
+
else
|
|
57
|
+
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
if [[ -z "$REPO_ROOT" || ! -d "$REPO_ROOT" ]]; then
|
|
61
|
+
echo "lite-manifest-drift-check: cannot resolve repo root" >&2
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
MANIFEST_FILE="${MANIFEST_OVERRIDE:-${REPO_ROOT}/${TARGET_TIER}-manifest.json}"
|
|
66
|
+
|
|
67
|
+
if [[ ! -f "$MANIFEST_FILE" ]]; then
|
|
68
|
+
echo "lite-manifest-drift-check: manifest file not found at $MANIFEST_FILE" >&2
|
|
69
|
+
echo "Run scripts/generate-lite-manifest.sh to create it." >&2
|
|
70
|
+
exit 2
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
for cmd in jq bash; do
|
|
74
|
+
if ! command -v "$cmd" >/dev/null 2>&1; then
|
|
75
|
+
echo "lite-manifest-drift-check: missing dependency: $cmd" >&2
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
done
|
|
79
|
+
|
|
80
|
+
TMP_CANDIDATE=$(mktemp)
|
|
81
|
+
trap 'rm -f "$TMP_CANDIDATE"' EXIT
|
|
82
|
+
|
|
83
|
+
bash "${REPO_ROOT}/scripts/generate-lite-manifest.sh" --tier "$TARGET_TIER" --root "$REPO_ROOT" --output "$TMP_CANDIDATE" 2>/dev/null
|
|
84
|
+
|
|
85
|
+
# Compare entries[] arrays (sorted; ignoring generated_at + upstream_commit)
|
|
86
|
+
COMMITTED_ENTRIES=$(jq -S '.entries' "$MANIFEST_FILE")
|
|
87
|
+
CANDIDATE_ENTRIES=$(jq -S '.entries' "$TMP_CANDIDATE")
|
|
88
|
+
|
|
89
|
+
if [[ "$COMMITTED_ENTRIES" == "$CANDIDATE_ENTRIES" ]]; then
|
|
90
|
+
echo "${TARGET_TIER}-manifest is up to date ($(jq '.entries | length' "$MANIFEST_FILE") entries)"
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# Drift detected — surface concrete diff
|
|
95
|
+
echo "" >&2
|
|
96
|
+
echo "============================================================" >&2
|
|
97
|
+
echo "DRIFT DETECTED: lite-manifest.json does not match substrate" >&2
|
|
98
|
+
echo "============================================================" >&2
|
|
99
|
+
echo "" >&2
|
|
100
|
+
|
|
101
|
+
COMMITTED_COUNT=$(jq '.entries | length' "$MANIFEST_FILE")
|
|
102
|
+
CANDIDATE_COUNT=$(jq '.entries | length' "$TMP_CANDIDATE")
|
|
103
|
+
echo "Committed manifest: $COMMITTED_COUNT entries" >&2
|
|
104
|
+
echo "Current substrate: $CANDIDATE_COUNT entries" >&2
|
|
105
|
+
echo "" >&2
|
|
106
|
+
|
|
107
|
+
# Show added / removed slugs by type for readability
|
|
108
|
+
COMMITTED_KEYS=$(echo "$COMMITTED_ENTRIES" | jq -r '.[] | .type + "/" + .slug' | sort)
|
|
109
|
+
CANDIDATE_KEYS=$(echo "$CANDIDATE_ENTRIES" | jq -r '.[] | .type + "/" + .slug' | sort)
|
|
110
|
+
|
|
111
|
+
ADDED=$(comm -23 <(echo "$CANDIDATE_KEYS") <(echo "$COMMITTED_KEYS"))
|
|
112
|
+
REMOVED=$(comm -13 <(echo "$CANDIDATE_KEYS") <(echo "$COMMITTED_KEYS"))
|
|
113
|
+
|
|
114
|
+
if [[ -n "$ADDED" ]]; then
|
|
115
|
+
echo "Entries ADDED to substrate but missing from manifest:" >&2
|
|
116
|
+
echo "$ADDED" | sed 's/^/ + /' >&2
|
|
117
|
+
echo "" >&2
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
if [[ -n "$REMOVED" ]]; then
|
|
121
|
+
echo "Entries in manifest but REMOVED from substrate:" >&2
|
|
122
|
+
echo "$REMOVED" | sed 's/^/ - /' >&2
|
|
123
|
+
echo "" >&2
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# Content hash changes (same slug, different content)
|
|
127
|
+
CONTENT_CHANGES=$(diff <(echo "$COMMITTED_ENTRIES" | jq -c '.[] | {type,slug,content_hash}' | sort) \
|
|
128
|
+
<(echo "$CANDIDATE_ENTRIES" | jq -c '.[] | {type,slug,content_hash}' | sort) \
|
|
129
|
+
| grep -E '^[<>]' | head -20 || true)
|
|
130
|
+
if [[ -n "$CONTENT_CHANGES" ]]; then
|
|
131
|
+
echo "Content-hash changes (same slug, different content):" >&2
|
|
132
|
+
echo "$CONTENT_CHANGES" | sed 's/^/ /' >&2
|
|
133
|
+
echo "" >&2
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
echo "----- Cure -----" >&2
|
|
137
|
+
echo " bash scripts/generate-lite-manifest.sh" >&2
|
|
138
|
+
echo " # then edit lite-manifest.json manifest_version per semver bump policy:" >&2
|
|
139
|
+
echo " # entry added → minor bump (e.g., 1.0.0 → 1.1.0)" >&2
|
|
140
|
+
echo " # entry removed → major bump (e.g., 1.1.0 → 2.0.0; breaks partners)" >&2
|
|
141
|
+
echo " # content_hash change only → patch bump (e.g., 1.1.0 → 1.1.1)" >&2
|
|
142
|
+
echo " bash scripts/render-lite-manifest-doc.sh" >&2
|
|
143
|
+
echo " git add lite-manifest.json docs/lite-manifest.md" >&2
|
|
144
|
+
echo "" >&2
|
|
145
|
+
|
|
146
|
+
exit 1
|