Repository navigation
Commit 1f5baf6
committed
fix(hooks-detailed-spec): align io.minimax.mcode schema with @minimax-ai/code@0.3.10 runtime
Updates the PR #20 companion to record the hook shape the 0.3.10
runtime actually accepts. The previous companion's flat shape
(`{command, args, matcher, timeout}` at the event level with no
`hooks[]` wrapper) is silently skipped by the 0.3.10 parser with
the warning "hooks.json matcher entry is missing a hooks[] array,
skipping". Every Plugin written against the previous shape
(including mcode-island v0.3.0) would receive zero deliveries in
0.3.10 even when the event name is in the Fwe allowlist.
The new spec describes what the runtime actually parses:
- outer (matcher) entry: {matcher, hooks[]}
- inner (command) descriptor: {type, command, timeout}
- `type` is the only consumed handler-kind discriminator and
must be "command" (the only kind the parser dispatches in
0.3.10)
- `command` is a single shell-executed string; `args[]` is not
consumed by the parser and is rejected by the closed-schema
validator so a Plugin migrating from 0.2.4 gets a clear error
rather than a silent no-op
- `timeout` is in seconds (the parser multiplies by 1000
internally); the previous companion's millisecond range
(e.g. 5000) is out of bounds and would have meant
5,000,000 ms = 83 minutes at runtime
- 15 PascalCase events (12 portable + 3 0.3.10 streaming):
MessageComplete, StreamChunk, StreamChunkThreshold
The 0.3.10 runtime reads hooks.json from
`${MINIMAX_DATA_DIR}/hooks/hooks.json` or
`${MINIMAX_DATA_DIR}/agents/<agentName>/hooks/hooks.json`, not
from a Plugin's own `io.minimax.mcode/hooks/hooks.json` path.
The Plugin registry accepts the `io.minimax.mcode` namespace in
plugin.json but the hook-config parser does not consult that
field. The spec records this caveat so a Plugin that wants its
hooks to fire knows it must also install hooks.json into one of
the two Runtime-resolved locations.
Validation
- scripts/lib/validation.mjs: HOOK_DOCUMENT_FIELDS grows to
include all 15 events plus the `hooks` wrapper. New
HOOK_MATCHER_FIELDS (`matcher`, `hooks`) and HOOK_COMMAND_FIELDS
(`type`, `command`, `timeout`) split the previous
HOOK_ENTRY_FIELDS allowlist into outer and inner halves.
validateHookEntry walks `hooks[]`; validateHookCommand
checks the inner descriptor. HOOK_RESERVED_FIELDS now covers
the 0.2.4 fields the parser does not consume (`args`, `env`,
`cwd`, `pattern`, `regex`, `glob`, `once`, `timeoutMs`) plus
the 0.2.4 internal discriminators (`shell`, `prompt`, `http`,
`agent`, `script`, `function`). `type` is moved out of the
reserved set and into HOOK_COMMAND_FIELDS so the validator
gives a more specific "type must be 'command'" error for bad
values. timeout range is 1..600 seconds. `cwd` traversal
tests are removed (cwd is no longer a hook field).
- The 0.2.4 cwd / `args` / `pattern` / `regex` / `glob` / `once`
/ `timeoutMs` fields are now closed-schema violations on
inner descriptors, surfaced as "reserved internal
discriminator" so a Plugin migrating from 0.2.4 to 0.3.10 gets
a clear error rather than a silent no-op.
Test evidence
- test/validation.test.mjs: existing tests updated to the 0.3.10
nested shape. New tests cover: outer / inner schema split,
type "command" required, 0.2.4 fields rejected as reserved,
both `{"hooks": {...}}` and `{...}` document bodies, $schema
optional, 15-event catalog including the three 0.3.10
streaming events. 21/21 pass.
- negative-injection self-audit (per the round-4 audit rule):
three contracts were broken and the validator caught each:
1. 0.2.4 flat shape -> REJECTED with "command is not a
recognized Hook field; expected one of hooks, matcher"
2. `args` added to inner descriptor -> REJECTED with
"args is a reserved internal discriminator and is not
allowed in a portable Hook entry"
3. `timeout: 5000` (out of 1..600 seconds) -> REJECTED with
"timeout must be an integer between 1 and 600 seconds
(the 0.3.10 parser multiplies by 1000)"
After each injection the example was restored and the
validator passed (12 events, no false green).
Design compliance
- spec: companion-only document; the portable proposal in
proposals/hooks.md (commit d86625d) is unchanged. Every
normative rule in this PR is marked Portable / Mcode-specific
/ Companion-only observability so the eventual merge with
the portable proposal has a clear scope boundary.
- example: examples/hello-mcode-hooks targets every event in
the 0.3.10 catalog with one record.mjs invocation each. Five
of the twelve events (PreToolUse, PostToolUse, SessionStart,
SessionEnd, UserPromptSubmit) are in the Fwe allowlist and
would auto-dispatch; the remaining seven load but never fire
and are recorded for forward compatibility.
- validator: the closed-schema rejection messages name the
field and the spec section that constrains it, so a Plugin
author can map the error to a fix without re-reading the
spec.
Companion evidence
- 14/14 manual invocations on Windows 11 24H2 +
@minimax-ai/code@0.3.10 (mcode-island v0.4.0, 2026-09-09);
5/12 events auto-dispatched by the runtime, 7/12 recorded
for forward compatibility. Recorded in the spec
"End-to-end smoke" section.
Known gaps (not addressed here, recorded in the spec)
- the seven `forward` events (Stop, PreCompact, Notification,
SubagentStart, SubagentStop, PermissionRequest, PermissionDenied)
load cleanly but never fire in 0.3.10. Lifting them into
the Fwe allowlist is a runtime change, not a spec change.
- the 0.3.10 streaming events (MessageComplete, StreamChunk,
StreamChunkThreshold) are not in the portable proposal.
Adoption or rejection is an upstream decision.
- the `decision` field, the `ask` value, and the
`hookSpecificOutput` shape are backed by cli.js literal
inspection only; no CI test exercises them.
- Plugin-supplied `extensions.io.minimax.mcode.hooks` paths in
plugin.json are accepted by the Plugin registry but the
0.3.10 hook-config parser does not read them. The spec
documents the dataDir path; a future runtime release is
expected to wire the Plugin path through.
Refs
- supersedes part of PR #20 (the schema description in the
previous companion is replaced by the 0.3.10 schema here)
- runtime: @minimax-ai/code@0.3.10 (npm, 2026-09-08)
- portable proposal: proposals/hooks.md (commit d86625d)
- user-reported regression recorded on 2026-09-091 parent 9600667 commit 1f5baf6
9 files changed
Lines changed: 1885 additions & 4 deletions
File tree
- examples/hello-mcode-hooks
- io.minimax.mcode/hooks
- scripts
- skills/hello-hooks
- proposals
- scripts/lib
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
0 commit comments