continuous-improvement 3.23.0 → 3.25.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 (78) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +37 -0
  3. package/QUICKSTART.md +19 -20
  4. package/README.md +71 -18
  5. package/SKILL.md +4 -0
  6. package/bin/check-invariant-count.mjs +144 -0
  7. package/bin/check-routing-targets.mjs +74 -4
  8. package/bin/check-test-count.mjs +135 -0
  9. package/bin/companion-preference-status.mjs +2 -5
  10. package/bin/generate-plugin-manifests.mjs +21 -2
  11. package/bin/harvest-friction.mjs +10 -8
  12. package/bin/install.mjs +4 -14
  13. package/bin/mcp-server.mjs +4 -9
  14. package/bin/observe.mjs +3 -3
  15. package/bin/reconcile-instinct-hashes.mjs +226 -0
  16. package/bin/refresh-third-party.mjs +180 -10
  17. package/commands/discipline.md +5 -2
  18. package/commands/reconcile.md +1 -1
  19. package/commands/superpowers.md +2 -2
  20. package/commands/verify-install.md +8 -3
  21. package/hooks/companion-preference.mjs +3 -10
  22. package/hooks/config-guard.mjs +94 -0
  23. package/hooks/gateguard.mjs +39 -39
  24. package/hooks/goal-drift-stop.mjs +2 -2
  25. package/hooks/query-cost-nudge.mjs +2 -2
  26. package/hooks/recall-briefing.mjs +2 -2
  27. package/hooks/route-prompt.mjs +2 -5
  28. package/hooks/session.mjs +2 -2
  29. package/hooks/workflow-distill.mjs +2 -2
  30. package/lib/config-guard-gate.mjs +243 -0
  31. package/lib/destructive-bash.mjs +216 -0
  32. package/lib/gateguard-state.mjs +5 -1
  33. package/lib/plugin-metadata.mjs +12 -1
  34. package/lib/skill-catalog.mjs +169 -0
  35. package/llms.txt +12 -1
  36. package/package.json +5 -3
  37. package/plugins/beginner.json +1 -1
  38. package/plugins/continuous-improvement/.claude-plugin/marketplace.json +2 -2
  39. package/plugins/continuous-improvement/.claude-plugin/plugin.json +2 -2
  40. package/plugins/continuous-improvement/README.md +1 -2
  41. package/plugins/continuous-improvement/bin/mcp-server.mjs +4 -9
  42. package/plugins/continuous-improvement/bin/observe.mjs +3 -3
  43. package/plugins/continuous-improvement/commands/discipline.md +5 -2
  44. package/plugins/continuous-improvement/commands/reconcile.md +1 -1
  45. package/plugins/continuous-improvement/commands/superpowers.md +2 -2
  46. package/plugins/continuous-improvement/commands/verify-install.md +8 -3
  47. package/plugins/continuous-improvement/hooks/companion-preference.mjs +3 -10
  48. package/plugins/continuous-improvement/hooks/config-guard.mjs +94 -0
  49. package/plugins/continuous-improvement/hooks/gateguard.mjs +39 -39
  50. package/plugins/continuous-improvement/hooks/goal-drift-stop.mjs +2 -2
  51. package/plugins/continuous-improvement/hooks/hooks.json +10 -0
  52. package/plugins/continuous-improvement/hooks/query-cost-nudge.mjs +2 -2
  53. package/plugins/continuous-improvement/hooks/recall-briefing.mjs +2 -2
  54. package/plugins/continuous-improvement/hooks/route-prompt.mjs +2 -5
  55. package/plugins/continuous-improvement/hooks/session.mjs +2 -2
  56. package/plugins/continuous-improvement/hooks/workflow-distill.mjs +2 -2
  57. package/plugins/continuous-improvement/lib/config-guard-gate.mjs +243 -0
  58. package/plugins/continuous-improvement/lib/destructive-bash.mjs +216 -0
  59. package/plugins/continuous-improvement/lib/gateguard-state.mjs +5 -1
  60. package/plugins/continuous-improvement/lib/plugin-metadata.mjs +12 -1
  61. package/plugins/continuous-improvement/scripts/route-recommendation.routes.json +2 -2
  62. package/plugins/continuous-improvement/skills/README.md +0 -1
  63. package/plugins/continuous-improvement/skills/continuous-improvement/SKILL.md +4 -0
  64. package/plugins/continuous-improvement/skills/deploy-receipt/SKILL.md +1 -1
  65. package/plugins/continuous-improvement/skills/gateguard/SKILL.md +17 -2
  66. package/plugins/continuous-improvement/skills/proceed-with-the-recommendation/SKILL.md +2 -2
  67. package/plugins/continuous-improvement/skills/reconcile/SKILL.md +0 -1
  68. package/plugins/continuous-improvement/skills/superpowers/SKILL.md +5 -6
  69. package/plugins/expert.json +1 -1
  70. package/scripts/route-recommendation.routes.json +2 -2
  71. package/skills/README.md +1 -2
  72. package/skills/deploy-receipt.md +1 -1
  73. package/skills/gateguard.md +17 -2
  74. package/skills/proceed-with-the-recommendation.md +2 -2
  75. package/skills/reconcile.md +0 -1
  76. package/skills/superpowers.md +5 -6
  77. package/plugins/continuous-improvement/skills/safety-guard/SKILL.md +0 -77
  78. package/skills/safety-guard.md +0 -77
