mindforge-cc 11.9.3 → 11.9.5

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "11.9.3",
2
+ "version": "11.9.5",
3
3
  "environment": "development",
4
4
  "governance": {
5
5
  "drift_threshold": 0.75,
@@ -47,7 +47,14 @@ Typical GitHub Action setup:
47
47
  steps:
48
48
  - uses: actions/checkout@v4
49
49
  - name: Run MindForge headless
50
- run: npx mindforge headless --phase 3
50
+ # `npx --package=mindforge-cc` PINS the package. Bare `npx mindforge` resolves to an UNRELATED
51
+ # third-party package on the public registry (mindforge@1.0.21, maintainer william@mindforge.ai) —
52
+ # not this project, whose npm name is mindforge-cc. In a fresh runner there is no local bin to
53
+ # shadow it, so npx fetches and unpacks that package, runs any install scripts it has, and then
54
+ # fails with "could not determine executable to run" because it exposes no `mindforge` bin.
55
+ # Measured both forms: the pinned one exits 0 with mindforge-cc in the npx cache; the bare one
56
+ # exits 1 after downloading someone else's package into a job holding MINDFORGE_TOKEN.
57
+ run: npx --yes --package=mindforge-cc mindforge headless --phase 3
51
58
  env:
52
59
  MINDFORGE_TOKEN: ${{ secrets.MINDFORGE_TOKEN }}
53
60
  AUTO_PUSH_ON_WAVE_COMPLETE: true
package/CHANGELOG.md CHANGED
@@ -1,5 +1,285 @@
1
1
  # Changelog
2
2
 
3
+ ## [11.9.5] — 2026-08-22 — The release path can no longer strand itself, and the SDK ships
4
+
5
+ Patch release, and the shortest one in a while. It exists because 11.9.4 published two
6
+ packages and then failed on the third, and that failure took the release page and the
7
+ `stable` dist-tag with it. Both causes are fixed here, and one of them is the reason this
8
+ release is worth cutting rather than waiting: **`mindforge-sdk` publishes for the first
9
+ time in seven versions, with provenance.**
10
+
11
+ ### Fixed
12
+
13
+ **A publish that cannot finish the release it started**
14
+
15
+ - **`sdk/package.json` had no `repository` field.** npm's provenance verification compares
16
+ that field against the attestation **server-side, at PUT** — so the publish was rejected
17
+ with `422 Unprocessable Entity … "repository.url" is ""` *after* `mindforge-cc` and
18
+ `mindforge-mcp-server` had already published irreversibly. `npm publish --dry-run` does
19
+ not perform that comparison, and nothing in this repository read `repository` at all: all
20
+ six preflight gates passed on a manifest the registry was guaranteed to reject. The field
21
+ is added, matching the spelling the other two packages use.
22
+
23
+ - **`scripts/ci/verify-provenance-metadata.js` makes that failure reachable before the
24
+ point of no return.** It runs in the release **preflight** job, ahead of every publish,
25
+ and it does not carry a list of packages: it discovers them from the workflow — every
26
+ step whose `run` contains both `npm publish` and `--provenance`, resolved through its
27
+ `working-directory` — so a fourth package is covered without editing the gate.
28
+ Discovering zero targets is a hard failure rather than a pass, on the principle that a
29
+ check which examined nothing must not report success.
30
+
31
+ Verified the only way that means anything: run against a git worktree at tag `v11.9.4` —
32
+ the exact tree that the registry rejected — it exits 1 and names `sdk/package.json` and
33
+ the missing field. It would have stopped that release before anything reached npm.
34
+
35
+ - **The release steps were ordered so that an optional package could strand the release.**
36
+ The SDK publish sat *before* `Create GitHub Release` and the `stable` dist-tag move.
37
+ A failing step fails the job, so both were skipped — which is precisely the harm the
38
+ placement comment claimed to prevent. The same mode had already fired on **v11.5.1** and
39
+ **v11.8.3** from the MCP publish; v11.9.4 was its third occurrence.
40
+
41
+ Reordered to: `Publish to npm` → `Create GitHub Release` → `stable` dist-tag →
42
+ `mcp-server` → `sdk`. The rule now encoded in `tests/action-pinning.test.js`: **no step
43
+ whose failure leaves no residue may be able to skip a step that finishes an irreversible
44
+ one.** That test previously asserted the opposite and had to be inverted in the same
45
+ commit — because `Run Full Test Suite` is the release job's first step, a reorder shipped
46
+ alone would have failed the release at its own gate.
47
+
48
+ The two finishers carry guards on their own inputs rather than a bare `!cancelled()`,
49
+ which would publish a release with a 0-byte body and an unmatched `.tgz` glob when
50
+ `Build Package` fails — the `bodyBytes=0` defect already seen on v11.9.0, .1 and .2. And
51
+ deliberately not `continue-on-error`: a tolerated failure makes the run conclude
52
+ **green**, which is the blindness that let the SDK sit seven versions behind, unattested,
53
+ with nothing noticing.
54
+
55
+ **Prose surfaces that shipped stale twice running**
56
+
57
+ - `bin/utils/readiness-gate.js` scored `RELEASENOTES.md` on `fileExists` while its
58
+ changelog sibling three lines away checked that the version appeared *in* the file. That
59
+ asymmetry is why the immutable 11.9.3 tarball's README said "Latest release v11.9.2" and
60
+ the immutable 11.9.4 tarball shipped with no 11.9.4 entry at all — while `README.md`
61
+ offers that file as the human-readable route to the BREAKING notes. Both are now checked,
62
+ anchored rather than by substring (`includes('11.9.4')` is also satisfied by
63
+ `## v11.9.40`).
64
+
65
+ **Policy change, stated plainly:** every release from here needs a `RELEASENOTES.md`
66
+ entry. Six published 11.x versions do not have one, so that file has been curated rather
67
+ than exhaustive; the gate only ever looks for the version being released, so those gaps
68
+ are unaffected.
69
+
70
+ - README's `## Latest release` is deliberately gated by a **test** rather than written by
71
+ `sync-version.js`. Auto-bumping the version token onto the previous release's paragraph
72
+ produces the right number attached to the wrong description — a better-disguised
73
+ falsehood than a visibly stale one.
74
+
75
+ - `docs/sdk-reference.md` offered `npx mindforge-cc@stable` as a way to get the SDK
76
+ "as part of the framework". Measured false: the published package declares exactly
77
+ `express` and `sql.js` and ships no `sdk/` directory. Removed.
78
+
79
+ ### Added
80
+
81
+ - **`mindforge-sdk` is published, with provenance** — its first release since 11.8.0 and
82
+ its first ever attested one. This is not cosmetic. Everything fixed in the SDK across
83
+ 11.8.1–11.9.4 reached nobody, including the one that matters most:
84
+ `WebSocketEventStream` scheduled a reconnect with `setTimeout(() => this.connect(), …)`
85
+ and nothing handled the returned promise, so a failed reconnect was an unhandled
86
+ rejection — fatal under Node's default mode, **terminating the caller's process**. Every
87
+ consumer on 11.8.0 still has that. (#191)
88
+
89
+ - `npm run provenance:check` — the gate above, runnable offline.
90
+
91
+ ### Notes for operators
92
+
93
+ - 11.9.4 is complete but was finished by hand: its GitHub Release was created at the
94
+ existing tag from the registry's own tarball (verified against `dist.integrity` and both
95
+ attestation bundles' subject digests), and the `v11.9.4` tag was deliberately **not**
96
+ moved, because two published packages' provenance attests to the commit it names.
97
+ - The `stable` dist-tag lagged at 11.9.3 between the two releases. Because the dist-tag
98
+ step now runs *ahead* of both additive publishes, this release moves it forward even if a
99
+ package publish fails again.
100
+ - Still outstanding, and requiring repository settings rather than code: there is no `v*`
101
+ **tag ruleset** restricting who may create the ref that triggers publishing, and
102
+ `NPM_TOKEN` remains a long-lived repository secret with no GitHub environment in front of
103
+ it. Create the environment *first* — adding `environment:` while none exists publishes
104
+ exactly as before while looking like a gate.
105
+
106
+ ## [11.9.4] — 2026-08-22 — Delivery: the gates register, the tarball matches its tag
107
+
108
+ Patch release. 11.9.3 argued that an instrument must not report success while doing
109
+ nothing. 11.9.4 is what an adversarial audit of the **published** 11.9.3 artifact found
110
+ when that standard was applied to the delivery itself: **enforcement that installed and
111
+ then declined to register, a tarball that could not be reproduced from its own tag, and a
112
+ README that understated what shipped.**
113
+
114
+ Every finding here came from measuring the published package in a confined environment —
115
+ not from reading the repository. That distinction is the whole content of this release.
116
+
117
+ ### BREAKING
118
+
119
+ - **The installer now registers hooks on projects where it previously declined.** If you
120
+ install into a project that has an ancestor directory containing a `.claude`, MindForge
121
+ now writes `.claude/settings.json` (merging append-only into any existing file) instead
122
+ of skipping. On a machine that has ever run Claude Code, `~/.claude` makes that
123
+ essentially every project — so most installs go from **0 registered hooks to 8**. Three
124
+ of them can block a tool call. If you were relying on the installer being inert here,
125
+ it no longer is; `.mindforge/hook-registration.json` records exactly what was written,
126
+ and the previous settings file is backed up under `.mindforge/backups/`. (#224)
127
+
128
+ ### Fixed
129
+
130
+ **Hook registration — the gates shipped installed but inert**
131
+
132
+ - **`register()` skipped whenever any ancestor held a `.claude` directory.** Measured
133
+ against the published 11.9.3 tarball, confined HOME, `~/.claude` as the only ancestor:
134
+ **11 hook scripts installed, 0 registered, no settings file written.** The gates that
135
+ the shipped `CLAUDE.md` calls MANDATORY were copied in and left unreachable. Same
136
+ sandbox on 11.9.4: **8 registered, installer preflight executed 7 of 8 and verified all
137
+ 3 deny-class hooks returning exit 2** before keeping the file. (#224)
138
+
139
+ The reason it printed — *"the harness will read `<ancestor>/.claude/settings.json`, not
140
+ this directory"* — was wrong three separate ways:
141
+
142
+ 1. `~/.claude/settings.json` is the **user tier**, applied in addition to the project
143
+ tier. Its existence carries no information about whether a project file is read, so
144
+ the condition that suppressed the gates was satisfied by an ordinary laptop.
145
+ 2. For a genuine project ancestor the claim is false too — that file is not read
146
+ either. Verified with a natural experiment: an ancestor two levels up carried a
147
+ `PreToolUse` Bash hook appending a marker to a log; across a dozen tool calls with
148
+ the inner directory as the project root, the log was never created. Skipping did not
149
+ deliver the gates elsewhere. It delivered them nowhere.
150
+ 3. The git-boundary guard was **dead code**. `stop` was the git toplevel while the walk
151
+ began at `dirname(projectRoot)`, so when toplevel equalled projectRoot the stop
152
+ condition could never be true and the walk ran to the filesystem root every time.
153
+ The boundary meant to keep the check local is why it reached `$HOME`.
154
+
155
+ It now warns and registers anyway: a registration that turns out inert costs nothing and
156
+ becomes live when the harness is launched there, whereas a skip is guaranteed inert. The
157
+ check additionally requires a real `settings.json` **file** — the old one accepted any
158
+ directory named `.claude`, and the one it hit in practice held only markdown. (#224)
159
+
160
+ - **The installer's only failure-path pointer led nowhere.** It told anyone whose hooks
161
+ were not registered to "see `docs/troubleshooting.md`", where the word *hook* appeared
162
+ **0** times. That file now carries the section the message names, separating "not
163
+ registered" from "registered but not live", and including a copy-paste payload that
164
+ drives a hook directly so a broken hook can be told apart from an unwired one. (#224)
165
+
166
+ **Release artifacts**
167
+
168
+ - **The published tarball could not be reproduced from its tag.**
169
+ `.mindforge/memory/sync-manifest.json` — gitignored, written at runtime by
170
+ `bin/memory/semantic-hub.js` — was **1 of 1979** shipped files not tracked at
171
+ `v11.9.3`, so provenance attested to a tree containing a file the repository does not
172
+ contain. With a `files[]` allowlist, a **directory** entry ships its contents regardless
173
+ of `.gitignore`; `files[]` already carried a negation for `pattern-library.jsonl` for
174
+ exactly this reason, and the manifest's entire content is the sync record *for that
175
+ already-excluded file*. (#225)
176
+
177
+ - **`mindforge-sdk` was NOT published in this release. It remains at 11.8.0.** This entry
178
+ originally claimed it shipped with provenance; that claim was false and is corrected here
179
+ rather than quietly deleted, because a release arguing for measured claims does not get to
180
+ misstate a supply-chain property.
181
+
182
+ What happened: the SDK sat at **11.8.0** on npm while `sync-version.js` kept
183
+ `sdk/package.json` at canonical — seven releases of disagreement (11.8.1 through 11.9.3,
184
+ none published) that nothing detected, because `version:check` verifies the tracked file
185
+ and not what the registry serves. It was also the only one of the three packages with **no
186
+ attestation**. A publish step was added for it, and the registry rejected it:
187
+
188
+ ```
189
+ npm error 422 Unprocessable Entity - PUT https://registry.npmjs.org/mindforge-sdk
190
+ Error verifying sigstore provenance bundle: Failed to validate repository information:
191
+ package.json: "repository.url" is "", expected to match
192
+ "https://github.com/sairam0424/MindForge" from provenance
193
+ ```
194
+
195
+ `sdk/package.json` carried no `repository` field. The other two packages both do, which is
196
+ exactly why they published and it did not. That field is validated **registry-side at PUT**,
197
+ after two irreversible publishes have already succeeded — `npm publish --dry-run` does not
198
+ check it and neither did any gate here. Both are fixed for 11.9.5: the field, and an offline
199
+ preflight gate that refuses to reach a publish without it.
200
+
201
+ The step was also badly placed. It ran **before** `Create GitHub Release` and the `stable`
202
+ dist-tag move, so its failure skipped both — which is the precise harm the placement comment
203
+ claimed to prevent. `mindforge-cc@11.9.4` and `mindforge-mcp-server@11.9.4` published
204
+ correctly with provenance attesting commit 353e8d41; the release page and the `stable` tag
205
+ were completed by hand afterwards. The finishing steps now run ahead of the additive
206
+ publishes, so no optional package can strand a release again.
207
+
208
+ - **The Homebrew formula carries the real 11.9.3 digest.** Verified against an independent
209
+ measurement rather than the tool's own output, and explicitly confirmed not to be the
210
+ SHA-256 of npm's 21-byte `{"error":"Not found"}` body — the constant #203 used to write
211
+ for every unpublished version. (#223)
212
+
213
+ **Honesty about what is enforced**
214
+
215
+ - **The README understated the product.** "What is actually enforced" still declared that
216
+ **no install channel registers hooks**, with a `No / No` table, and stated that the
217
+ plugin channel's dispatcher crashes on every fire. Measured: the plugin's
218
+ `scripts/lib/` exists, all **14** path tokens in `plugins/mindforge/hooks/hooks.json`
219
+ resolve under the plugin root, and driving the dispatcher by hand returns **exit 2** for
220
+ `mindforge-block-no-verify` and `mindforge-config-protection`. A document that
221
+ under-claims a security capability is the same defect as one that over-claims it —
222
+ either way it describes a system that is not the one shipped. Now stated per channel,
223
+ with the four cases that remain deliberately unenforced and the three liveness
224
+ preconditions outside MindForge's control. (#225)
225
+
226
+ **Defects the published-artifact audit found (#222)**
227
+
228
+ - The **v11.9.3 release page shipped empty**: `changelog-fetcher.js --latest` exits 0
229
+ while writing zero bytes, so the `||` fallback never fired and a 195-line changelog
230
+ reached nobody. The step now prefers the in-tree changelog and fails on an empty body.
231
+ - A **shipped CI snippet told users to `npx` a package we do not own**.
232
+ `npx mindforge …` resolves to an unrelated third-party package; in a fresh runner it is
233
+ fetched and unpacked, install scripts and all, inside a job holding `MINDFORGE_TOKEN`.
234
+ Now pinned with `--package=mindforge-cc`.
235
+ - The **version-source gate missed a live defect twice**. `mindforge health` printed
236
+ `RELEASE v0.4.2` and `Current : v11.9.3` twenty-six lines apart in a project declaring
237
+ 0.4.2, and produced `Unexpected end of JSON input` with no manifest at all — the verb
238
+ whose job is verifying installation integrity misreporting the installation. Widening
239
+ the gate surfaced two more of the same shape, one of which bound the **wrong** version
240
+ silently into approval records, which is worse than no binding because the check still
241
+ returns a verdict.
242
+ - `mindforge approve` died with ENOENT on any non-Node project. Absent values are now
243
+ recorded as `null` rather than invented.
244
+
245
+ **Test infrastructure**
246
+
247
+ - `tests/production.test.js` carried the uncommitted diff into its clone with
248
+ `git diff HEAD` (which includes staged files) but committed it with `commit -a` (which
249
+ stages only modified and deleted **tracked** files). So any commit **adding** a file
250
+ under `bin/` or `tests/` left the clone dirty and failed the gate's own cleanliness
251
+ assertion — reintroducing precisely the `--no-verify` pressure that carry exists to
252
+ avoid, for the one case it did not cover.
253
+
254
+ ### Added
255
+
256
+ New regression gates, each falsified by reinstating the exact defect and confirming RED,
257
+ with every touched file restored byte-exact afterwards:
258
+
259
+ - `tests/hook-registration-ancestor.test.js` — six properties, the first of which asserts
260
+ the ancestor detector fires at all, so the other five cannot pass by never triggering.
261
+ - `every shipped file is tracked in git` — property-based, no name list. It cannot fail on
262
+ a clean clone, which is how the real leak survived CI, so it is paired with a second
263
+ test that plants the runtime state in a throwaway clone and then packs.
264
+ - `all three published packages publish with provenance` — counts the `npm publish`
265
+ invocations rather than matching one, because a single-match regex was satisfied by
266
+ either of the two packages that already had an attestation while the SDK had none. Also
267
+ asserts the SDK step is idempotent and ordered after the proven publishes.
268
+
269
+ ### Notes for operators
270
+
271
+ - **These fixes change installer behaviour on almost every project.** After upgrading,
272
+ check `.mindforge/hook-registration.json` to see what was registered, and restart the
273
+ harness — hooks are snapshotted at session start.
274
+ - A registered hook is only *live* if the harness has been restarted, the project is
275
+ trusted in the harness, and `CLAUDE_PROJECT_DIR` is set with `node` on the hook PATH.
276
+ None of those three are in MindForge's control; the last is a deliberate trade against a
277
+ fail-closed shell tail that was measured denying benign commands on a fresh clone.
278
+ - Two gaps remain that require repository settings rather than code, and are recorded
279
+ rather than claimed fixed: there is no `v*` **tag ruleset** restricting who may create
280
+ the ref that triggers publishing, and `NPM_TOKEN` is a long-lived repository secret with
281
+ no GitHub environment in front of it.
282
+
3
283
  ## [11.9.3] — 2026-08-21 — Honesty: gates that can fail, commands that run, a release path that is checked
4
284
 
5
285
  Patch release. No new features. Twenty-one fixes, and they all turned out to be the
@@ -12,6 +292,14 @@ Contains behaviour changes under a patch bump — several of the things being fi
12
292
  bugs that a consumer could have been relying on. Read BREAKING before upgrading if you
13
293
  script against the CLI or the installer.
14
294
 
295
+ > **Corrected after release.** Three measured numbers in this entry were wrong and are fixed above:
296
+ > the count of places `--status`/`--stop` were documented (removed rather than re-guessed — it reads 4,
297
+ > 9 or 15 depending on how you count, which is the argument against stating it); the eslint total, which
298
+ > was 199 on the author's machine and **190** on a clean clone, because 9 problems came from an
299
+ > untracked local directory; and "four releases behind", which is **three** (11.8.3 → 11.9.0 → 11.9.1 →
300
+ > 11.9.2). Found by an adversarial audit of this changelog against the published artifact. A release
301
+ > arguing that measured numbers should be reproducible has to hold its own notes to that standard.
302
+
15
303
  ### BREAKING
16
304
 
17
305
  Each of these is a bug fix whose correct behaviour differs from the shipped behaviour.
@@ -113,15 +401,16 @@ Each of these is a bug fix whose correct behaviour differs from the shipped beha
113
401
  exactly one event — a `v*` tag push — and the repository's only ruleset targets
114
402
  branches, so its six required checks applied to nothing on the path that ships. GitHub
115
403
  cannot attach required status checks to a tag. A `preflight` job now gates it. (#216)
116
- - The `stable` npm dist-tag was moved by hand, or not at all — it sat four releases
117
- behind `latest` (11.8.3 against 11.9.2), so `npm i mindforge-cc@stable` delivered a
404
+ - The `stable` npm dist-tag was moved by hand, or not at all — it sat three releases
405
+ behind `latest` (11.8.3 against 11.9.2, via 11.9.0 and 11.9.1), so `npm i mindforge-cc@stable` delivered a
118
406
  build with none of the 11.9.x fixes. The release workflow now moves it as its final
119
407
  step: forward-only, prereleases skipped, and verified against npm's uncached dist-tags
120
408
  endpoint rather than the CDN-cached packument. (#216)
121
409
 
122
410
  **Dashboard**
123
411
 
124
- - `--status` and `--stop` were documented in nine places and implemented in none; both
412
+ - `--status` and `--stop` were documented across the harness roots and the docs and implemented
413
+ nowhere; both
125
414
  printed nothing and exited 0. Now implemented, before `express` is required, so they
126
415
  work without the dependency installed. (#206)
127
416
  - `--stop` identified the target by the SHAPE of its command line, which matched any
@@ -141,8 +430,8 @@ Each of these is a bug fix whose correct behaviour differs from the shipped beha
141
430
  **Verification**
142
431
 
143
432
  - `mindforge verify`'s lint stage used `--max-warnings=0`, which made it impossible to
144
- pass in the repository it ships from: `npx eslint .` reports 199 problems / 0 errors /
145
- 199 warnings, so `verify` reported a lint FAILURE on a tree that is green by the
433
+ pass in the repository it ships from: on a clean clone `npx eslint .` reports 190 problems /
434
+ 0 errors / 190 warnings, so `verify` reported a lint FAILURE on a tree that is green by the
146
435
  project's own contract. Aligned with the project's definition; errors still fail. (#204)
147
436
  - `temporal cleanup` printed "🧹 Cleaning up old temporal snapshots..." and
148
437
  "✅ Cleanup complete." with no cleanup between them. Now wired to
package/MINDFORGE.md CHANGED
@@ -1,9 +1,9 @@
1
- # MINDFORGE.md — Parameter Registry (v11.9.3)
1
+ # MINDFORGE.md — Parameter Registry (v11.9.5)
2
2
 
3
3
  ## 1. IDENTITY & VERSIONING
4
4
 
5
5
  [NAME] = MindForge
6
- [VERSION] = 11.9.3
6
+ [VERSION] = 11.9.5
7
7
  [STABLE] = true
8
8
  [MODE] = "Platform Sovereign"
9
9
  [REQUIRED_CORE_VERSION] = 11.9.1
package/README.md CHANGED
@@ -6,11 +6,24 @@
6
6
 
7
7
  ## Latest release
8
8
 
9
- **v11.9.2** (2026-08-16) — Correctness: the config gate can now fail, the audit chain no longer
10
- forks under concurrent writers, trace retrieval works, and the cost ledger has one record shape.
11
- **Contains a breaking change to the dashboard HTTP surface** (loopback-only) see the BREAKING
12
- section in [CHANGELOG.md](./CHANGELOG.md), or [RELEASENOTES.md](./RELEASENOTES.md) for
13
- human-readable notes.
9
+ **v11.9.5** (2026-08-22) — The release path can no longer strand itself, and the SDK ships.
10
+ 11.9.4 published two packages and then failed on the third; because that step sat *before* the
11
+ release page and the `stable` dist-tag move, its failure skipped both. Fixed two ways: the steps
12
+ that finish a release now run ahead of any additive package publish, and a new offline preflight
13
+ gate refuses to reach a publish that the registry will reject. Verified against a worktree at tag
14
+ `v11.9.4` — the exact tree npm rejected — the gate exits 1 and names the file.
15
+
16
+ **`mindforge-sdk` publishes for the first time since 11.8.0, and for the first time with
17
+ provenance.** Everything fixed in it across 11.8.1–11.9.4 had reached nobody, including a
18
+ `WebSocketEventStream` reconnect whose unhandled rejection **terminates the caller's process**.
19
+
20
+ The previous release, **v11.9.4**, is where the hook gates started actually registering: 11.9.3
21
+ shipped the code and then declined to run it on essentially every project. Measured against the
22
+ published tarballs — 11.9.3: **11 hook scripts installed, 0 registered**; 11.9.4: **8 registered,
23
+ 3 deny-class verified blocking**. That **behaviour change under a patch bump** still applies — the
24
+ installer writes `.claude/settings.json` where it previously declined, merging append-only and
25
+ backing up first. See the BREAKING section in [CHANGELOG.md](./CHANGELOG.md), or
26
+ [RELEASENOTES.md](./RELEASENOTES.md) for human-readable notes.
14
27
 
15
28
  ---
16
29
 
@@ -19,7 +32,9 @@ human-readable notes.
19
32
  Read this before the install instructions. MindForge ships a large corpus of agent
20
33
  instructions — commands, skills, personas, protocols — and those are advisory: they work by
21
34
  being in the model's context, and a model can decline them. The parts that would *block* an
22
- action are hooks, and **no install channel currently registers them.**
35
+ action are hooks. Through 11.9.2 **no channel registered them.** 11.9.3 added the registration code
36
+ but it declined to run on almost every project, so in practice nothing was enforced there either.
37
+ **As of 11.9.4** both channels register and execute them **on Claude Code**, and nowhere else.
23
38
 
24
39
  | Capability | Plugin channel | `npx` channel |
25
40
  |---|---|---|
@@ -27,34 +42,48 @@ action are hooks, and **no install channel currently registers them.**
27
42
  | Skills / personas / protocol docs | Yes | Yes |
28
43
  | Subagents | Yes | Yes |
29
44
  | Audit hash-chain (`bin/verify-audit.js`) | Yes | Yes |
30
- | **Hooks enforced (can block a tool call)** | **No** | **No** |
31
-
32
- Why, specifically:
33
-
34
- - **No hook configuration ships, and nothing generates one.** `package.json` `files[]` has 48
35
- entries and none contains `settings`, so neither `.claude/settings.json` nor
36
- `.agent/settings.json` is published. All references to those paths in `bin/` are reads or
37
- metadata strings there is no code that writes or merges one. `bin/harness-audit.js:335`
38
- even offers "wire trust-gate + block-no-verify into both …" as a *fix suggestion*, auditing a
39
- wiring nothing creates.
40
- - **The plugin channel's hooks additionally crash when fired.**
41
- `plugins/mindforge/scripts/run-with-flags.js:24` requires `./lib/hook-flags`, and
42
- `plugins/mindforge/scripts/lib/` does not exist in the published plugin. Running the
43
- dispatcher gives `Error: Cannot find module './lib/hook-flags'` and exit 1. The module it
44
- needs does exist at `.agent/hooks/lib/hook-flags.js`; it was never copied in.
45
-
46
- So treat MindForge as **governance-by-convention plus a tamper-evident audit log**, not as a
47
- policy enforcement point. Installing it also expands your repository's trust boundary by a large
48
- volume of agent instructions review what you install. Making hook registration real per
49
- harness is the headline goal of v12; the audit chain is genuinely verifiable today
45
+ | **Hooks enforced (can block a tool call)** | **Claude Code only** | **Claude Code + `--local` only** |
46
+
47
+ What that means, measured rather than asserted:
48
+
49
+ - **The `npx` channel generates the config it never used to ship.** `files[]` has 49 entries and
50
+ none of them contains `settings`, so no settings file is *published* — instead
51
+ `bin/installer/hook-registration.js` writes one at install time, merging append-only into any
52
+ file you already have. Measured on a confined install: **8 hooks registered** into
53
+ `.claude/settings.json`, of which the installer's own preflight **executed 7 and verified all 3
54
+ deny-class hooks returning exit 2** before keeping the file. A preflight failure rolls the
55
+ registration back rather than leaving a config whose commands do not run.
56
+ - **The plugin channel's dispatcher runs.** It previously crashed on every fire —
57
+ `run-with-flags.js` requires `./lib/hook-flags` and `plugins/mindforge/scripts/lib/` was not
58
+ copied in. That directory now exists, all **14 path tokens** in
59
+ `plugins/mindforge/hooks/hooks.json` resolve under the plugin root, and driving the dispatcher by
60
+ hand returns **exit 2** for `mindforge-block-no-verify` and `mindforge-config-protection`.
61
+
62
+ Still **not** enforced, deliberately and with a printed reason for each: any runtime other than
63
+ Claude Code (Cursor, Copilot, Gemini/Antigravity, OpenCode), `--global` scope, a self-install
64
+ inside a MindForge checkout, and Windows. Writing a Claude-schema config into `.cursor/` without an
65
+ execution-verified hook contract would be decorative. Every outcome, including "not registered", is
66
+ printed by the installer and written to `.mindforge/hook-registration.json`.
67
+
68
+ Three things gate whether a registered hook is *live*, none of them in MindForge's control: the
69
+ harness must be **restarted** (hooks are snapshotted at session start), the project must be
70
+ **trusted** in the harness, and `CLAUDE_PROJECT_DIR` must be set with `node` on the hook PATH —
71
+ if it is not, the commands exit 1 and the gate is simply absent, which is a deliberate trade
72
+ against a fail-closed tail that was measured denying benign commands on a fresh clone. See
73
+ *Hooks are installed but nothing is blocked* in `docs/troubleshooting.md`.
74
+
75
+ So: on Claude Code, treat MindForge as a policy enforcement point for the 8 registered hooks plus
76
+ a tamper-evident audit log; on every other harness, as **governance-by-convention** plus that same
77
+ audit log. Installing it also expands your repository's trust boundary by a large volume of agent
78
+ instructions — review what you install. The audit chain is verifiable today
50
79
  (`node bin/verify-audit.js`).
51
80
 
52
81
  ---
53
82
 
54
83
  ## Install
55
84
 
56
- Claude Code plugin marketplace (no project files written). **Note:** the plugin's hooks do not
57
- fire — see *What is actually enforced* above. Slash commands, skills and subagents do work.
85
+ Claude Code plugin marketplace (no project files written). The plugin's hooks now fire — see
86
+ *What is actually enforced* above for what that does and does not cover.
58
87
 
59
88
  ```bash
60
89
  /plugin marketplace add sairam0424/MindForge