@massu/core 2.4.0 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +126 -0
- package/commands/massu-bearings.md +1 -1
- package/commands/massu-recap.md +16 -5
- package/dist/cli.js +291 -80
- package/dist/hooks/auto-learning-pipeline.js +53 -9
- package/dist/hooks/classify-failure.js +55 -11
- package/dist/hooks/cost-tracker.js +23 -9
- package/dist/hooks/fix-detector.js +54 -9
- package/dist/hooks/incident-pipeline.js +53 -9
- package/dist/hooks/intent-suggester.js +53 -9
- package/dist/hooks/memory-recall.js +53 -9
- package/dist/hooks/post-edit-context.js +53 -9
- package/dist/hooks/post-tool-use.js +19 -6
- package/dist/hooks/pre-compact.js +19 -6
- package/dist/hooks/pre-delete-check.js +53 -9
- package/dist/hooks/pre-tool-use-gate.js +57 -11
- package/dist/hooks/quality-event.js +23 -9
- package/dist/hooks/rule-enforcement-pipeline.js +53 -9
- package/dist/hooks/security-gate.js +53 -9
- package/dist/hooks/session-end.js +19 -6
- package/dist/hooks/session-start.js +120 -37
- package/dist/hooks/user-prompt.js +19 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,132 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
10
10
|
> redistributed under the **Apache License 2.0**. Full license + NOTICE:
|
|
11
11
|
> `packages/core/assets/embedder/MODEL-LICENSE`.
|
|
12
12
|
|
|
13
|
+
## [2.5.1] - 2026-08-09
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **Hooks emitted an output field that does not exist, so no hook message ever reached
|
|
18
|
+
Claude.** Eleven hooks wrote `{"message": …}` to stdout. `message` is not in Claude Code's
|
|
19
|
+
hook output schema at all — it appears only in `Notification`/`FileChanged` *input* — so
|
|
20
|
+
every advisory string those hooks produced was discarded. The failure was self-inflicted in
|
|
21
|
+
an exact way: because `{"message": …}` is valid JSON, `UserPromptSubmit` parsed it as JSON
|
|
22
|
+
instead of treating it as plain text, so the one event that would have delivered the string
|
|
23
|
+
as stdout is precisely the event that dropped it. A slightly malformed string would have
|
|
24
|
+
arrived.
|
|
25
|
+
|
|
26
|
+
Replaced by a single chokepoint emitting the documented shape,
|
|
27
|
+
`{"hookSpecificOutput": {"hookEventName", "additionalContext"}}`. `hookEventName` is
|
|
28
|
+
REQUIRED, so the helper takes the event as a typed, required argument and the old
|
|
29
|
+
one-argument form is DELETED rather than deprecated — a regression to the silent shape is
|
|
30
|
+
now a compile error, not a silent no-op. An unrecognised event emits nothing and says why
|
|
31
|
+
on stderr. Output is truncated at the spec's 10,000-char cap by us rather than by the
|
|
32
|
+
runtime.
|
|
33
|
+
|
|
34
|
+
**Blocking behaviour is unchanged.** The `PreToolUse` gates deny via `process.exit(2)` plus
|
|
35
|
+
stderr, which never depended on this path; what was lost was their *advisory* (warn-level)
|
|
36
|
+
messages, and everything `rule-enforcement-pipeline` emits, since that hook only ever
|
|
37
|
+
advises.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- **`scripts/ops/retag-release-to-npm-githead.sh`** — audits every release tag against the
|
|
42
|
+
commit npm actually published. A release tag is supposed to answer "which commit shipped as
|
|
43
|
+
X.Y.Z", and two things break that: the tag is created before the post-publish bookkeeping
|
|
44
|
+
commit and then moved forward, or it is never created at all. npm records `gitHead` with
|
|
45
|
+
every publish, so the answer is recoverable — this reads THAT and refuses anything else.
|
|
46
|
+
|
|
47
|
+
Dry-run by default; reports its denominator; FAILS CLOSED when the registry returns no
|
|
48
|
+
usable `gitHead` or names a commit absent from the repo, rather than guessing; never
|
|
49
|
+
force-pushes — it moves the local tag and prints the push command, so publishing a
|
|
50
|
+
rewritten ref stays deliberate.
|
|
51
|
+
|
|
52
|
+
First full audit: `50 tag(s) examined; 6 already correct, 34 drifted, 10 error(s)`. The 2.x
|
|
53
|
+
series is now 7/7 correct; 1.x is left as-is deliberately.
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
|
|
57
|
+
- **The documented Stage-D release ceremony had no legal ending.** It ordered
|
|
58
|
+
`bump -> tag -> publish -> .mcp.json pin refresh`, but the pin refresh is a COMMIT, so
|
|
59
|
+
following it always leaves commits on top of a published-and-tagged release — exactly what
|
|
60
|
+
CR-64 refuses. Measured during the 2.5.0 release: gate `[11/22]` returned
|
|
61
|
+
`versionIsPublished:true, tagSha 90c82344, headSha a74648ae`.
|
|
62
|
+
|
|
63
|
+
Corrected to **pin -> publish -> tag**, so the published tree, npm's `gitHead`, the tag and
|
|
64
|
+
HEAD are one commit by construction. Fixed at both sites that state the order. The repo's
|
|
65
|
+
own history already worked this way — `v2.3.1`, `v2.3.0` and `v2.1.0` each tag their pin
|
|
66
|
+
commit — so the prose was the outlier, not the practice.
|
|
67
|
+
|
|
68
|
+
- **The same plan predicted a release does not touch `website/`.** The CHANGELOG is mirrored
|
|
69
|
+
into `website/`, so every release does; gate `[9/22]` failed on a 29h deploy lag. Replaced
|
|
70
|
+
the prediction with the measured fact and made `/massu-deploy` an explicit ceremony step.
|
|
71
|
+
|
|
72
|
+
Incident: `docs/incidents/2026-08-01-release-ceremony-had-no-legal-ending.md`
|
|
73
|
+
|
|
74
|
+
## [2.5.0] - 2026-08-01
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
|
|
78
|
+
- **`install-hooks` ADDED a hook registration instead of REPLACING it, so every version bump
|
|
79
|
+
doubled the hooks — and that defect BLOCKED ITS OWN REMEDY.** `mergeHookEntries` deduped by
|
|
80
|
+
EXACT COMMAND STRING, and the emitted command embeds the version, so
|
|
81
|
+
`npx -y @massu/core@1.16.2 hook-runner user-prompt` and the `@2.4.0` form were two different
|
|
82
|
+
strings, two different hooks, BOTH kept. The command was simultaneously the payload and the
|
|
83
|
+
identity, so any payload change forged a new identity.
|
|
84
|
+
|
|
85
|
+
Measured, not theoretical: one workspace carried `1.15.5` x16 **and** `1.15.2` x15 at once,
|
|
86
|
+
with 15 hook names firing TWICE per event and writing double telemetry indefinitely. Worse,
|
|
87
|
+
because upgrading duplicated, nobody upgraded — 9 of 10 workspaces on this machine sat on a
|
|
88
|
+
pre-2.0.0 version whose `better-sqlite3` ABI did not match the running Node, and one had
|
|
89
|
+
**26,119 `NODE_MODULE_VERSION` hook failures**: a total massu outage. It stayed invisible
|
|
90
|
+
because a crashing hook still **exits 0**.
|
|
91
|
+
|
|
92
|
+
Registration identity is now the `hook-runner` subcommand — a stable CLI contract (`cli.ts`
|
|
93
|
+
dispatches on exactly that token), invariant under both a version bump and a launch-mechanism
|
|
94
|
+
change. Non-massu entries keep the historical exact-command semantics, so a customer's own
|
|
95
|
+
hooks are never touched or collapsed.
|
|
96
|
+
|
|
97
|
+
Proof it can fail (CR-72, planted in the real tree, restored byte-identical by sha256):
|
|
98
|
+
reverting the identity branch to exact-command dedup turns
|
|
99
|
+
`installer-registration-identity-drift-guard.test.ts` RED — that guard drives the REAL
|
|
100
|
+
`installHooks()` over a real temp project seeded with an older-version block, a node-direct
|
|
101
|
+
block and a customer hook.
|
|
102
|
+
|
|
103
|
+
### Added
|
|
104
|
+
|
|
105
|
+
- **A version-stable launcher shim, cutting ~0.86s of npm bootstrap off each hook fire.**
|
|
106
|
+
`npx -y @massu/core@<v> …` spends ~0.86s starting npm before any massu code runs. Measured
|
|
107
|
+
interleaved, min-of-5: `npx` 0.97s, `npx --offline` 0.88s, `node <cli.js>` 0.11s — so the
|
|
108
|
+
cost is npm's own startup, NOT the registry, and no npx flag addresses it.
|
|
109
|
+
|
|
110
|
+
`install-hooks` now writes `~/.massu/bin/massu-hook` and emits
|
|
111
|
+
`"~/.massu/bin/massu-hook" <version> hook-runner <name>`. That path is VERSION-STABLE, and
|
|
112
|
+
the shim re-resolves the runtime AT FIRE TIME, exec'ing npx when it is absent.
|
|
113
|
+
|
|
114
|
+
This deliberately does NOT bake a versioned absolute path, which would reintroduce the class
|
|
115
|
+
P-003 (v1.9.4) eliminated: such a path is "invalidated by cache clears, global-install
|
|
116
|
+
relocations, or npx upgrades — silently 404-ing the hooks". Probing at emit time says nothing
|
|
117
|
+
about invalidation after emit. Three fail-safe layers instead — shim missing at emit emits
|
|
118
|
+
npx; runtime missing at fire execs npx; both present takes the fast path — so no single
|
|
119
|
+
missing artifact is fatal and a stale registration self-heals.
|
|
120
|
+
|
|
121
|
+
Measured after: fast path **0.11s vs npx 1.07s (9.7x)**. The fallback is mutation-proven — a
|
|
122
|
+
shim with its npx branch removed FAILS the same no-runtime case (exit 27), so the test is not
|
|
123
|
+
vacuous. `exec` in both branches preserves stdin, stdout and the exit code.
|
|
124
|
+
|
|
125
|
+
Semver MINOR: `massuHookIdentity`, `massuShimPath`, `massuShimBody`, `installMassuShim`,
|
|
126
|
+
`resolveMassuShim`, `massuRuntimeDir`, `massuRuntimeCliPath`, `resolveMassuRuntimeCli` and
|
|
127
|
+
`materializeMassuRuntime` are new exports. `MASSU_NO_NODE_DIRECT=1` forces the npx form.
|
|
128
|
+
|
|
129
|
+
### Changed
|
|
130
|
+
|
|
131
|
+
- **CR-70 Layer 3 amended: installer symmetry is no longer npx-specific.** Both emitters read
|
|
132
|
+
one resolver and agree on mechanism AND pinned version. The drift-guard previously asserted
|
|
133
|
+
the literal `'npx'`, which pinned an implementation detail rather than the property — it
|
|
134
|
+
would go RED on a correct mechanism change and GREEN on a wrong one that used npx on both
|
|
135
|
+
sides. Three assertions were amended on the same grounds
|
|
136
|
+
(`init-hook-paths-no-absolute`, `init-mcp-json-pin`, `cli.test.ts`); each keeps its original
|
|
137
|
+
protection of a visible pinned version and a known launch mechanism.
|
|
138
|
+
|
|
13
139
|
## [2.4.0] - 2026-07-31
|
|
14
140
|
|
|
15
141
|
### Fixed
|
|
@@ -41,7 +41,7 @@ Read these in parallel where possible:
|
|
|
41
41
|
| 10 | `.claude/metrics/command-invocations.jsonl` | Command usage frequency — top/least used in last 7 days |
|
|
42
42
|
| 11 | `.claude/metrics/bearings-history.jsonl` | Previous bearings runs — show trends |
|
|
43
43
|
| 12 | `.claude/commands/` (ls -d */`) | Detect folder-based skills (directories vs flat files) |
|
|
44
|
-
| 13 |
|
|
44
|
+
| 13 | `~/.claude/bin/memory-contradiction-check . --baseline .claude/memory-contradiction-baseline.json` | Unresolved pairs + denominator; exit 2 = could-not-look, a FINDING |
|
|
45
45
|
| 14 | `massu_cost_session` + `massu_cost_trend` MCP tools | Token spend this session, cost trend over time |
|
|
46
46
|
| 15 | `session-state/deferred-verifications.md` | Pending production verifications from previous deploys |
|
|
47
47
|
|
package/commands/massu-recap.md
CHANGED
|
@@ -104,12 +104,15 @@ If no significant work: skip this section.
|
|
|
104
104
|
Run the memory contradiction scanner:
|
|
105
105
|
|
|
106
106
|
```bash
|
|
107
|
-
|
|
107
|
+
~/.claude/bin/memory-contradiction-check . --baseline .claude/memory-contradiction-baseline.json
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
- **
|
|
110
|
+
Read the EXIT CODE, and read the denominator it prints — a
|
|
111
|
+
verdict with no `memory files read` line means it aborted before reporting:
|
|
112
|
+
- **exit 0** — no unresolved contradictions. Quote `unresolved pairs : 0` as the evidence.
|
|
113
|
+
- **exit 1** — unresolved pairs, or a STALE baseline entry. Display each to the user:
|
|
114
|
+
- **exit 2** — it COULD NOT LOOK (missing corpus, unreadable file, bad baseline). This is
|
|
115
|
+
a FINDING, never a clean run. Report it as such and do not proceed as though clean.
|
|
113
116
|
|
|
114
117
|
```
|
|
115
118
|
MEMORY HEALTH
|
|
@@ -122,7 +125,15 @@ MEMORY HEALTH
|
|
|
122
125
|
|
|
123
126
|
**Do NOT auto-resolve contradictions.** Surface them so the user can decide which memory is stale.
|
|
124
127
|
|
|
125
|
-
If the
|
|
128
|
+
**If the checker is missing, report that as a FAILED check — never as a benign note.**
|
|
129
|
+
This step was unrunnable in this repo from the day it was written until 2026-08-02: it
|
|
130
|
+
named `scripts/memory-contradiction-check.sh`, which never existed here (0 tracked
|
|
131
|
+
matches, 0 commits ever touching the path — it was copied from another repo's protocol).
|
|
132
|
+
The instruction to "note it as unavailable" is what let a mandatory check report nothing
|
|
133
|
+
for months, because "could not look" and "looked and found nothing" read identically.
|
|
134
|
+
The checker is now version-controlled at `~/.claude/bin/memory-contradiction-check`, has a
|
|
135
|
+
caller in `npm test` (`memory-contradiction-check-caller.test.ts`), a fixture suite, and a
|
|
136
|
+
live-fire attack — so its absence is itself a red test.
|
|
126
137
|
|
|
127
138
|
### Step 3: Update Session State
|
|
128
139
|
|