@@ -7,8 +7,8 @@
7
7
  "plugins": [
8
8
  {
9
9
  "name": "continuous-improvement",
10
- "description": "The persistent-memory and runtime-discipline layer for Claude Code. It remembers the corrections you already gave, grounds every edit in real facts before it lands, and — through the Mulahazah engine — turns each fix into a reusable instinct, so a lesson learned once is applied automatically next time with no re-teaching. Built on the 7 Laws of AI Agent Discipline (research, plan, verify, reflect, learn) and shipped as 29 bundled skills, instinct-aware hooks, an MCP toolset for recall and reflection, and a GitHub Action transcript linter that feeds real work history back into sharper instincts.",
11
- "version": "3.23.0",
10
+ "description": "The persistent-memory and runtime-discipline layer for Claude Code. It remembers the corrections you already gave, grounds every edit in real facts before it lands, and — through the Mulahazah engine — turns each fix into a reusable instinct, so a lesson learned once is applied automatically next time with no re-teaching. Built on the 7 Laws of AI Agent Discipline (research, plan, verify, reflect, learn) and shipped as 28 bundled skills, instinct-aware hooks, an MCP toolset for recall and reflection, and a GitHub Action transcript linter that feeds real work history back into sharper instincts.",
11
+ "version": "3.25.0",
12
12
  "source": "./plugins/continuous-improvement",
13
13
  "author": {
14
14
  "name": "naimkatiman"
package/CHANGELOG.md CHANGED
@@ -4,6 +4,43 @@ All notable changes to this skill are documented here.
4
4
 
5
5
  ---
6
6
 
7
+ ## [3.25.0] — 2026-09-07
8
+
9
+ ### Added
10
+
11
+ - **Tagged releases now deploy continuous-improvement.dev** — after npm publish, `release.yml` deploys `docs/landing` to the Cloudflare Pages project and re-reads the live domain so a missed deploy fails the release instead of turning `landing-drift.yml` red the next morning. Gated on `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID`: without them the step warns and skips, so a fork still gets a complete publish. Last in the job, so a Cloudflare outage cannot block npm. (#307)
12
+ - **`verify:test-count`** — every surface that states the suite size must agree, and CI tees `npm test` then asserts the claimed count against the real run. The filesystem cannot derive the number (generated cases in loops), so the docs stay the source of truth and the live run is the check. (#306)
13
+ - **`verify:invariant-count`** — derives the invariant list from the `verify:all` script and fails if `CLAUDE.md`, `AGENTS.md`, or `docs/RELEASING.md` claim a different count or a different ordered name list. (#312)
14
+
15
+ ### Changed
16
+
17
+ - **oh-my-claudecode snapshot 4.13.6 → 5.3.0** — `ultrawork` is gone upstream; the autonomous-run row now routes to `ultragoal`. The never-existent `oh-my-claudecode:retrospective` target is dropped rather than substituted. (#308, #309)
18
+ - **obra/superpowers snapshot 5.1.0 → 6.3.0**. (#304)
19
+ - **`verify:routing-targets` checks the vendored snapshot**, not just the declaration in `optional-companions.json`. A declared `oh-my-claudecode:` / `superpowers:` / `agent-skills:` / `ruflo-swarm:` target must exist at `third-party/<snapshot>/skills/<name>/`. (#311)
20
+
21
+ ### Fixed
22
+
23
+ - **`refresh-third-party.mjs` keeps `OUR_NOTES.md` and `.fork-only-skills.txt`** across the wipe, and reports how many of ours were preserved. (#310)
24
+ - **Refresh aborts before the wipe** if upstream removed a skill our flat source still routes to, naming the skills and files. Bypass is `--allow-stale-refs`, not `--force`. (#313)
25
+ - **Brainstorming dispatcher wording** now describes the three-path router, not just the architectural path. (#305)
26
+
27
+ ## [3.24.0] — 2026-09-06
28
+
29
+ ### Added
30
+
31
+ - **`config-guard`: the files that wire the guardrails are no longer unguarded** — a new PreToolUse hook (`hooks/config-guard.mjs`, matcher `Bash|Edit|MultiEdit|Write|NotebookEdit`) watches `.claude/settings.json`, `.claude/settings.local.json`, `.mcp.json`, `hooks.json`, `.claude/hooks/`, `.claude/plugins/`, `.claude-plugin/`, and the `claude plugin` / `mcp` / `config` CLI forms that edit them. Until now an agent could switch off every gate this plugin ships in a single `Edit` and nothing stood in the way. `CI_CONFIG_GUARD=warn` is the default and only prints one stderr line; `block` denies with the reason and the bypass; `off` disables it. Reads never trigger it, `CI_CONFIG_GUARD_ALLOW=true` clears one call, and it fails open on malformed input. Ported from karanb192/claude-code-hooks (MIT); the code and pattern list are ours. (#302)
32
+
33
+ ### Changed
34
+
35
+ - **Destructive Bash is caught by structure, not spelling** — the guard was a 19-entry substring list, so `rm -r -f dist` walked past a rule written as `rm -rf`. A new pure classifier (`lib/destructive-bash.mjs`) splits the command at unquoted separators, tokenizes each segment (skipping `sudo`, `env` and `VAR=` prefixes) and applies structured rules for recursive-force `rm`, forced `git clean`, discarding `git checkout` / `git restore`, `find -delete`, a `+refspec` push and `git stash drop`, falling back to the original substring list. Every deny now ends with `Matched rule: <id>`, so the agent and the operator can see which rule fired. (#301)
36
+ - **`CI_GATEGUARD_EXCLUDE` says when it skips the gate** — an exclusion used to be silent, so an operator whose config excluded a path saw no block and no explanation. The hook now prints one stderr line naming the matched fragment, and a catch-all fragment such as `/` or `.` is reported as the file gate being off for every path. The decision itself is unchanged, and destructive Bash is still never excluded. (#300)
37
+ - **`safety-guard` retired; the six context-engineering rules applied across the bundle** — the skill's runtime edge never existed, so it is removed rather than left as prose that implies enforcement. The bundle is now 28 skills. (#293)
38
+ - **The site and the README state only what the code does** — continuous-improvement.dev rebuilt around one question, "did I check, or did I hope": the two `/plugin` commands as the install, the literal deny JSON captured from the hook instead of mocked spec boxes, every enforcement card labelled with its real default, and a "what it cannot do" block naming the honor-system clearance, the ungated Bash writes and the fact that a Beginner install alone does not learn. New `docs/philosophy.md` carries the sourcing. Unsourced numbers ("~90% of users", "nearly every install support thread") are gone. (#299, #296)
39
+
40
+ ### Fixed
41
+
42
+ - **One instinct hash for `C:/` and `c:/`** — a Windows drive letter differing only in case produced two project hashes, so observations split across two instinct directories and `/harvest` read half the history. Harvest sandbox false positives dropped in the same pass. (#297)
43
+
7
44
  ## [3.23.0] — 2026-08-15
8
45
 
9
46
  ### Added
package/QUICKSTART.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Zero to working in under 2 minutes.
4
4
 
5
- This is the **Beginner** path. It mirrors README.md and is enough for ~90% of users — no Node, no bash, no shell. If you want the MCP server, observation hooks, and instinct packs, see the **Expert (npx)** section at the bottom.
5
+ This is the **Beginner** path. It mirrors README.md and is enough for most users — no npm install, no bash, no shell (the hooks run with the `node` on your PATH, 18+; without it the gate is silently absent). If you want the MCP server, observation hooks, and instinct packs, see the **Expert (npx)** section at the bottom.
6
6
 
7
7
  ---
8
8
 
@@ -33,7 +33,7 @@ Without it, `/superpowers` still works — it falls back to inline behavior —
33
33
  /discipline
34
34
  ```
35
35
 
36
- You should see the 7 Laws quick-reference card. If the command is not recognized after a restart, see Troubleshooting in [README.md](README.md#troubleshooting-install).
36
+ You should see the 7 Laws quick-reference card. If the command is not recognized after a restart, see Troubleshooting in [README.md](README.md#install).
37
37
 
38
38
  **Check 2 — runtime gate is firing** (the `hooks/gateguard.mjs` script must invoke). Ask Claude to write a throwaway file with no research first:
39
39
 
@@ -41,7 +41,7 @@ You should see the 7 Laws quick-reference card. If the command is not recognized
41
41
  Edit a new file scratch.txt and put the word "hello" in it. Don't research anything first.
42
42
  ```
43
43
 
44
- You should see Claude **blocked** by the bundled `gateguard` PreToolUse hook (`hooks/gateguard.mjs`) with a fact-list reason: list importers, list public functions affected, show data-file schemas, quote the user instruction. That block is the proof the hook is wired and firing. If Claude writes the file with no pause, the hook did not load — see [README.md → Troubleshooting](README.md#troubleshooting-install).
44
+ You should see Claude **blocked** by the bundled `gateguard` PreToolUse hook (`hooks/gateguard.mjs`) with a fact-list reason: list importers, list public functions affected, show data-file schemas, quote the user instruction. That block is the proof the hook is wired and firing. If Claude writes the file with no pause, the hook did not load — see [README.md → Troubleshooting](README.md#install).
45
45
 
46
46
  If you also want to confirm observation hooks: run `/dashboard` and look for a non-zero `Total` under `Observations` — that proves `observe.sh` / `observe.mjs` is recording tool calls.
47
47
 
@@ -60,21 +60,14 @@ If you ever see Claude skip a Law that the runtime hook doesn't enforce, name it
60
60
 
61
61
  ## Step 2: Use it
62
62
 
63
- Give your agent a task and prefix it:
63
+ Give the agent a real task. You do **not** have to prefix every prompt — the `gateguard` PreToolUse hook (`hooks/gateguard.mjs`) fires on `Edit` / `Write` / destructive `Bash` either way.
64
64
 
65
- ```
66
- Use the continuous-improvement framework to [your task here].
67
- ```
65
+ If you want the model-side loop named out loud:
68
66
 
69
- Examples:
70
67
  ```
71
68
  Use the continuous-improvement framework to add pagination to the users API endpoint.
72
- Use the continuous-improvement framework to debug why the login form breaks on mobile.
73
- Use the continuous-improvement framework to refactor the payment module to use the new SDK.
74
69
  ```
75
70
 
76
- Your agent will research, plan, execute one thing at a time, verify, and reflect.
77
-
78
71
  For long tasks that need persistent notes on disk, run:
79
72
 
80
73
  ```
@@ -85,24 +78,26 @@ That creates `task_plan.md`, `findings.md`, and `progress.md` in the project roo
85
78
 
86
79
  ---
87
80
 
88
- ## Step 3: Check learning
81
+ ## Step 3: Close the loop (this is the learning trigger)
89
82
 
90
- After completing non-trivial work, run the canonical reflection command:
83
+ Hooks capture tool calls silently. Instincts do **not** form until you ask. After a real task, run:
91
84
 
92
85
  ```
93
86
  /seven-laws
94
87
  ```
95
88
 
96
- This shows what the system has learned instincts, confidence levels, and the current auto-level. `/continuous-improvement` is kept as an alias for backward compatibility and runs the same workflow.
89
+ That is reflect + analyze + status. Skip it and observations pile up while nothing compounds. `/continuous-improvement` is an alias for the same workflow.
90
+
91
+ When a bug feels familiar: `/recall <the error>`. One defect, one PR: `/ship`.
97
92
 
98
93
  ---
99
94
 
100
95
  ## How auto-leveling works
101
96
 
102
- You don't configure anything. The system promotes itself. The unit is
97
+ On-demand by default (not every session start — that costs tokens). The unit is
103
98
  **observations** — one per tool call, not one per session — so a single active
104
99
  session can produce dozens. The four levels below mirror the source-of-truth
105
- table in [SKILL.md](SKILL.md):
100
+ table in [SKILL.md](SKILL.md). They apply when you run `/seven-laws` / `analyze`, a weekly schedule, or always-on:
106
101
 
107
102
  | Level | Trigger | What happens |
108
103
  |-------|---------|-------------|
@@ -124,7 +119,7 @@ Corrections drop instinct confidence. Unused instincts decay. The system self-co
124
119
  → Reply: *"What verification command did you run? Show me the output."*
125
120
 
126
121
  **No instincts showing up yet?**
127
- Normal. The system needs 20+ observations before it creates instincts. Keep working.
122
+ Run `/seven-laws` after a real session. Then check `/dashboard`. Zero `Total` under Observations means capture is not wired (Expert install / plugin hooks). A high Total and zero instincts means you have not closed the loop yet. 20+ observations is the floor before analysis creates instincts.
128
123
 
129
124
  ---
130
125
 
@@ -139,7 +134,7 @@ npx continuous-improvement install --mode expert
139
134
  npx continuous-improvement install --pack react # optional: react | python | go | meta
140
135
  ```
141
136
 
142
- Precondition: Node 18 / 20 / 22. Runtime hooks execute Node directly, so Git Bash and `jq` are not required. Re-run the installer once after upgrading to migrate legacy Bash hook rows. See [README.md § Expert](README.md#expert--adds-mcp-server-observation-hooks-and-instinct-packs) for the troubleshooting matrix.
137
+ Precondition: Node 18 / 20 / 22. Runtime hooks execute Node directly, so Git Bash and `jq` are not required. Re-run the installer once after upgrading to migrate legacy Bash hook rows. See [README.md § Install](README.md#install) for the troubleshooting matrix.
143
138
 
144
139
  Verify with `/dashboard` — you should see instinct health and observation count.
145
140
 
@@ -147,7 +142,11 @@ Verify with `/dashboard` — you should see instinct health and observation coun
147
142
 
148
143
  ## That's it
149
144
 
150
- The skill is most valuable when:
145
+ Smarter models do not retire this. They still skip your existing helper, claim "done" without the test output, and forget last week's correction. The runtime gate (`hooks/gateguard.mjs`) and this-repo memory do not merge into the model.
146
+
147
+ You benefit in three rungs: (1) install and work — the gate fires; (2) `/seven-laws` after a real session — observations become instincts; (3) Expert + optional `CLAUDE_RECALL_BRIEFING=1` — memory fires without asking.
148
+
149
+ Most valuable when:
151
150
  - You're under pressure and tempted to skip steps
152
151
  - A task has failed 2+ times
153
152
  - You want your agent to stop repeating the same mistakes
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- <!-- README landing-page structure rationale: docs/plans/2026-06-28-readme-progressive-disclosure.md (supersedes docs/plans/2026-05-14-readme-landing-rewrite.md) -->
1
+ <!-- README landing-page structure rationale: docs/plans/2026-08-22-readme-still-useful.md (adds model-forward + benefit rungs; prior: docs/plans/2026-06-28-readme-progressive-disclosure.md, docs/plans/2026-05-14-readme-landing-rewrite.md) -->
2
2
 
3
3
  <p align="center">
4
4
  <img src="assets/combined.gif" alt="Before vs After — The 7 Laws of AI Agent Discipline" width="700" />
@@ -23,19 +23,19 @@
23
23
  </p>
24
24
 
25
25
  <p align="center">
26
- <b>New here?</b> → <a href="QUICKSTART.md">QUICKSTART.md</a> (2 minutes) · <a href="https://continuous-improvement.dev">continuous-improvement.dev</a>
26
+ <b>New here?</b> → <a href="QUICKSTART.md">QUICKSTART.md</a> (2 minutes) · <a href="https://continuous-improvement.dev">continuous-improvement.dev</a> · Latest: <a href="CHANGELOG.md">v3.23.0</a> (2026-08-15)
27
27
  </p>
28
28
 
29
29
  ## Quick start
30
30
 
31
- Inside Claude Code — two commands, no Node, no bash:
31
+ Inside Claude Code — two commands, no npm install, no bash (the hooks run with the `node` on your PATH, 18+):
32
32
 
33
33
  ```bash
34
34
  /plugin marketplace add naimkatiman/continuous-improvement
35
35
  /plugin install continuous-improvement@continuous-improvement
36
36
  ```
37
37
 
38
- **If you don't know which to pick, use Beginner.** That is the install above — enough for ~90% of users. Want the MCP server, observation hooks, and instinct packs too? See [Expert install](#install) below.
38
+ **If you don't know which to pick, use Beginner.** That is the install above — enough for most users. Want the MCP server, observation hooks, and instinct packs too? See [Expert install](#install) below.
39
39
 
40
40
  Verify it is live: run `/discipline` in Claude Code and you should see the 7 Laws card. (Commands load on session start — if it is not recognized, restart Claude Code once.)
41
41
 
@@ -53,12 +53,54 @@ Claude Code is powerful but leaves intelligence on the table: it edits before re
53
53
 
54
54
  ---
55
55
 
56
+ ## As models get better
57
+
58
+ Yes. This is still worth installing. Smarter models write more plausible code, faster. They still skip the helper that already exists in *your* repo, say "done" without the test output, and forget last Tuesday's correction. Those are harness problems, not IQ problems.
59
+
60
+ | Merges into the model over time | Does not merge — this is the product |
61
+ |---|---|
62
+ | Planning etiquette, "remember to verify" reminders, routing flavor | The runtime gate (`hooks/gateguard.mjs`) that physically blocks an unresearched `Edit` |
63
+ | Generic coding taste | *This repo's* past fixes, refusals, and instincts |
64
+ | Sounding sure | Proof: the command you ran, the SHA that deployed, the test that failed then passed |
65
+
66
+ The standing stance is [`model-forward`](skills/model-forward.md): go with the model, retire scaffolding when the native harness covers it, keep the two invariants — goal-driven execution, and research / verify / learn guardrails. Skills are disposable. The gate and the memory are not.
67
+
68
+ ---
69
+
70
+ ## How you actually benefit
71
+
72
+ Three rungs. Most people stop at 1, never run `/seven-laws`, and conclude "it didn't learn." Capture is silent. Instincts form when you close the loop.
73
+
74
+ | Rung | What you do | What you get | When |
75
+ |---|---|---|---|
76
+ | **1. Same session** | Beginner install (Quick start above). Give the agent a real task. Do not prefix every prompt — `hooks/gateguard.mjs` fires on `Edit` / `Write` / destructive `Bash`. | Reckless edits get blocked. Fake "done" has nowhere to hide once [`verification-loop`](skills/verification-loop.md) / [`tdd-workflow`](skills/tdd-workflow.md) run. | First hour. No memory required. |
77
+ | **2. Close the loop** | After a non-trivial session: `/seven-laws`. When a bug feels familiar: `/recall <the error>`. One defect, one PR: `/ship`. Long task: `/planning-with-files` (writes `task_plan.md`). | Observations become instincts. Yesterday's correction survives into today. | After the first real task, then at session end. |
78
+ | **3. Memory that fires without asking** | Expert install (`npx`, below). Optional: `CLAUDE_RECALL_BRIEFING=1`. After ~20 observations: `/harvest` and `/distill`. | Starter instincts, MCP tools (`ci_plan_init` / `ci_recall` / distill), a briefing of the last relevant fix on the next related prompt. | When you want compounding, not just a seatbelt. |
79
+
80
+ `/learn-eval`, `/harvest`, and `/distill` return empty on day 1 if observation history is missing. That is not a broken command — it is rung 3 without rung 2. Check `/dashboard`: a zero `Total` under Observations means capture is not wired; a high Total and zero instincts means you have not run `/seven-laws` yet.
81
+
82
+ ---
83
+
56
84
  ## See it in action
57
85
 
58
86
  <p align="center">
59
87
  <img src="assets/gateguard-demo.png" alt="gateguard blocks an unresearched Edit until the investigation is on the table" width="820" />
60
88
  </p>
61
89
 
90
+ The image is rendered from [`demo/gateguard-demo.html`](demo/gateguard-demo.html), faithful to what the hook prints. The literal output of `hooks/gateguard.mjs` on a `Write` payload with no research on the table (v3.23.0, paths shortened):
91
+
92
+ ```json
93
+ {
94
+ "hookSpecificOutput": {
95
+ "hookEventName": "PreToolUse",
96
+ "permissionDecision": "deny",
97
+ "permissionDecisionReason": "Before creating src/lib/retry-helper.mts, present these facts:\n\n 1. List ALL files that import/require this file (use Grep)\n 2. List the public functions/classes affected by this change\n 3. If this file reads/writes data files, show field names, structure, and date format\n 4. Quote the user's current instruction verbatim\n\nThen clear the gate and retry the same call. ..."
98
+ }
99
+ }
100
+ ```
101
+
102
+ One denied call, a printed reason, a retry. Reproduce it yourself: `printf '{"tool_name":"Write","tool_input":{"file_path":"x.mts"}}' | node hooks/gateguard.mjs`.
103
+
62
104
  Without Continuous Improvement, "fix the login redirect bug" looks like this:
63
105
 
64
106
  > Claude edits `Login.tsx`, `LoginForm.tsx`, `useAuth.ts`, `authRouter.ts`, `redirects.ts`, and `useNavigate.ts` — six files, no plan, no investigation. Says "done". The redirect still loops. You roll back six files.
@@ -93,6 +135,10 @@ Research -> Plan -> Execute (one thing) -> Verify -> Reflect -> Learn -> Iterate
93
135
 
94
136
  Full spec, reflection-block format, and anti-examples: [SKILL.md](SKILL.md). Full Law-to-tool alignment matrix: [CONTRIBUTING.md § Law Coverage Matrix](CONTRIBUTING.md#law-coverage-matrix).
95
137
 
138
+ **Why these seven.** Every red flag in the table is a wish standing in for a check: "this should work" hopes the test passes, "I'll remember" hopes memory survives the session. The Laws are one old sentence turned into checks an agent can run on itself: the wise one takes account of himself and works for what comes after; the weak one follows his impulse and merely wishes (Jami` at-Tirmidhi 2459). You do not need the theology to use the test: before you say done, did you check, or did you hope? Sourcing, both gradings, and the mapping to each Law: [docs/philosophy.md](docs/philosophy.md).
139
+
140
+ **What a gate can and cannot do.** `hooks/gateguard.mjs` can force the *form* of research; it cannot force honesty. It stays honor-system once the agent says the facts are on the table, which is why the account is settled by outcomes instead of by the agent's word: a correction cuts an instinct's confidence, a drifted session cannot quietly say done, and "verified" means the command output is in the reply.
141
+
96
142
  ---
97
143
 
98
144
  ## Install
@@ -153,7 +199,7 @@ Update later with `/plugin marketplace update continuous-improvement` or by re-r
153
199
  <details>
154
200
  <summary><b>Troubleshooting install</b></summary>
155
201
 
156
- Three failures account for nearly every install support thread. Try them in order:
202
+ Three failures account for most install problems. Try them in order:
157
203
 
158
204
  | Symptom | Real cause | Fix |
159
205
  |---|---|---|
@@ -177,6 +223,9 @@ The framework has documented operator-level modes that change hook behavior with
177
223
  | `CLAUDE_TYPECHECK_GATE` | `hooks/typecheck-stop.mjs` (a `Stop` hook) runs the project typecheck (the `typecheck` npm script, else a local `tsc --noEmit`) on changed TS files at turn end and feeds a failure back to the model. `off` (default) is a no-op — the global advisory `typecheck-changed.sh` stays the default layer; `warn` prints a one-line stderr notice; `block` re-prompts with the tsc output so a headless/autonomous `-p` loop fixes its own type errors before ending the turn. Skips non-TS repos and turns where no TS file changed; fails open on any error or timeout. | bash/zsh: `export CLAUDE_TYPECHECK_GATE=block` in `~/.bashrc` / `~/.zshrc`. PowerShell: `$env:CLAUDE_TYPECHECK_GATE='block'` (session) or `[Environment]::SetEnvironmentVariable('CLAUDE_TYPECHECK_GATE','block','User')` (persistent). |
178
224
  | `CLAUDE_RECALL_BRIEFING=1` | `hooks/recall-briefing.mjs` (a UserPromptSubmit hook) makes episodic memory proactive: on the first substantive prompt of a session it searches this project's past observations (BM25) and injects a one-time `<system-reminder>` with the most relevant prior activity, so the agent reuses a past fix instead of re-deriving it. Opt-in and default off; it is an amplifier, never a gate — it cannot block a prompt and fails open. The `ci_recall` MCP tool stays available for explicit, deeper searches. | bash/zsh: `export CLAUDE_RECALL_BRIEFING=1` in `~/.bashrc` / `~/.zshrc`. PowerShell: `$env:CLAUDE_RECALL_BRIEFING=1` (session) or `[Environment]::SetEnvironmentVariable('CLAUDE_RECALL_BRIEFING','1','User')` (persistent). |
179
225
  | `CLAUDE_WORKFLOW_DISTILL_NUDGE=on` | `hooks/workflow-distill.mjs` (a `Stop` hook) closes the orchestration-to-memory loop: when a native Workflow run's output then passed a verify in the same session, it prints a one-line stderr nudge to run the `ci_distill_from_workflow` MCP tool, so an expensive multi-agent run leaves a durable Mulahazah draft instinct instead of evaporating. `on` enables it; default (unset or any other value) is off. Opt-in amplifier, never a gate — it cannot block the Stop, dedupes per run, and fails open. | bash/zsh: `export CLAUDE_WORKFLOW_DISTILL_NUDGE=on` in `~/.bashrc` / `~/.zshrc`. PowerShell: `$env:CLAUDE_WORKFLOW_DISTILL_NUDGE='on'` (session) or `[Environment]::SetEnvironmentVariable('CLAUDE_WORKFLOW_DISTILL_NUDGE','on','User')` (persistent). |
226
+ | `CI_GATEGUARD_EXCLUDE` | Comma-separated path fragments that `hooks/gateguard.mjs` skips for Edit / Write / MultiEdit (case-insensitive substring match on the forward-slash path). Meant for a prose wiki or a generated scratch dir. The hook prints one stderr line naming the fragment whenever an exclusion fires, and a catch-all fragment such as `/` or `.` matches every path and is reported as the file gate being off. Destructive Bash is never excluded. Unset (default) changes nothing. | bash/zsh: `export CI_GATEGUARD_EXCLUDE=docs/wiki,scratch/`. PowerShell: `$env:CI_GATEGUARD_EXCLUDE='docs/wiki,scratch/'` (session). |
227
+ | `CI_GATEGUARD_TARGET_LOCK=block` | `hooks/gateguard.mjs` refuses any Edit / Write / MultiEdit whose absolute target canonicalizes outside the session project root, before the fact gate and independent of clearance, so a wrong-repo or wrong-worktree write is denied even with perfect facts. Off by default. | bash/zsh: `export CI_GATEGUARD_TARGET_LOCK=block`. PowerShell: `$env:CI_GATEGUARD_TARGET_LOCK='block'` (session). |
228
+ | `CI_CONFIG_GUARD` | `hooks/config-guard.mjs` (a PreToolUse hook on Bash / Edit / Write / MultiEdit / NotebookEdit) watches the files that wire the guardrails: `.claude/settings*.json`, `.mcp.json`, `hooks.json`, `.claude/hooks/`, `.claude/plugins/`, `.claude-plugin/`, and the `claude plugin\|mcp\|config` CLI forms that edit them. `warn` (default) prints one stderr line and never blocks; `block` denies the call with the reason and the bypass; `off` disables it. Reads never trigger it. One-call bypass: `CI_CONFIG_GUARD_ALLOW=true`. Fails open. | bash/zsh: `export CI_CONFIG_GUARD=block` in `~/.bashrc` / `~/.zshrc`. PowerShell: `$env:CI_CONFIG_GUARD='block'` (session) or `[Environment]::SetEnvironmentVariable('CI_CONFIG_GUARD','block','User')` (persistent). |
180
229
  | `CLAUDE_QUERY_COST_NUDGE=on` | `hooks/query-cost-nudge.mjs` (a `Stop` hook) guards against surprise DB bills: when the working tree has changed DB/query files (`.sql`, `.prisma`, `migrations/`, `/db/`, `schema.*`, `drizzle`) at turn end, it injects a once-per-session `additionalContext` reminder to run a D1-aware cost audit — dispatch the `database-reviewer` agent or check EXPLAIN QUERY PLAN, index coverage, N+1, and D1 `rows_read` billing before finishing. `on` enables it; default (unset) is off. Opt-in amplifier, never a gate; dedupes per session and fails open. | bash/zsh: `export CLAUDE_QUERY_COST_NUDGE=on` in `~/.bashrc` / `~/.zshrc`. PowerShell: `$env:CLAUDE_QUERY_COST_NUDGE='on'` (session) or `[Environment]::SetEnvironmentVariable('CLAUDE_QUERY_COST_NUDGE','on','User')` (persistent). |
181
230
 
182
231
  </details>
@@ -208,26 +257,26 @@ Shared files (`GEMINI.md`, `AGENTS.md`, `.rules`, `CONVENTIONS.md`, `copilot-ins
208
257
 
209
258
  ## Who this is for
210
259
 
211
- <details>
212
- <summary><b>Use it if you ship from real repos — skip it if you only do one-off prompts</b></summary>
213
-
214
260
  Use this if you:
215
261
 
216
262
  - ship from real repositories with real consequences
217
263
  - have been bitten by an agent that edits before understanding
218
264
  - want tests, builds, or healthchecks to pass before "done"
219
- - want lessons from yesterday to survive into today
265
+ - want the work to outlive the session: lessons from yesterday survive into today, and today's fix survives into the next engineer's week
220
266
 
221
267
  Skip it if you:
222
268
 
223
269
  - only do one-off prompts (no edits, no commits)
224
- - do not use Claude Code
225
270
  - dislike *any* friction before agent edits
226
- - want a prompt template, not a runtime gate
271
+ - want a prompt template, not a runtime gate (`hooks/gateguard.mjs`)
227
272
 
228
- (The runtime gate is `hooks/gateguard.mjs`; full mechanics in [How enforcement works](#install) above.)
273
+ Claude Code gets the full install (hooks, MCP, instincts). Other agents can still load the 7 Laws as a rules file — see [Works with other agents](#install).
229
274
 
230
- </details>
275
+ ---
276
+
277
+ ## What leaves your machine
278
+
279
+ Nothing. Observations (`observations.jsonl`) and instincts live under `~/.claude/instincts/` and are never uploaded. The npx installer makes one throttled, fail-open read of the public npm registry to print a newer-version notice; `CLAUDE_CI_UPDATE_CHECK=off` silences it. No telemetry. Raw observation rows can contain command heads and file paths, so review them before you share an export — see [SECURITY.md](SECURITY.md).
231
280
 
232
281
  ---
233
282
 
@@ -243,7 +292,7 @@ You have used Claude Code (or any agentic coding tool) long enough to recognize
243
292
  | 1 | **Misalignment** | The agent doesn't do what you want — invents requirements, reinvents helpers that already exist, or builds the wrong thing before anyone challenged the idea | Law 1 (Research) | [`roast`](skills/roast.md), [`grill-me`](skills/grill-me.md), [`grill-with-docs`](skills/grill-with-docs.md), [`gateguard`](skills/gateguard.md), [`workspace-surface-audit`](skills/workspace-surface-audit.md) |
244
293
  | 2 | **No shared language** | The agent uses 20 words where 1 would do; jargon decoded fresh every session; variable names drift from domain terms | Law 2 (Plan), Law 7 (Learn) | [`grill-with-docs`](skills/grill-with-docs.md) (writes & maintains `CONTEXT.md`), [`token-budget-advisor`](skills/token-budget-advisor.md), [`strategic-compact`](skills/strategic-compact.md) |
245
294
  | 3 | **No feedback loop** | The code doesn't work — agent claims "done" without running build, tests, or healthcheck | Law 4 (Verify) | [`tdd-workflow`](skills/tdd-workflow.md), [`verification-loop`](skills/verification-loop.md), [`deploy-receipt`](skills/deploy-receipt.md) |
246
- | 4 | **Design rot** | Ball-of-mud accelerates — agent bundles three concerns into one PR, stacks untested changes, ignores prior architectural decisions | Law 2 (Plan), Law 3 (One Thing) | [`superpowers:writing-plans`](https://github.com/obra/superpowers/blob/main/skills/writing-plans/SKILL.md), [`safety-guard`](skills/safety-guard.md), [`worktree-safety`](skills/worktree-safety.md), [`wild-risa-balance`](skills/wild-risa-balance.md) |
295
+ | 4 | **Design rot** | Ball-of-mud accelerates — agent bundles three concerns into one PR, stacks untested changes, ignores prior architectural decisions | Law 2 (Plan), Law 3 (One Thing) | [`superpowers:writing-plans`](https://github.com/obra/superpowers/blob/main/skills/writing-plans/SKILL.md), [`gateguard`](skills/gateguard.md), [`worktree-safety`](skills/worktree-safety.md), [`wild-risa-balance`](skills/wild-risa-balance.md) |
247
296
  | 5 | **Forgotten lessons** | Next session starts from zero — prior corrections, decisions, instincts are lost; the same mistake repeats next week | Law 5 (Reflect), Law 7 (Learn) | [`handoff`](skills/handoff.md), Mulahazah instinct engine |
248
297
 
249
298
  Three of those alignment + reflection skills (`grill-me`, `grill-with-docs`, `handoff`) are MIT-licensed ports from mattpocock/skills; the rest are continuous-improvement-native. Every failure mode has at least one runtime hook or model-side skill that catches it before it lands in the diff.
@@ -257,7 +306,7 @@ Three of those alignment + reflection skills (`grill-me`, `grill-with-docs`, `ha
257
306
  <details>
258
307
  <summary><b>How instincts form, level up, and decay — you configure nothing</b></summary>
259
308
 
260
- Hooks capture every tool call. After ~20 observations Claude analyzes patterns and creates **instincts** with confidence scores: silent below 0.5, suggested at 0.5–0.69, auto-applied at 0.7+. Corrections drop confidence by 0.1; unused instincts decay. Project-scoped; promoted to global after seen across 2+ projects. You configure nothing.
309
+ Hooks capture every tool call silently. When you run `/seven-laws` (or `/harvest`, `/distill`) after ~20 observations, Claude analyzes patterns and creates **instincts** with confidence scores: silent below 0.5, suggested at 0.5–0.69, auto-applied at 0.7+. What runs in code today: `ci_reinforce` adds 0.15 on an accepted suggestion and cuts 0.1 on a correction, and `/harvest` weights recurring friction by recency. The confirm, decay, and cross-project promotion rules in [SKILL.md](SKILL.md) are instructions the model applies during analysis, not a background process. Nothing forms until you close the loop.
261
310
 
262
311
  <p align="center">
263
312
  <img src="assets/diagram-mulahazah-learning.jpg" alt="Mulahazah pipeline" width="820" />
@@ -314,7 +363,7 @@ All 29 legacy commands ship in the marketplace bundle. The marketplace Beginner
314
363
 
315
364
  ## Skills
316
365
 
317
- The plugin ships **29 skills** — 1 core + 1 featured + 7 tier-1 + 17 tier-2 + 3 always-bundled. Beginner install gets tier-1, featured, and the always-bundled companion; Expert adds tier-2, the MCP server, and observation hooks. The tier-1 `ship` skill is the single source for the `/ship` workflow and the npm installer makes it global across Claude Code projects. Full catalog with per-skill descriptions, Law tagging, and drop-in single-file install: [docs/skills.md](docs/skills.md). Adding a 30th skill: [CONTRIBUTING.md § Evolution — adding a new skill](CONTRIBUTING.md#evolution--adding-a-new-skill).
366
+ The plugin ships **28 skills** — 1 core + 1 featured + 7 tier-1 + 16 tier-2 + 3 always-bundled. Beginner install gets tier-1, featured, and the always-bundled companion; Expert adds tier-2, the MCP server, and observation hooks. The tier-1 `ship` skill is the single source for the `/ship` workflow and the npm installer makes it global across Claude Code projects. Full catalog with per-skill descriptions, Law tagging, and drop-in single-file install: [docs/skills.md](docs/skills.md). Adding a 29th skill: [CONTRIBUTING.md § Evolution — adding a new skill](CONTRIBUTING.md#evolution--adding-a-new-skill).
318
367
 
319
368
  ---
320
369
 
@@ -357,11 +406,15 @@ Proof-format templates ship in [templates/](templates/): `release_receipt_templa
357
406
 
358
407
  - [QUICKSTART.md](QUICKSTART.md) — 2-minute setup
359
408
  - [SKILL.md](SKILL.md) — full 7 Laws spec
360
- - [docs/skills.md](docs/skills.md) — full 29-skill catalog
409
+ - [skills/model-forward.md](skills/model-forward.md) — what stays valuable as models improve
410
+ - [docs/skills.md](docs/skills.md) — full 28-skill catalog
361
411
  - [examples/](examples/) — bug fix, feature build, refactor walkthroughs
362
412
  - [templates/insights-claude-md.md](templates/insights-claude-md.md) — paste-in CLAUDE.md blocks for verification discipline, environment notes, think-before-acting, and git/deploy workflow (sourced from the 28-day usage report)
363
413
  - [CONTRIBUTING.md](CONTRIBUTING.md) — architecture, repo internals, adding a new skill
414
+ - [docs/philosophy.md](docs/philosophy.md) — the one sentence the product is built on, with sources and the mapping to each Law
415
+ - [CHANGELOG.md](CHANGELOG.md) — what changed in each release
364
416
  - [SECURITY.md](SECURITY.md)
417
+ - Found a bug? [Open an issue](https://github.com/naimkatiman/continuous-improvement/issues) with the one-line output of `/verify-install`
365
418
 
366
419
  <details>
367
420
  <summary><b>Uninstall · the brand stack · in the wild</b></summary>
@@ -381,7 +434,7 @@ One product, three names. Use the one that fits the audience:
381
434
  | Layer | Name | When you say it |
382
435
  |-------|------|-----------------|
383
436
  | **Brand** | The 7 Laws of AI Agent Discipline | Tweets, talks, docs, "what is this" |
384
- | **Engine** | Mulahazah | The auto-leveling instinct system inside it |
437
+ | **Engine** | Mulahazah | Arabic for observation. The engine watches every tool call; the confidence ledger keeps the account (suggest at 0.5, apply at 0.7, corrections cut). Taking account of yourself is *muhasabah*, the reason the engine exists: [docs/philosophy.md](docs/philosophy.md) |
385
438
  | **Package** | `continuous-improvement` | `npm install`, `/plugin install`, `settings.json` |
386
439
 
387
440
  Every skill description leads with `Enforces Law N (...)` so the discipline tag shows up the moment the skill is loaded; the lint `verify:skill-law-tag` blocks any skill that drops the tag.
package/SKILL.md CHANGED
@@ -42,6 +42,8 @@ Before executing, state:
42
42
  - Build passes
43
43
  - You can explain what changed in one sentence
44
44
 
45
+ "Done" is an audit, not a hope: settle the actual result against the Verification you stipulated in Law 2.
46
+
45
47
  ## Law 5: Reflect After Every Session
46
48
 
47
49
  After non-trivial tasks:
@@ -59,6 +61,8 @@ After non-trivial tasks:
59
61
 
60
62
  The "Rule to add" field feeds Law 7 — it becomes an instinct with 0.6 starting confidence.
61
63
 
64
+ This block is the ledger: what worked and what failed are the session's gains and losses; the rule to add is what you carry into the session you will not be in.
65
+
62
66
  The "Iteration — Next best recommendations" field feeds Law 6. List the **top 3 ranked** core-development moves based on the current code state — what to build, fix, refactor, or investigate next so the feature/system advances. Item #1 is the strongest recommendation; #2 and #3 are alternative directions the user can pick from. NOT git plumbing (commit, push, PR), NOT pure CI ceremony (run tests, type-check), NOT deploy steps. Those belong in the end-of-run summary, not here.
63
67
 
64
68
  Format per item: `<verb> <object at path:line> (<why, one clause grounded in current context>)`.
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Invariant Count Check
4
+ *
5
+ * `CLAUDE.md` and `AGENTS.md` tell an agent, as a hard instruction, to run
6
+ * `npm run verify:all` and state how many invariants that is plus the full
7
+ * ordered list. `docs/RELEASING.md` states the count in its release checklist.
8
+ * All three are hand-maintained prose, and all three drifted: on 2026-09-06
9
+ * CLAUDE.md and RELEASING.md said 16 against an actual 17, and AGENTS.md — the
10
+ * file Codex reads as its contract — said **12** and listed an incomplete set,
11
+ * having missed skill-count-prose, command-count, landing-version and
12
+ * reconcile-parity entirely.
13
+ *
14
+ * That is worse than a stale number. An agent told "12 invariants" that sees 17
15
+ * OK lines has no way to know whether it ran too much or the doc is wrong, and
16
+ * an agent reading the list as authoritative will not notice an invariant it was
17
+ * never told about.
18
+ *
19
+ * Source of truth: the `verify:all` script in package.json. Everything before
20
+ * the first non-`verify:` step is a content invariant; the rest (today just
21
+ * `typecheck`) is the trailing tail the docs name after the colon. Derived, not
22
+ * hardcoded, so adding an invariant to the chain is the only edit needed to make
23
+ * this check demand the docs follow.
24
+ *
25
+ * Two claim shapes are recognised:
26
+ * long — "(17 content invariants + typecheck: a, b, …, typecheck)" count + ordered names
27
+ * short — "(17 invariants + typecheck)" count only
28
+ *
29
+ * Fail-closed: a scanned file with no claim at all is a violation, not a pass.
30
+ *
31
+ * Usage:
32
+ * node bin/check-invariant-count.mjs # Check the current repo
33
+ * node bin/check-invariant-count.mjs <repo-root> # Check a specific repo root
34
+ *
35
+ * Exit codes:
36
+ * 0 — every claim matches the verify:all chain
37
+ * 1 — a count is stale, a name list is wrong or out of order, or a file states none
38
+ */
39
+ import { readFileSync } from "node:fs";
40
+ import { join } from "node:path";
41
+ import { argv, cwd, exit } from "node:process";
42
+ /** Files that carry a verify:all claim. Explicit, so dropping one is deliberate. */
43
+ export const CLAIM_FILES = ["CLAUDE.md", "AGENTS.md", join("docs", "RELEASING.md")];
44
+ const LONG_RE = /\((\d+) content invariants \+ typecheck:\s*([^)]+)\)/g;
45
+ const SHORT_RE = /\((\d+) invariants \+ typecheck\)/g;
46
+ /** Split the verify:all chain into `verify:*` invariants and the trailing steps. */
47
+ export function parseVerifyAllChain(chainScript) {
48
+ const steps = [...chainScript.matchAll(/npm run ([A-Za-z0-9:_-]+)/g)].map((m) => m[1]);
49
+ const invariants = [];
50
+ const trailing = [];
51
+ for (const step of steps) {
52
+ if (step.startsWith("verify:"))
53
+ invariants.push(step.slice("verify:".length));
54
+ else
55
+ trailing.push(step);
56
+ }
57
+ return { invariants, trailing };
58
+ }
59
+ export function parseClaims(content) {
60
+ const claims = [];
61
+ for (const m of content.matchAll(LONG_RE)) {
62
+ claims.push({
63
+ count: Number(m[1]),
64
+ names: m[2].split(",").map((s) => s.trim()).filter(Boolean),
65
+ });
66
+ }
67
+ for (const m of content.matchAll(SHORT_RE)) {
68
+ claims.push({ count: Number(m[1]), names: null });
69
+ }
70
+ return claims;
71
+ }
72
+ export function findViolations(chain, claimsByFile) {
73
+ const violations = [];
74
+ const expectedCount = chain.invariants.length;
75
+ const expectedNames = [...chain.invariants, ...chain.trailing];
76
+ for (const [file, claims] of Object.entries(claimsByFile)) {
77
+ if (claims.length === 0) {
78
+ violations.push(`${file}: states no "verify:all" invariant-count claim. Expected "(${expectedCount} invariants + typecheck)" or the long form with the full list.`);
79
+ continue;
80
+ }
81
+ for (const claim of claims) {
82
+ if (claim.count !== expectedCount) {
83
+ violations.push(`${file}: claims ${claim.count} invariants but verify:all runs ${expectedCount}.`);
84
+ }
85
+ if (claim.names === null)
86
+ continue;
87
+ if (claim.names.length !== expectedNames.length) {
88
+ const missing = expectedNames.filter((n) => !claim.names.includes(n));
89
+ const extra = claim.names.filter((n) => !expectedNames.includes(n));
90
+ violations.push(`${file}: the listed steps do not match verify:all.` +
91
+ (missing.length ? ` Missing: ${missing.join(", ")}.` : "") +
92
+ (extra.length ? ` Not in the chain: ${extra.join(", ")}.` : ""));
93
+ }
94
+ else if (claim.names.some((n, i) => n !== expectedNames[i])) {
95
+ violations.push(`${file}: the listed steps are out of order. Expected the chain order: ${expectedNames.join(", ")}.`);
96
+ }
97
+ }
98
+ }
99
+ return violations;
100
+ }
101
+ export function checkInvariantCount(repoRoot) {
102
+ const pkgRaw = readFileSync(join(repoRoot, "package.json"), "utf8");
103
+ const pkg = JSON.parse(pkgRaw);
104
+ const chainScript = pkg.scripts?.["verify:all"];
105
+ if (typeof chainScript !== "string" || chainScript.length === 0) {
106
+ return {
107
+ chain: { invariants: [], trailing: [] },
108
+ violations: [`package.json has no "verify:all" script, so no claim can be checked.`],
109
+ };
110
+ }
111
+ const chain = parseVerifyAllChain(chainScript);
112
+ const claimsByFile = {};
113
+ for (const rel of CLAIM_FILES) {
114
+ let content;
115
+ try {
116
+ content = readFileSync(join(repoRoot, rel), "utf8");
117
+ }
118
+ catch {
119
+ claimsByFile[rel] = [];
120
+ continue;
121
+ }
122
+ claimsByFile[rel] = parseClaims(content);
123
+ }
124
+ return { chain, violations: findViolations(chain, claimsByFile) };
125
+ }
126
+ function main() {
127
+ const repoRoot = argv[2] ?? cwd();
128
+ const { chain, violations } = checkInvariantCount(repoRoot);
129
+ if (violations.length === 0) {
130
+ console.log(`OK invariant-count: all ${CLAIM_FILES.length} doc surface(s) state ${chain.invariants.length} invariants + ${chain.trailing.join(", ")}, matching the verify:all chain.`);
131
+ exit(0);
132
+ }
133
+ console.error(`FAIL invariant-count: ${violations.length} stale claim(s) about verify:all.\n`);
134
+ for (const v of violations)
135
+ console.error(` ${v}`);
136
+ console.error(`\nFix: verify:all currently runs ${chain.invariants.length} invariants — ` +
137
+ `${[...chain.invariants, ...chain.trailing].join(", ")}. ` +
138
+ `Update the claim in each file above to match. The chain in package.json is the source of truth.`);
139
+ exit(1);
140
+ }
141
+ const invokedDirectly = argv[1] !== undefined && import.meta.url.endsWith(argv[1].replace(/\\/g, "/"));
142
+ if (invokedDirectly || argv[1]?.endsWith("check-invariant-count.mjs")) {
143
+ main();
144
+ }