@thebassclef/lite 0.1.2 → 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.
Files changed (293) hide show
  1. package/dist/cli.cjs +238 -127
  2. package/dist/cli.js +240 -129
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/lite/.bassclef-source.json +10 -0
  7. package/dist/lite/.claude/settings.json +212 -0
  8. package/dist/lite/CLAUDE.md +41 -0
  9. package/dist/lite/gitignore +58 -0
  10. package/dist/lite/standards/bassclef-wiring-manifest.json +497 -0
  11. package/dist/lite/whereami.md +24 -0
  12. package/package.json +8 -2
  13. package/substrate/.bassclef/lite-manifest.json +0 -2701
  14. package/substrate/.claude/agents/architect.md +0 -70
  15. package/substrate/.claude/agents/builder.md +0 -114
  16. package/substrate/.claude/agents/designer.md +0 -156
  17. package/substrate/.claude/agents/reviewer.md +0 -88
  18. package/substrate/.claude/hooks/artifact-ingestion-gate.sh +0 -357
  19. package/substrate/.claude/hooks/assert-verify-steering.sh +0 -77
  20. package/substrate/.claude/hooks/bassclef-source-config-validate.sh +0 -215
  21. package/substrate/.claude/hooks/bassclef-sync.sh +0 -634
  22. package/substrate/.claude/hooks/compound-noun-scrub.sh +0 -292
  23. package/substrate/.claude/hooks/kiss-expansion-inject.sh +0 -69
  24. package/substrate/.claude/hooks/longrun-prep-compounding-axis-check.sh +0 -492
  25. package/substrate/.claude/hooks/longrun-prep-compounding-sequence-check.sh +0 -492
  26. package/substrate/.claude/hooks/plain-english-steering.sh +0 -156
  27. package/substrate/.claude/hooks/post-skill-friction-check.sh +0 -177
  28. package/substrate/.claude/hooks/post-skill-telemetry.sh +0 -62
  29. package/substrate/.claude/hooks/pre-build-gate.sh +0 -511
  30. package/substrate/.claude/hooks/pre-commit-gate.sh +0 -451
  31. package/substrate/.claude/hooks/session-end.sh +0 -433
  32. package/substrate/.claude/hooks/session-reflection.sh +0 -303
  33. package/substrate/.claude/hooks/skill-body-grade-gate.sh +0 -219
  34. package/substrate/.claude/hooks/skill-body-intent-drift.sh +0 -107
  35. package/substrate/.claude/hooks/skill-step-list-check.sh +0 -171
  36. package/substrate/.claude/hooks/state-validate.sh +0 -271
  37. package/substrate/.claude/hooks/substrate-clarity-gate.sh +0 -1110
  38. package/substrate/.claude/hooks/temperance-gate.sh +0 -147
  39. package/substrate/.claude/hooks/testing-tier-enforce.sh +0 -233
  40. package/substrate/.claude/hooks/turn-prose-grade-measure.sh +0 -219
  41. package/substrate/.claude/hooks/turn-prose-kiss-check.sh +0 -463
  42. package/substrate/.claude/hooks/vocabulary-migration-check.sh +0 -171
  43. package/substrate/.claude/hooks/whereami-utc-gate.sh +0 -142
  44. package/substrate/.claude/luminaries/alan-cooper.md +0 -170
  45. package/substrate/.claude/luminaries/alistair-cockburn.md +0 -140
  46. package/substrate/.claude/luminaries/amazon-pr-faq.md +0 -34
  47. package/substrate/.claude/luminaries/ash-maurya.md +0 -121
  48. package/substrate/.claude/luminaries/bill-buxton.md +0 -210
  49. package/substrate/.claude/luminaries/charles-sanders-peirce.md +0 -150
  50. package/substrate/.claude/luminaries/david-ogilvy.md +0 -192
  51. package/substrate/.claude/luminaries/don-norman.md +0 -173
  52. package/substrate/.claude/luminaries/edward-tufte.md +0 -179
  53. package/substrate/.claude/luminaries/eric-evans.md +0 -160
  54. package/substrate/.claude/luminaries/frederick-brooks.md +0 -50
  55. package/substrate/.claude/luminaries/gang-of-four.md +0 -157
  56. package/substrate/.claude/luminaries/glenford-myers.md +0 -230
  57. package/substrate/.claude/luminaries/hunt-thomas.md +0 -115
  58. package/substrate/.claude/luminaries/hyrum-wright.md +0 -94
  59. package/substrate/.claude/luminaries/jason-fried-dhh.md +0 -46
  60. package/substrate/.claude/luminaries/jesse-james-garrett.md +0 -154
  61. package/substrate/.claude/luminaries/john-ousterhout.md +0 -94
  62. package/substrate/.claude/luminaries/karl-popper.md +0 -132
  63. package/substrate/.claude/luminaries/kent-beck.md +0 -168
  64. package/substrate/.claude/luminaries/linus-torvalds.md +0 -218
  65. package/substrate/.claude/luminaries/martin-fowler.md +0 -164
  66. package/substrate/.claude/luminaries/michael-feathers.md +0 -125
  67. package/substrate/.claude/luminaries/michael-nygard.md +0 -118
  68. package/substrate/.claude/luminaries/robert-c-martin.md +0 -164
  69. package/substrate/.claude/luminaries/saltzer-schroeder.md +0 -194
  70. package/substrate/.claude/luminaries/sophia-prater.md +0 -193
  71. package/substrate/.claude/luminaries/stephen-toulmin.md +0 -131
  72. package/substrate/.claude/luminaries/tony-hoare.md +0 -170
  73. package/substrate/.claude/luminaries/vaughn-vernon.md +0 -50
  74. package/substrate/.claude/luminaries/w-edwards-deming.md +0 -158
  75. package/substrate/.claude/rules/accessor-library-discipline.md +0 -138
  76. package/substrate/.claude/rules/adr-discipline.md +0 -120
  77. package/substrate/.claude/rules/api-conventions.md +0 -125
  78. package/substrate/.claude/rules/artifact-ingestion.md +0 -179
  79. package/substrate/.claude/rules/assert-only-after-verify.md +0 -137
  80. package/substrate/.claude/rules/blocked-items.md +0 -146
  81. package/substrate/.claude/rules/bootstrap-pair-discipline.md +0 -141
  82. package/substrate/.claude/rules/branching.md +0 -28
  83. package/substrate/.claude/rules/cold-adopter-harness-discipline.md +0 -129
  84. package/substrate/.claude/rules/commit-conventions.md +0 -22
  85. package/substrate/.claude/rules/compounding-axis-fresh-analysis.md +0 -188
  86. package/substrate/.claude/rules/compounding-sequence-fresh-analysis.md +0 -188
  87. package/substrate/.claude/rules/context-engineering.md +0 -202
  88. package/substrate/.claude/rules/context-management.md +0 -85
  89. package/substrate/.claude/rules/defensive-bash.md +0 -68
  90. package/substrate/.claude/rules/deferred-actions.md +0 -233
  91. package/substrate/.claude/rules/destructive-operations.md +0 -69
  92. package/substrate/.claude/rules/diagnosis.md +0 -38
  93. package/substrate/.claude/rules/github-issue-flash-tweet.md +0 -156
  94. package/substrate/.claude/rules/guardrails.md +0 -73
  95. package/substrate/.claude/rules/hook-wire-on-author.md +0 -103
  96. package/substrate/.claude/rules/identifier-leak-prevention.md +0 -104
  97. package/substrate/.claude/rules/iteration-bet-brief-completeness.md +0 -54
  98. package/substrate/.claude/rules/lite-manifest-schema-change-discipline.md +0 -98
  99. package/substrate/.claude/rules/longrun-prep-plan-doc-compression.md +0 -89
  100. package/substrate/.claude/rules/loop-discipline.md +0 -81
  101. package/substrate/.claude/rules/manual-prod-approval.md +0 -100
  102. package/substrate/.claude/rules/marker-enrichment-discipline.md +0 -99
  103. package/substrate/.claude/rules/mobile-ephemeral-session.md +0 -109
  104. package/substrate/.claude/rules/new-dependency-check.md +0 -51
  105. package/substrate/.claude/rules/oo-ad-entry-point.md +0 -117
  106. package/substrate/.claude/rules/operator-facing-prose.md +0 -196
  107. package/substrate/.claude/rules/option-label-discipline.md +0 -108
  108. package/substrate/.claude/rules/pattern-annotation.md +0 -100
  109. package/substrate/.claude/rules/plain-english-discipline.md +0 -156
  110. package/substrate/.claude/rules/plan-enumeration-needs-value-props.md +0 -211
  111. package/substrate/.claude/rules/pr-body-shape.md +0 -317
  112. package/substrate/.claude/rules/pr-strategy.md +0 -167
  113. package/substrate/.claude/rules/pr-title-shape.md +0 -161
  114. package/substrate/.claude/rules/prototype-workflow.md +0 -65
  115. package/substrate/.claude/rules/reserved-skill-names.md +0 -123
  116. package/substrate/.claude/rules/schema-management.md +0 -49
  117. package/substrate/.claude/rules/sdlc-gates.md +0 -149
  118. package/substrate/.claude/rules/security.md +0 -37
  119. package/substrate/.claude/rules/session-artifacts.md +0 -236
  120. package/substrate/.claude/rules/skill-composition-declarations.md +0 -124
  121. package/substrate/.claude/rules/skill-description-clarity.md +0 -247
  122. package/substrate/.claude/rules/skill-procedure-step-list.md +0 -137
  123. package/substrate/.claude/rules/state-schema-validation.md +0 -162
  124. package/substrate/.claude/rules/stuck-signal-diagnostic.md +0 -140
  125. package/substrate/.claude/rules/substrate-config-schema.md +0 -98
  126. package/substrate/.claude/rules/test-list-discipline.md +0 -175
  127. package/substrate/.claude/rules/test-sufficiency.md +0 -210
  128. package/substrate/.claude/rules/testing-tier-config.md +0 -145
  129. package/substrate/.claude/rules/testing.md +0 -38
  130. package/substrate/.claude/rules/turn-estimate-grounding.md +0 -134
  131. package/substrate/.claude/rules/visual-hierarchy.md +0 -437
  132. package/substrate/.claude/rules/we-dont-break-adopters.md +0 -126
  133. package/substrate/.claude/rules/whereami-load-bearing.md +0 -202
  134. package/substrate/.claude/rules/writing-craft-discipline.md +0 -92
  135. package/substrate/.claude/rules/wu-sequencing-compounds.md +0 -145
  136. package/substrate/.claude/skills/build/SKILL.md +0 -640
  137. package/substrate/.claude/skills/chronicle/SKILL.md +0 -55
  138. package/substrate/.claude/skills/clean-artifacts/SKILL.md +0 -249
  139. package/substrate/.claude/skills/decompose/SKILL.md +0 -280
  140. package/substrate/.claude/skills/diagnose/SKILL.md +0 -297
  141. package/substrate/.claude/skills/feynman/SKILL.md +0 -90
  142. package/substrate/.claude/skills/howdoi/SKILL.md +0 -105
  143. package/substrate/.claude/skills/ia-model/SKILL.md +0 -108
  144. package/substrate/.claude/skills/interaction-design/SKILL.md +0 -112
  145. package/substrate/.claude/skills/interpret-input/SKILL.md +0 -180
  146. package/substrate/.claude/skills/journal/SKILL.md +0 -209
  147. package/substrate/.claude/skills/kiss/SKILL.md +0 -449
  148. package/substrate/.claude/skills/launch/SKILL.md +0 -915
  149. package/substrate/.claude/skills/lean-canvas/SKILL.md +0 -332
  150. package/substrate/.claude/skills/longrun/SKILL.md +0 -463
  151. package/substrate/.claude/skills/luminary/SKILL.md +0 -481
  152. package/substrate/.claude/skills/ogilvy-writing-audit/SKILL.md +0 -177
  153. package/substrate/.claude/skills/onboard-repo/SKILL.md +0 -1624
  154. package/substrate/.claude/skills/pattern-review/SKILL.md +0 -99
  155. package/substrate/.claude/skills/personas/SKILL.md +0 -207
  156. package/substrate/.claude/skills/promote/SKILL.md +0 -283
  157. package/substrate/.claude/skills/requirement/SKILL.md +0 -98
  158. package/substrate/.claude/skills/retro/SKILL.md +0 -117
  159. package/substrate/.claude/skills/riff/SKILL.md +0 -114
  160. package/substrate/.claude/skills/roadmap-reconcile/SKILL.md +0 -163
  161. package/substrate/.claude/skills/session-end/SKILL.md +0 -309
  162. package/substrate/.claude/skills/session-log/SKILL.md +0 -299
  163. package/substrate/.claude/skills/skills/SKILL.md +0 -228
  164. package/substrate/.claude/skills/spec/SKILL.md +0 -105
  165. package/substrate/.claude/skills/sprint/SKILL.md +0 -392
  166. package/substrate/.claude/skills/stage/SKILL.md +0 -384
  167. package/substrate/.claude/skills/state-a-problem/SKILL.md +0 -185
  168. package/substrate/.claude/skills/temperance/SKILL.md +0 -108
  169. package/substrate/.claude/skills/use-case/SKILL.md +0 -417
  170. package/substrate/.claude/skills/user-stories/SKILL.md +0 -268
  171. package/substrate/.claude/skills/value-prop/SKILL.md +0 -251
  172. package/substrate/.claude/skills/verify/SKILL.md +0 -160
  173. package/substrate/.claude/skills/visual-review/SKILL.md +0 -503
  174. package/substrate/.claude/skills/whats-the-plan/SKILL.md +0 -202
  175. package/substrate/.claude/skills/whereami/SKILL.md +0 -307
  176. package/substrate/AGENTS.md +0 -79
  177. package/substrate/CLAUDE-lite.md +0 -85
  178. package/substrate/CODE_OF_CONDUCT.md +0 -28
  179. package/substrate/CONTRIBUTING.md +0 -177
  180. package/substrate/README.md +0 -173
  181. package/substrate/SECURITY.md +0 -19
  182. package/substrate/architecture/decisions/ADR-029-release-pipeline.md +0 -79
  183. package/substrate/architecture/decisions/ADR-031-non-breaking-changes-adopter-discipline.md +0 -139
  184. package/substrate/architecture/decisions/ADR-032-adopter-sync-dispatcher-architecture.md +0 -192
  185. package/substrate/architecture/decisions/ADR-039-release-tagging-scheme.md +0 -145
  186. package/substrate/architecture/decisions/ADR-040-planning-skill-vocabulary-and-lite-profile.md +0 -155
  187. package/substrate/architecture/decisions/ADR-044-unified-skill-body-template.md +0 -162
  188. package/substrate/lib/clean-artifacts-sweep.sh +0 -112
  189. package/substrate/lib/code-comment-discipline.sh +0 -144
  190. package/substrate/lib/composer-preflight.sh +0 -459
  191. package/substrate/lib/hook-inject.sh +0 -255
  192. package/substrate/lib/luminary-pick.sh +0 -96
  193. package/substrate/lib/output-discipline.sh +0 -143
  194. package/substrate/lib/prose-scan-boundary.sh +0 -171
  195. package/substrate/lib/rewrite-check.sh +0 -214
  196. package/substrate/lib/state.sh +0 -1372
  197. package/substrate/lib/telemetry.sh +0 -205
  198. package/substrate/lib/tier-check.sh +0 -187
  199. package/substrate/lib/tier-dependency-audit.sh +0 -1088
  200. package/substrate/presence/install/bassclef-hook-connect.sh +0 -178
  201. package/substrate/presence/install/bassclef-sync.dispatcher.template.sh +0 -841
  202. package/substrate/presence/install/bassclef-sync.template.sh +0 -2076
  203. package/substrate/presence/install/schedule-auto-save.cron.sh +0 -88
  204. package/substrate/presence/install/schedule-auto-save.taskscheduler.md +0 -122
  205. package/substrate/scripts/aggregate-telemetry.sh +0 -217
  206. package/substrate/scripts/analyze-tier-dependencies.sh +0 -239
  207. package/substrate/scripts/generate-lite-manifest.sh +0 -505
  208. package/substrate/scripts/generate-tier-manifest.sh +0 -28
  209. package/substrate/scripts/intent-drift-check.sh +0 -456
  210. package/substrate/scripts/lite-manifest-drift-check.sh +0 -146
  211. package/substrate/scripts/render-lite-manifest-doc.sh +0 -150
  212. package/substrate/standards/adr-template.md +0 -86
  213. package/substrate/standards/api-conventions/nextjs.md +0 -84
  214. package/substrate/standards/artifact-composition.md +0 -209
  215. package/substrate/standards/bash-hook-safety.md +0 -246
  216. package/substrate/standards/bassclef-configs-schema.md +0 -232
  217. package/substrate/standards/bassclef-evolution.md +0 -143
  218. package/substrate/standards/bassclef-internal-jargon.md +0 -244
  219. package/substrate/standards/bassclef-managed-sentinel.md +0 -96
  220. package/substrate/standards/bassclef-source-config.md +0 -228
  221. package/substrate/standards/branch-stacking.md +0 -408
  222. package/substrate/standards/code-safety-principles.md +0 -176
  223. package/substrate/standards/composer-prerequisites.md +0 -155
  224. package/substrate/standards/deferred-actions-schema.md +0 -204
  225. package/substrate/standards/dependency-discipline/cargo.md +0 -39
  226. package/substrate/standards/dependency-discipline/gem.md +0 -43
  227. package/substrate/standards/dependency-discipline/go-mod.md +0 -41
  228. package/substrate/standards/dependency-discipline/npm.md +0 -42
  229. package/substrate/standards/dependency-discipline/pip.md +0 -42
  230. package/substrate/standards/deployment-topology/ec2-tailscale.md +0 -225
  231. package/substrate/standards/deployment-topology.md +0 -69
  232. package/substrate/standards/docs-sync-allowlist.md +0 -76
  233. package/substrate/standards/domain-and-dns.md +0 -145
  234. package/substrate/standards/frontend-stack.md +0 -67
  235. package/substrate/standards/frontmatter-schema.md +0 -154
  236. package/substrate/standards/graceful-exit.md +0 -227
  237. package/substrate/standards/hook-idempotency.md +0 -102
  238. package/substrate/standards/hook-injection-discipline.md +0 -202
  239. package/substrate/standards/hook-install-class.md +0 -215
  240. package/substrate/standards/input-handler-interface.md +0 -152
  241. package/substrate/standards/lite-manifest-schema-changes.md +0 -135
  242. package/substrate/standards/luminary-matching.md +0 -105
  243. package/substrate/standards/luminary-problem-patterns.md +0 -481
  244. package/substrate/standards/migration-discipline/active-record.md +0 -50
  245. package/substrate/standards/migration-discipline/alembic.md +0 -43
  246. package/substrate/standards/migration-discipline/gorm.md +0 -50
  247. package/substrate/standards/migration-discipline/prisma.md +0 -53
  248. package/substrate/standards/migration-discipline/sqlalchemy.md +0 -51
  249. package/substrate/standards/mobile-ephemeral-session.md +0 -167
  250. package/substrate/standards/model-routing-discipline.md +0 -160
  251. package/substrate/standards/ogilvy-writing-rules.md +0 -225
  252. package/substrate/standards/opener-discipline.md +0 -96
  253. package/substrate/standards/operator-facing-prose-discipline.md +0 -201
  254. package/substrate/standards/persona-schema.md +0 -229
  255. package/substrate/standards/pluggable-luminaries.md +0 -323
  256. package/substrate/standards/pr-body-discipline.md +0 -115
  257. package/substrate/standards/preview-state-schema.md +0 -189
  258. package/substrate/standards/project-directory-layout.md +0 -276
  259. package/substrate/standards/release-tagging.md +0 -137
  260. package/substrate/standards/reserved-skill-names.md +0 -120
  261. package/substrate/standards/scannable-multi-option-output.md +0 -261
  262. package/substrate/standards/sdlc-compliance.md +0 -286
  263. package/substrate/standards/sdlc-gates/typescript.md +0 -57
  264. package/substrate/standards/secrets-lifecycle.md +0 -210
  265. package/substrate/standards/security-scanner-adapter.md +0 -145
  266. package/substrate/standards/session-board.md +0 -256
  267. package/substrate/standards/skill-output-discipline.md +0 -90
  268. package/substrate/standards/state-spine-contract.md +0 -255
  269. package/substrate/standards/state-spine.md +0 -511
  270. package/substrate/standards/steering-hints/kiss-words.md +0 -11
  271. package/substrate/standards/substrate-config-schema.md +0 -267
  272. package/substrate/standards/tech-stack-config.md +0 -109
  273. package/substrate/standards/tier-dependency-analysis.md +0 -167
  274. package/substrate/standards/tier-runtime-deps/lite.md +0 -57
  275. package/substrate/standards/tier-tag-schema.md +0 -155
  276. package/substrate/standards/two-layer-config.md +0 -99
  277. package/substrate/standards/use-case-format.md +0 -292
  278. package/substrate/standards/user-story-invest.md +0 -268
  279. package/substrate/standards/velocity-and-appetite.md +0 -229
  280. package/substrate/standards/voice-input-pattern.md +0 -119
  281. package/substrate/standards/whereami-schema.md +0 -301
  282. package/substrate/standards/worktree-management.md +0 -211
  283. package/substrate/standards/writing-guide.md +0 -213
  284. package/substrate/templates/chronicle-template.md +0 -75
  285. package/substrate/templates/deferred-action-template.md +0 -45
  286. package/substrate/templates/memory-proposal-template.md +0 -77
  287. package/substrate/templates/persona-template.md +0 -200
  288. package/substrate/templates/pr-faq.md +0 -45
  289. package/substrate/templates/secret-rotation-template.md +0 -162
  290. package/substrate/templates/spec-template.md +0 -131
  291. package/substrate/templates/use-case-template.md +0 -194
  292. package/substrate/templates/user-story-template.md +0 -107
  293. package/substrate/templates/whereami-template.md +0 -101
@@ -1,456 +0,0 @@
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
@@ -1,146 +0,0 @@
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