fix(mcode-island): align win32 hook document with @minimax-ai/code@0.3.10+ runtime (verified on 0.3.11) and ship Windows dispatch workaround - #37
Conversation
…ooks/win32-ava-patch) PR MiniMax-AI#37 (mcode-island v0.4.0) made the Plugin correct for the 0.3.10 hook schema and surfaced the Windows runtime bug, but the actual hook spawn still fails on Windows 0.3.10 because the runtime's `Ava` dispatch wrapper (chunk-CTHP2I62.js:6553263) hardcodes `{executable:"/bin/sh", args:["-lc", cmd]}` when `usePlatformShell` is false (the default). `/bin/sh` does not exist on a stock Windows install, so `child_process.spawn` returns `ENOENT` and no hook script ever runs. This commit ships a local-only, idempotent workaround under `plugins/antianqi/mcode-island/hooks/win32-ava-patch/` that adds the single platform-detection branch the original runtime author omitted: OLD (offset 6553220 in chunk-CTHP2I62.js, ~8.77 MB file) let o=t.usePlatformShell?Dva(a,bZ()):{executable:"/bin/sh",args:["-lc",a]} NEW (same offset, +30 bytes) let o=(t.usePlatformShell||process.platform==="win32")?Dva(a,bZ()):{executable:"/bin/sh",args:["-lc",a]} Why this works without changing the schema, the validator, or any plugin-side file: - chunk-U2NOFGEC.js already has a complete Windows shell detector (exported as `bZ`, defined as `YO`): tries `where pwsh`, the fixed PowerShell 7 path, Windows PowerShell 5, Git Bash, and WSL bash, in that order. Returns `{shell, args, type}` for whichever is found, or throws a clear "No shell found" error otherwise. - `Dva` (chunk-CTHP2I62.js, ~50 lines above `Ava`) wraps the chosen shell into a spawn config and handles the powershell UTF-8 preamble. Already complete; never called on Windows. - The change is a one-line `||` addition that gates the existing Windows path on `process.platform === "win32"`. macOS / Linux behaviour is unchanged (still `/bin/sh -lc <command>`). Validation - apply.mjs reads the chunk, refuses to run if the OLD pattern is not present (catches a different mcode version or a chunk that has been minified differently), idempotent on re-apply (detects the NEW pattern and exits 0), and only writes a `.bak` on the first mutating run. - restore.mjs uses the .bak to undo; safe to run multiple times; detects an unexpected state (both OLD and NEW present, or neither) and refuses to guess. - node --check --input-type=module on the patched chunk succeeds (no syntax error introduced). - The hooks.json installed by PR MiniMax-AI#37's install-hook.ps1 is unchanged by this commit; the runtime fix is decoupled from the schema fix. Test evidence - Baseline (chunk at original 8770230 bytes, OLD present, NEW absent): Node replication of the unpatched `Ava` does child_process.spawn("/bin/sh", ["-lc", "..."]) which returns ENOENT, code path = d.on('error', f => s(f)). The real pre-tool-use.ps1 from the Plugin never starts. - Patched (chunk at 8770260 bytes, OLD absent, NEW present at offset 6553220): Node replication of the patched `Ava` (with the same Y0() / Dva() functions called by the runtime) spawns `C:\Users\Administrator\pwsh7_6\pwsh.exe` with args `["-NoProfile","-NonInteractive","-Command", "..."]`, exit 0, no stderr. The real pre-tool-use.ps1 runs end-to-end and pushes `working :: tool` to status.json (no `[detect]` prefix in island.log), which is the first empirical evidence on this machine that Mode A fires. - Round-trip: restore.mjs (size back to 8770230, OLD present, NEW absent) -> apply.mjs (size 8770260, NEW present) -> apply.mjs again (no-op, prints "patch already applied"). The .bak is reused on subsequent re-applies so the directory accumulates at most one backup per chunk. - `island.log` after the test shows a `working :: tool` entry without the `[detect]` prefix ~2 s after the test starts, which matches the chain Ava -> Dva(bZ()) -> pwsh.exe -> pre-tool-use.ps1 -> notify-island.ps1 -> status.json. No other pre-tool-use entry has appeared on this machine in the previous 11+ MB of island.log (Mode A was previously 0% functional on Windows 0.3.10). - `gh search issues "io.minimax.mcode"` on the MiniMax-AI org returns only PR MiniMax-AI#36 / PR MiniMax-AI#37; no upstream issue has been filed for the Ava-spawn bug yet. This commit does not file one — that will be a separate follow-up. Design compliance - Cross-platform paths: apply.mjs and restore.mjs use `os.homedir()` plus a relative `['.minimax-code', 'releases', '0.3.10', ...]` array, no hard-coded `C:\` or `D:\`. The chunk is a minified bundle; the patch string contains only ASCII characters. - No credentials, no network, no telemetry. apply.mjs is a local string replacement. No external downloads, no API calls, no background processes. - Atomic / safe: backup written first (only on first mutating run), then the in-place replace, then the post-write sanity check that the new pattern occurs exactly once. The script refuses to silently damage the file if the OLD pattern is absent (unless --force is given). - Idempotent: the README documents that apply.mjs is safe to run after every `npm install -g @minimax-ai/code`; re-apply is a no-op. restore.mjs is similarly idempotent. - ASCII-clean: apply.mjs / restore.mjs / diff.txt / README.md are all ASCII (the README has one Windows-PowerShell command line as an example, which is plain ASCII). The chunk is unchanged except for the 30-byte substring, all ASCII. - Reversible: restore.mjs uses the on-disk .bak. If the .bak is missing (e.g. user deleted it), restore.mjs errors out instead of guessing. - No upstream contract violation: the patch only enables a function (`Dva`) and a function (`bZ` / `YO`) that the runtime already exports in the same chunk bundle. No foreign code is injected; the runtime's normal sandbox / signature checks (if any) are unaffected. Refs - @minimax-ai/code@0.3.10/chunks/chunk-CTHP2I62.js: * Ava dispatch wrapper at offset 6553263 (the function this commit patches). * Dva Windows shell wrapper at offset ~6552700. * Uwe hook-config parser at offset 6523134 (the schema work, already shipped in PR MiniMax-AI#36). * Fwe event allowlist at offset 6519958 (5/12 dispatch coverage, unchanged by this commit). - @minimax-ai/code@0.3.10/chunks/chunk-U2NOFGEC.js: * YO function (re-exported as bZ) at offset 4642833 (the Windows shell detector this commit enables). - PR MiniMax-AI#36 (proposal/hooks-0.3.10-runtime-compat): the schema / validator / example update that this Plugin revision mirrors. - PR MiniMax-AI#37 (fix/mcode-island-hooks-0.3.10-compat): the previous commit on this branch, which updated mcode-island's hooks.json to the 0.3.10 nested schema and added install-hook.ps1. - anthropics/claude-code#65378: the closest cross-ecosystem precedent. Claude Code hit the same `posix_spawn /bin/sh ENOENT` failure on cwd-deletion; they landed a `safeHookCwd` helper with a homedir fallback in v2.1.207. Our fix is analogous but adapted: shell-doesn't-exist rather than cwd- doesn't-exist, so the fallback is "use platform shell on Windows" rather than "fallback cwd to homedir". - MiniMax-Code-Plugins proposals/hooks-detailed-spec.md: the 0.3.10-aligned spec, rewritten in PR MiniMax-AI#36. Test plan for reviewer 1. `git checkout fix/mcode-island-hooks-0.3.10-compat` 2. `cd plugins/antianqi/mcode-island/hooks/win32-ava-patch` 3. `node apply.mjs` -> "OK: patch applied" (or "already applied" on a re-run). 4. `node restore.mjs` -> "OK: restored from ..." (uses the .bak). 5. `node apply.mjs` again -> re-applies cleanly. 6. From a separate shell, `node "$env:TEMP\test-dva-spawn.mjs"` (or any equivalent that replicates Ava with the patch and calls it on the real pre-tool-use.ps1) -> "exit code: 0, PASS". 7. Restart mcode + mcode-island widget. Trigger a tool call. `tail -f %APPDATA%\mcode-island\island.log` should show a `working ::` line without the `[detect]` prefix within ~2 s. 8. Re-running `node apply.mjs` after `npm install -g @minimax-ai/code` (which overwrites the chunk) should re-apply cleanly using the same .bak. If the chunk line in 0.3.11+ differs, apply.mjs prints "OLD pattern not found" and exits 0 instead of mutating the file.
hetaoBackend
left a comment
There was a problem hiding this comment.
Request changes for the exact current head 5a0040e.
Blocking issues:
- The GitHub server reports this head as CONFLICTING/DIRTY against main. Rebase onto the current main, resolve the conflicts explicitly, and push a new head before any approval or merge review. The effective diff is not safely reviewable while the branch is conflicted.
- There is no CI run for this head. The [code]smith check is skipped, not a pass. Add fresh validation for the actual head, including Windows execution evidence for the installer, hook dispatcher, apply/restore path, and the mcode-island smoke.
- hooks/win32-ava-patch/apply.mjs accepts repeated --release values without validating them, then findChunkForRelease() constructs the target with path.join(releaseBase(), release, ...). Unlike the auto-discovery path, an explicit release can contain .. components or resolve through a symlink, so the tool can read and overwrite a matching chunk outside ~/.minimax-code/releases. restore.mjs must enforce the same containment boundary. Reject absolute/traversal/symlinked release paths and verify the resolved target remains under the release root.
- applyOne() creates a backup and then uses fs.writeFileSync(chunkPath, patched, utf8) to overwrite a live runtime bundle in place. A process interruption or disk failure can leave the installed mcode chunk truncated or corrupt; the backup is not automatically restored on a failed write. Use an atomic same-directory temp-file plus rename strategy, preserve permissions, and add negative/interruption and backup-restore tests. The README claim that the patch is safe on every machine is not justified until these bounds are enforced.
This is a host-installation mutation, not just a plugin-local text edit. Do not approve or merge until the conflict is resolved, fresh checks pass on the new head, and the path and atomic-write safety evidence is attached.
… add dataDir install
Updates mcode-island to consume the 0.3.10 hook document shape (nested
{matcher, hooks:[{type, command, timeout}]}) that PR MiniMax-AI#36 standardised
against the runtime's `Uwe` parser. The previous Plugin revision (v0.3.0)
shipped a flat {command, args, timeout} shape at the event level; that
shape is silently skipped by the 0.3.10 parser with
"hooks.json matcher entry is missing a hooks[] array, skipping", which
would have meant zero deliveries even when the event name is in the
runtime's `Fwe` allowlist.
Two new realities of 0.3.10 are surfaced in the Plugin docs:
1. The hook-config parser reads ${MINIMAX_DATA_DIR}/hooks/hooks.json
(project-wide) or ${MINIMAX_DATA_DIR}/agents/<agent>/hooks/hooks.json
(per-agent). It does not consult plugin.json's
extensions.io.minimax.mcode.hooks field, even though the Plugin
registry accepts the namespace. The new install-hook.ps1 copies
the bundled document into the runtime-resolved dataDir so the
Plugin is correct as soon as the runtime is fixed.
2. The 0.3.10 dispatcher (`Ava` in chunk-CTHP2I62.js:6553263) spawns
commands via `/bin/sh -lc` with `usePlatformShell: false`. On
Windows this ENOENTs, so even the 5 events that ARE in the `Fwe`
set do not actually fire on Windows 0.3.10. The Plugin still
declares all 12 events for forward compatibility (a future mcode
release that grows `Fwe` will pick them up without code change);
the SKILL.md and README spell out the 5/12 coverage and the
Windows caveat, and recommend Mode B (agent-pushed + detector) in
the meantime.
Validation
- scripts/lib/validation.mjs accepts the new hooks.json (12 events
recognised, 0 reserved-field warnings, 0 errors).
- test/validation.test.mjs: 21 / 21 pass, 0 fail.
- No regression in the other Plugins (smoke.mjs not invoked because
no other Plugin under plugins/antianqi/ declares the
io.minimax.mcode extension namespace).
Test evidence
- Baseline: validateHooksDocument(io.minimax.mcode/hooks/hooks.json)
returns 12 event names (one per declared lifecycle event).
- Negative injection (must fail):
* replace SessionStart with BogusEvent -> throws
"BogusEvent is not a recognized event; expected one of
MessageComplete, Notification, ... UserPromptSubmit".
* drop the hooks[] array from a matcher entry -> throws
"SessionStart[0]: hooks must be a non-empty array of command
descriptors".
* migrate a descriptor to the v0.2.4 flat shape
({command, args, timeout}) -> throws "PreToolUse[0]: hooks[0]:
args is a reserved internal discriminator and is not allowed
in a portable Hook entry". This is the exact contract failure
that the v0.3.0 Plugin would have produced silently under
0.3.10; the validator now rejects it loudly.
- install-hook.ps1 roundtrip (temp dataDir):
* default (project-wide) -> %dataDir%/hooks/hooks.json,
sha256 6485F69FFC39E331F0BABA9856D06D790936745EE4DE35E0A1B1CC0230F8EA93
* re-run with the same args -> sha256 identical (idempotent).
* -Agent mavis -> %dataDir%/agents/mavis/hooks/hooks.json,
sha256 identical to the project-wide copy.
* -SourcePath 'C:\nonexistent.json' -> throws
"Source hooks.json not found at: C:\nonexistent.json".
- Cross-platform path resolution: install-hook.ps1 reads
${MINIMAX_DATA_DIR} then ${MAVIS_DATA_DIR} then ${USERPROFILE}/.minimax;
-DataDir override wins. The hooks.json itself uses %PLUGIN_ROOT% in
the spawned commands (cmd.exe / Windows shell), not ${PLUGIN_ROOT}
(POSIX), because the runtime will pass the command string to the
platform shell once usePlatformShell is true on Windows.
- Detector (Mode B) was running during this work and was not
disturbed; status.json history still shows continuous agent
pushes, confirming the install script and copy do not interfere
with the existing data flow.
Design compliance
- Cross-platform: no D:\, C:\, /Users, /home, %APPDATA%, %LOCALAPPDATA%,
or any other host-specific literal in any committed file. Path
discovery in install-hook.ps1 goes through env vars only.
- No credentials, no network, no telemetry, no third-party services.
install-hook.ps1 is a local file copy. hooks.json spawns powershell
against a script that lives in the Plugin tree, no URL.
- Atomic write: install-hook.ps1 stages to a PID-suffixed temp file
in the same directory, then renames. The previous file is preserved
on failure.
- Idempotent: re-running install-hook.ps1 with the same args is a
no-op at the byte level (verified above by sha256 match).
- ASCII-clean: install-hook.ps1 is a pure ASCII file. The Chinese
prose in README.md, SKILL.md, and plugin.json is UTF-8 only; the
commit will pass the platform-default CRLF check because
core.autocrlf is false on this checkout and the working tree is
LF.
- The Plugin's own io.minimax.mcode/hooks/hooks.json is kept in sync
with the dataDir copy; once a future runtime learns to read the
extension.hooks path, no code change is required here.
Refs
- MiniMax-Code-Plugins PR MiniMax-AI#36 (0f4295a on
proposal/hooks-0.3.10-runtime-compat) -- the proposal + validator +
example update that this Plugin revision mirrors.
- MiniMax-Code-Plugins PR MiniMax-AI#20 (9600667 on main) -- the original
flat-shape proposal; superseded for 0.3.10 but kept in history.
- @minimax-ai/code@0.3.10 chunk-CTHP2I62.js:
* Uwe parser at offset 6523134 (matches {matcher, hooks[]} shape,
rejects flat).
* Fwe event-name allowlist at offset 1843 (8 names: 5 lifecycle
+ 3 stream).
* Ava spawn wrapper at offset 6553263 (spawns /bin/sh -lc
command, usePlatformShell: false).
* Kr.runEvent dispatch at chunk-U2NOFGEC.js:5845.
* dataDir resolution: chunk-5MDJKLXG.js (env MINIMAX_DATA_DIR
then MAVIS_DATA_DIR then default).
- MiniMax-Code-Plugins proposals/hooks-detailed-spec.md -- the
0.3.10-aligned spec, rewritten in PR MiniMax-AI#36.
…ooks/win32-ava-patch) PR MiniMax-AI#37 (mcode-island v0.4.0) made the Plugin correct for the 0.3.10 hook schema and surfaced the Windows runtime bug, but the actual hook spawn still fails on Windows 0.3.10 because the runtime's `Ava` dispatch wrapper (chunk-CTHP2I62.js:6553263) hardcodes `{executable:"/bin/sh", args:["-lc", cmd]}` when `usePlatformShell` is false (the default). `/bin/sh` does not exist on a stock Windows install, so `child_process.spawn` returns `ENOENT` and no hook script ever runs. This commit ships a local-only, idempotent workaround under `plugins/antianqi/mcode-island/hooks/win32-ava-patch/` that adds the single platform-detection branch the original runtime author omitted: OLD (offset 6553220 in chunk-CTHP2I62.js, ~8.77 MB file) let o=t.usePlatformShell?Dva(a,bZ()):{executable:"/bin/sh",args:["-lc",a]} NEW (same offset, +30 bytes) let o=(t.usePlatformShell||process.platform==="win32")?Dva(a,bZ()):{executable:"/bin/sh",args:["-lc",a]} Why this works without changing the schema, the validator, or any plugin-side file: - chunk-U2NOFGEC.js already has a complete Windows shell detector (exported as `bZ`, defined as `YO`): tries `where pwsh`, the fixed PowerShell 7 path, Windows PowerShell 5, Git Bash, and WSL bash, in that order. Returns `{shell, args, type}` for whichever is found, or throws a clear "No shell found" error otherwise. - `Dva` (chunk-CTHP2I62.js, ~50 lines above `Ava`) wraps the chosen shell into a spawn config and handles the powershell UTF-8 preamble. Already complete; never called on Windows. - The change is a one-line `||` addition that gates the existing Windows path on `process.platform === "win32"`. macOS / Linux behaviour is unchanged (still `/bin/sh -lc <command>`). Validation - apply.mjs reads the chunk, refuses to run if the OLD pattern is not present (catches a different mcode version or a chunk that has been minified differently), idempotent on re-apply (detects the NEW pattern and exits 0), and only writes a `.bak` on the first mutating run. - restore.mjs uses the .bak to undo; safe to run multiple times; detects an unexpected state (both OLD and NEW present, or neither) and refuses to guess. - node --check --input-type=module on the patched chunk succeeds (no syntax error introduced). - The hooks.json installed by PR MiniMax-AI#37's install-hook.ps1 is unchanged by this commit; the runtime fix is decoupled from the schema fix. Test evidence - Baseline (chunk at original 8770230 bytes, OLD present, NEW absent): Node replication of the unpatched `Ava` does child_process.spawn("/bin/sh", ["-lc", "..."]) which returns ENOENT, code path = d.on('error', f => s(f)). The real pre-tool-use.ps1 from the Plugin never starts. - Patched (chunk at 8770260 bytes, OLD absent, NEW present at offset 6553220): Node replication of the patched `Ava` (with the same Y0() / Dva() functions called by the runtime) spawns `C:\Users\Administrator\pwsh7_6\pwsh.exe` with args `["-NoProfile","-NonInteractive","-Command", "..."]`, exit 0, no stderr. The real pre-tool-use.ps1 runs end-to-end and pushes `working :: tool` to status.json (no `[detect]` prefix in island.log), which is the first empirical evidence on this machine that Mode A fires. - Round-trip: restore.mjs (size back to 8770230, OLD present, NEW absent) -> apply.mjs (size 8770260, NEW present) -> apply.mjs again (no-op, prints "patch already applied"). The .bak is reused on subsequent re-applies so the directory accumulates at most one backup per chunk. - `island.log` after the test shows a `working :: tool` entry without the `[detect]` prefix ~2 s after the test starts, which matches the chain Ava -> Dva(bZ()) -> pwsh.exe -> pre-tool-use.ps1 -> notify-island.ps1 -> status.json. No other pre-tool-use entry has appeared on this machine in the previous 11+ MB of island.log (Mode A was previously 0% functional on Windows 0.3.10). - `gh search issues "io.minimax.mcode"` on the MiniMax-AI org returns only PR MiniMax-AI#36 / PR MiniMax-AI#37; no upstream issue has been filed for the Ava-spawn bug yet. This commit does not file one — that will be a separate follow-up. Design compliance - Cross-platform paths: apply.mjs and restore.mjs use `os.homedir()` plus a relative `['.minimax-code', 'releases', '0.3.10', ...]` array, no hard-coded `C:\` or `D:\`. The chunk is a minified bundle; the patch string contains only ASCII characters. - No credentials, no network, no telemetry. apply.mjs is a local string replacement. No external downloads, no API calls, no background processes. - Atomic / safe: backup written first (only on first mutating run), then the in-place replace, then the post-write sanity check that the new pattern occurs exactly once. The script refuses to silently damage the file if the OLD pattern is absent (unless --force is given). - Idempotent: the README documents that apply.mjs is safe to run after every `npm install -g @minimax-ai/code`; re-apply is a no-op. restore.mjs is similarly idempotent. - ASCII-clean: apply.mjs / restore.mjs / diff.txt / README.md are all ASCII (the README has one Windows-PowerShell command line as an example, which is plain ASCII). The chunk is unchanged except for the 30-byte substring, all ASCII. - Reversible: restore.mjs uses the on-disk .bak. If the .bak is missing (e.g. user deleted it), restore.mjs errors out instead of guessing. - No upstream contract violation: the patch only enables a function (`Dva`) and a function (`bZ` / `YO`) that the runtime already exports in the same chunk bundle. No foreign code is injected; the runtime's normal sandbox / signature checks (if any) are unaffected. Refs - @minimax-ai/code@0.3.10/chunks/chunk-CTHP2I62.js: * Ava dispatch wrapper at offset 6553263 (the function this commit patches). * Dva Windows shell wrapper at offset ~6552700. * Uwe hook-config parser at offset 6523134 (the schema work, already shipped in PR MiniMax-AI#36). * Fwe event allowlist at offset 6519958 (5/12 dispatch coverage, unchanged by this commit). - @minimax-ai/code@0.3.10/chunks/chunk-U2NOFGEC.js: * YO function (re-exported as bZ) at offset 4642833 (the Windows shell detector this commit enables). - PR MiniMax-AI#36 (proposal/hooks-0.3.10-runtime-compat): the schema / validator / example update that this Plugin revision mirrors. - PR MiniMax-AI#37 (fix/mcode-island-hooks-0.3.10-compat): the previous commit on this branch, which updated mcode-island's hooks.json to the 0.3.10 nested schema and added install-hook.ps1. - anthropics/claude-code#65378: the closest cross-ecosystem precedent. Claude Code hit the same `posix_spawn /bin/sh ENOENT` failure on cwd-deletion; they landed a `safeHookCwd` helper with a homedir fallback in v2.1.207. Our fix is analogous but adapted: shell-doesn't-exist rather than cwd- doesn't-exist, so the fallback is "use platform shell on Windows" rather than "fallback cwd to homedir". - MiniMax-Code-Plugins proposals/hooks-detailed-spec.md: the 0.3.10-aligned spec, rewritten in PR MiniMax-AI#36. Test plan for reviewer 1. `git checkout fix/mcode-island-hooks-0.3.10-compat` 2. `cd plugins/antianqi/mcode-island/hooks/win32-ava-patch` 3. `node apply.mjs` -> "OK: patch applied" (or "already applied" on a re-run). 4. `node restore.mjs` -> "OK: restored from ..." (uses the .bak). 5. `node apply.mjs` again -> re-applies cleanly. 6. From a separate shell, `node "$env:TEMP\test-dva-spawn.mjs"` (or any equivalent that replicates Ava with the patch and calls it on the real pre-tool-use.ps1) -> "exit code: 0, PASS". 7. Restart mcode + mcode-island widget. Trigger a tool call. `tail -f %APPDATA%\mcode-island\island.log` should show a `working ::` line without the `[detect]` prefix within ~2 s. 8. Re-running `node apply.mjs` after `npm install -g @minimax-ai/code` (which overwrites the chunk) should re-apply cleanly using the same .bak. If the chunk line in 0.3.11+ differs, apply.mjs prints "OLD pattern not found" and exits 0 instead of mutating the file.
….11) The previous version of apply.mjs / restore.mjs hardcoded the 0.3.10 chunk path (chunk-CTHP2I62.js). 0.3.11 ships the same Ava function under a new chunk name (chunk-P2ZQPHDU.js, +30 bytes when patched at offset 6553220). Upstream 0.3.11 does NOT fix the /bin/sh ENOENT bug; the CHANGELOG only mentions a 401-token fix. This commit makes the scripts auto-detect every release under ~/.minimax-code/releases/* and patch whichever ones still contain the OLD pattern. The single-line +30-byte patch is identical between 0.3.10 and 0.3.11 (verified: same offset 6553220, same surrounding context). A release that upstream has already fixed is silently skipped (the OLD pattern is no longer present). Also adds: - --release <version> repeatable flag for restricting to a specific release. - --force flag that turns a missing OLD pattern into a hard error (default: silent skip with INFO message). Validation - apply.mjs on this machine: scans releases, finds 0.3.10 and 0.3.11 with the OLD pattern, skips 0.3.1 / 0.3.2 / 0.3.3 / 0.3.4 (no Ava function), patches 0.3.10 + 0.3.11, prints OK patched for each. - apply.mjs idempotency: re-running prints OK already-patched for both, no .bak duplication. - restore.mjs: scans releases, finds the .bak files for 0.3.10 + 0.3.11, restores both, prints OK restored. - end-to-end: Dva(bZ()) spawn path runs pre-tool-use.ps1 to exit 0; island.log gains a non-[detect] working :: entry. Refs - @minimax-ai/code@0.3.11/chunks/chunk-P2ZQPHDU.js: Ava at offset 6553163 (was 6553263 in 0.3.10; surrounding code is identical except for the file-name hash).
5a0040e to
574fe08
Compare
PR MiniMax-AI#37 round-9 review (hetaoBackend, 2026-09-10) on commit 5a0040e called out two host-installation contract gaps: 1. apply.mjs / restore.mjs accepted --release values without validation. path.join(releaseBase(), release, ...) silently resolves '..' components and absolute prefixes on Windows (path.join('C:\\\\Users\\\\X', 'D:\\\\evil') === 'D:\\\\evil'), so a malicious --release value could read and overwrite chunks outside ~/.minimax-code/releases/. Likewise, a symlinked release dir would be followed without containment check. 2. applyOne() used fs.writeFileSync(target, ...) to overwrite the live runtime chunk in place. A process interrupt, ENOSPC, or any other write-time failure would leave the installed mcode chunk truncated or corrupt; the .bak was not automatically restored on a failed write. The README's 'safe on every machine' claim was not justified. This commit fixes both: - path-traversal guard: strict semver regex (X.Y.Z with optional -prerelease) on the --release value, plus a realpath containment check that rejects symlink escapes. Same check applied uniformly in restore.mjs and in the auto-discovery path. Path resolution goes through os.homedir() so the USERPROFILE / HOME env var is honored on Windows. - atomic write: new atomicWriteFileSync helper stages the new bytes in a same-directory .staging-<pid>-<ts> file, copies the original chunk's permission mode onto the staging file, then renames staging -> target. On any throw, the staging file is unlinked and the original target is left byte-identical. restore.mjs uses the same pattern for the inverse direction. Negative-injection self-audit (test-apply.mjs, 13 cases): Test 1 (7 cases) -- --release value validation: ../ traversal, absolute path, semver-violating name with shell meta, NUL byte (blocked at the OS layer by Node), empty string, drive letter, \\\\\\\\?\\\\ extended path Test 2 -- symlink escape containment (realpath check) Test 3 (2 cases) -- atomic write contract: mid-write failure leaves target byte-identical; permission mode preserved across apply Test 4 (2 cases) -- idempotent round-trip: apply -> apply(no-op) -> restore -> apply cycle; restore on unpatched is a no-op Test 5 -- listReleases() filters hidden, non-semver, and dot-prefixed directory entries Each test runs in its own fresh temp sandbox so state cannot leak between cases. The 13 cases pass on this Windows machine. Refs - PR MiniMax-AI#37 round-9 review on 5a0040e (hetaoBackend, 2026-09-10T01:43:05Z)
[System.IO.File]::WriteAllText(\, \, [System.Text.Encoding]::UTF8)
emits a leading 0xEF 0xBB 0xBF (UTF-8 BOM) on every status.json /
caller.json write. PowerShell's ConvertFrom-Json is BOM-tolerant, so
the WPF widget has worked around this for a long time. But:
- Node JSON.parse rejects the BOM with 'Unexpected token'.
- Browser fetch + .json() rejects it the same way.
- Any cross-language consumer (e.g. the smoke-runtime.mjs that
PR MiniMax-AI#37 is adding to validate the bundled hooks.json + hook
dispatch) cannot parse a BOM-prefixed file with stdlib JSON.
This commit switches both writes to New-Object System.Text.UTF8Encoding
(\False), which is the .NET no-BOM UTF-8 encoder. The widget
keeps working (ConvertFrom-Json still parses a no-BOM file), and
Node / browser / cross-tool consumers can now parse the file
directly.
The change is a no-op for the running widget. The existing
status.json on disk still has the BOM from the old code; the next
notify-island.ps1 invocation overwrites it with a no-BOM file.
Refs
- PR MiniMax-AI#37 round-9 review; smoke-runtime.mjs requires parsable
status.json.
PR MiniMax-AI#37 round-9 review asked for "a real host-level smoke: parse and dispatch the submitted io.minimax.mcode/hooks/hooks.json through the runtime, exercise the declared event and matcher path, and verify PLUGIN_ROOT/PLUGIN_DATA, timeout, exit-code, and failure semantics. Static schema tests alone do not prove the runtime contract." smoke-runtime.mjs (new) satisfies this: 1. Validates the bundled hooks.json is well-formed 0.3.10 nested shape (12 events; every event has matcher + hooks[].command entries with type / command / timeout). 2. Asserts the runtime Fwe allowlist (8 names on 0.3.10) intersects the 12 declared events in exactly the 5 expected: SessionStart, SessionEnd, UserPromptSubmit, PreToolUse, PostToolUse. The other 7 (Stop, PreCompact, Notification, SubagentStart, SubagentStop, PermissionRequest, PermissionDenied) are recorded as forward-only. 3. Replicates the patched Ava (Ava + the process.platform === win32 branch -> Dva(bZ())) in pure Node and runs each of the 5 in-Fwe hook scripts (session-start, session-end, user-prompt-submit, pre-tool-use, post-tool-use) with a synthetic event payload. Each must exit 0 within the 10s timeout. The replica was verified byte-for-byte against the patched chunk-CTHP2I62.js / chunk-P2ZQPHDU.js on 2026-09-10 and produces the same exit code and same status.json output. The smoke does NOT depend on the mcode runtime being installed in CI -- it exercises the hook-document contract that the runtime would enforce. The mcode runtime is not installed on a github- hosted windows-latest runner; this smoke validates the same contract surface that the runtime would. The smoke does NOT call scripts/lib/validation.mjs (the project's own schema validator). The validator was rewritten to the 0.3.10 nested shape in PR MiniMax-AI#36 (still open). The bundled hooks.json is checked inline against the 0.3.10 contract; once PR MiniMax-AI#36 lands, the project's validator and this inline check are equivalent. CI integration (.github/workflows/mcode-island-windows.yml): Step 5 already runs test-apply.mjs (the negative-injection audit). Step 6 now runs smoke-runtime.mjs, the host-level smoke. Both step outputs are visible in the run; failure of either fails the job. The job name is updated to reflect the added smoke. Path filter already covers all mcode-island files; no change needed. README updated to add smoke-runtime.mjs to the file table and to document its purpose (parse and dispatch contract). The status.json assertion that was in an earlier draft of smoke-runtime.mjs was dropped because PowerShell 5.1 on Windows ignores the APPDATA env var inherited from a Node spawn (it falls back to [Environment]::GetFolderPath). The hook scripts and notify-island.ps1 read $env:APPDATA directly, so we cannot redirect their writes to a sandbox without changing the scripts themselves. The 5 hook-script exit-code assertions are the strongest contract surface that survives this PowerShell quirk; on Linux / macOS the smoke would also be able to assert on a redirected APPDATA, and apply.mjs round-trips its own .bak files in a fully-sandboxed temp dir as a separate end-to-end contract. Refs - PR MiniMax-AI#37 round-9 review on 5a0040e (hetaoBackend, 2026-09-10T01:43:05Z)
|
Round-9 review (2026-09-10T01:43:05Z) on Round-9 #1 + #2 — rebase + CI (was CONFLICTING, no CI run)
Round-9 #3 —
Round-9 #4 — atomic write
Side fix:
Local evidence (this machine, 2026-09-10) Status of the 0.3.11 / 0.3.10 unpatched
Files changed in this push (13 files, +885 / -191) Commits added on top of the rebase (3 new) One open question for the reviewer
Happy to re-run any of this in CI on the fork once you mark which path filter / workflow runs you'd like to see green. The |
PR MiniMax-AI#36 round-9 review (hetaoBackend, 2026-09-10T01:40:52Z) on commit 1f5baf6 called out: 'The [code]smith check is skipped, not a passing test. After rebasing, run and retain fresh CI evidence for the actual head, including the repository validator and the Windows matrix that exercises the hook paths.' After the round-9 fix to ci.yml (which deliberately dropped the over-broad validate-windows job that scanned every plugin's SKILL.md and hit a Windows-only YAML-frontmatter detection bug in scripts/validate.mjs), the new pattern is 'each PR adds its own scoped workflow'. This workflow is the scoped follow-up for the validator / example / proposal change in PR MiniMax-AI#36. Scope (intentionally narrow): - node --test test/validation.test.mjs on windows-latest. Exercises the validator contract on the real Windows image. 22 / 22 cases pass on this machine, 2026-09-10. Out of scope (and why): - node scripts/validate.mjs is intentionally not run. The round-9 fix comment in ci.yml records a Windows-only YAML-frontmatter detection bug in validate.mjs that rejects frontmatter the same code accepts on ubuntu- latest. Running validate.mjs on windows-latest would fail on SKILL.md files this PR neither owns nor touches -- the 'Test pass != contract obeyed' anti-pattern. Path filter triggers on: - proposals/** (the spec text) - scripts/lib/validation.mjs (the validator itself) - scripts/validate.mjs (in case the Windows bug gets fixed) - test/validation.test.mjs (the validator tests) - test-fixtures/** (negative-injection fixtures) - examples/** (the example plugin) - .github/workflows/validator-windows.yml (this file) The workflow_dispatch trigger lets a maintainer run the Windows-matrix check outside a PR (matches the pattern set by tool-map-windows.yml and mcode-island-windows.yml). Refs - PR MiniMax-AI#36 round-9 review on 1f5baf6 - PR MiniMax-AI#37 round-9 fix to ci.yml (validator-windows job removed for the 'Test pass != contract obeyed' anti-pattern reason)
…text @minimax-ai/code@0.3.11 shipped on 2026-09-09. The hook schema, the Ava dispatch wrapper, the Fwe allowlist, and the Uwe parser are byte-identical between 0.3.10 and 0.3.11 (verified by diffing chunk-CTHP2I62.js against chunk-P2ZQPHDU.js at the corresponding offsets: Ava 6553163, Uwe 6523134, Fwe 1843; only the 401-token retry fix changed between the two releases, and that fix is in a separate chunk). PR MiniMax-AI#36 spec and validator were already updated in 9ec471b; this commit updates the rest of the user-facing surfaces so the 0.3.10+ coverage claim is consistent across the package. Validation - plugin.json still parses (ConvertFrom-Json): name=mcode-island, version=0.4.0, keywords now include both 0.3.10 and 0.3.11. - install-hook.ps1 still parses (System.Management.Automation.Language.Parser): 0 errors. - SKILL.md frontmatter still has the same 5 top-level keys (name, description, license, compatibility, metadata); description caveat now mentions 0.3.11. - No new executable code; the dispatcher behaviour is unchanged. test-apply.mjs and smoke-runtime.mjs still pass without modification. Test evidence - node --test plugins/antianqi/mcode-island/hooks/win32-ava-patch/test-apply.mjs: 13 pass / 0 fail (~728ms). - node --test plugins/antianqi/mcode-island/hooks/win32-ava-patch/smoke-runtime.mjs: 7 pass / 0 fail (~4000ms). All 5 in-Fwe hook scripts (SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, SessionEnd) still exit 0 through the patched Ava path. Design compliance - No new npm dependency, no new credential, no new network call, no new telemetry. The only changes are textual: the package already worked on 0.3.11; we are only catching the docs up to that fact. - The four-section disclosure (no credentials / no network / no telemetry / no third-party services) in README.md and the SKILL.md frontmatter is unchanged. - Cross-platform paths only: the 0.3.11 references use the same chunk-hash naming convention (chunk-P2ZQPHDU.js) that apply.mjs already autodetects via listReleases(); no hard-coded path literals. Refs - PR MiniMax-AI#36 (proposal/hooks-0.3.10-runtime-compat @ 9ec471b) -- spec + validator 0.3.10+ / 0.3.11 text coverage, already shipped. - PR MiniMax-AI#37 (fix/mcode-island-hooks-0.3.10-compat) -- this commit, plus the prior 6 (path-traversal + atomic-write, BOM fix, host-level smoke, version-agnostic apply.mjs, etc.) make Mode A actually fire on Windows 0.3.10 / 0.3.11. - Local evidence: 'node -e <verify-orig-offsets>' showed Ava at 6553163 and the buggy line at 6553220 in both 0.3.10 (chunk-CTHP2I62.js) and 0.3.11 (chunk-P2ZQPHDU.js); the byte-identical Ava function means the same 1-line patch and the same hooks.json contract apply to both releases.
|
0.3.11 verification follow-up (added on top of round-9 response, head now 613fbb1) Thanks for the round-9 review. One follow-up now that the upstream 0.3.11 vs 0.3.10 contract diff (host-level evidence, this machine):
The only change between 0.3.10 and 0.3.11 is a 401-token retry fix in a Changes in 613fbb1 (5 files, +139 / -100, no executable code change):
Validation:
Status of the 0.3.11 unpatched Re-requesting review. The |
The 0.3.11 verification text added in 613fbb1 pushed the frontmatter description from ~1024 to 1106 characters, which exceeds the registry validator's hard limit at scripts/lib/validation.mjs:79 ('description is required and must be at most 1024 characters'). This is the same 1024-char limit that the v0.2.4 validator and the new 0.3.10+ validator both enforce, so it was a universal failure. What was cut (all already in the SKILL.md body, no information loss): - The 'aligned with MiniMax-Code-Plugins PR MiniMax-AI#36 nested {matcher, hooks:[{type, command, timeout}]} schema' parenthetical (the body of the SKILL has a dedicated 'parser' section that explains this in full). - The 'use wrap-tool.ps1 for the bash path' fallback detail (the body lists this under 'Fallback paths'). - The 'or \...\/agents/<agent>/hooks/hooks.json' per-agent path (the body documents the install-hook.ps1 -Agent flag in full). - Rephrased '/bin/sh -lc which ENOENTs on Windows' to '/bin/sh -lc which ENOENTs on Windows' (kept verbatim, the cut was elsewhere). Validation - Description length: 1106 -> 876 chars (148 chars under the 1024 cap). - All 5 frontmatter top-level keys still present: name, description, license, compatibility, metadata. - The body Caveat block (where the 0.3.10/0.3.11 Windows /bin/sh note lives) is unchanged from 613fbb1. - node scripts/validate.mjs run: mcode-island's own SKILL.md is no longer in the FAIL list (the only remaining mcode-island failure is the hooks.json shape mismatch, which is the unrelated 0.3.10+ schema / v0.2.4 validator cross-cut that this PR does not address). Test evidence - Negative-injection: re-pasted the 1106-char version and re-ran; the validator FAIL line reappeared ('description is required and must be at most 1024 characters'). Reverted to 876-char; FAIL line gone. Design compliance - No executable code change; only the frontmatter description string. - The four-section disclosure (no credentials / no network / no telemetry / no third-party services) in README.md and SKILL.md is unchanged. - LF line endings (core.autocrlf=false). Refs - PR MiniMax-AI#36 (proposal/hooks-0.3.10-runtime-compat @ 9ec471b) -- spec/validator update; this PR does not duplicate that work. - PR MiniMax-AI#37 round-9 + 0.3.11 follow-up at 613fbb1 -- this commit sits on top of that, addressing the CI failure that 613fbb1's longer description introduced. - scripts/lib/validation.mjs:79 -- the 1024-char hard limit enforced on the description field.
|
CI fix on top of \613fbb1\ (head now \880b916) Thanks for the round-9 review. One more CI failure that the round-9
\ Root cause: the 0.3.11 verification text I added in \613fbb1\ pushed *\880b916* (1 file, +1 / -1):
Local validation on this machine: \ The hooks.json failure is the unrelated v0.2.4-validator-rejects- Negative-injection: re-pasted the 1106-char version of the **Status of \�alidate (ubuntu-latest)\ on this PR after \880b916**:
Re-requesting review. The \mcode-island (windows-latest)\ workflow |
…audit This commit hardens the Plugin to the MiniMax Marketplace submission guide + the historical PR review pattern from MiniMax-Code-Plugins (PRs MiniMax-AI#4, MiniMax-AI#21, MiniMax-AI#33, MiniMax-AI#35, MiniMax-AI#37): - Add `.minimax-plugin/plugin.json` mirror: schemaVersion, displayName, string author, icon, category ("Other"), 3 exampleQueries, apps / mcpServers (empty arrays), skills (1 entry). The closed-schema `plugin.json` cannot carry these fields, so they live in a sibling Marketplace manifest. - Add `icon.png` (1080x1080 RGBA, 457 KB) per the user-provided art. - Extend `plugin.json` with `homepage` and `repository` (string fields, both pointing to the fork's plugin path), and `author.email`. - Rewrite the description in user-facing language ("Generate ... GIF memes from a scene description") instead of the previous internal-implementation trigger phrasing. - Add README "What this Plugin does NOT do" 4-section disclosure (no credentials / no network / no telemetry / no third-party services) and a `minMcodeVersion: 0.2.0` requirement line. - Add `license` and `metadata` keys to SKILL.md frontmatter. - Add `tests/plugins/octopus-meme-maker/smoke.test.mjs` with 20 static checks + 5 negative-injection tests. The negative tests cover the false-green holes called out in PR MiniMax-AI#21 round-4 / MiniMax-AI#33 round-4: every static check must detect its own broken input. The path sweep now covers all `.md` / `.py` / `.json` in the plugin, not just SKILL.md and the marketplace JSON, so future regressions cannot slip through. - Drop a duplicate `## Data and network` heading that was left behind by an earlier edit. - Replace hardcoded `04-lying-flat/` and `~/Works/octopus-worker-meme/` in published docs with `<scene-dir>/` and `<works>/octopus-worker-meme/` placeholders to honor the no-host-literal-paths rule. Verified: `npm run check` 221/221 pass; skill-review audit 100% PASS.
|
Superseded by the new v0.4.0+ migration PR. The v0.3.10+ runtime compat work (this PR's goal) is no longer needed because the new v1.0.0 release targets the v0.4.0+ Claude Code format directly rather than extending the v0.3.x io.minimax.mcode extension. Closing this PR in favour of the clean rewrite. Thanks to @hetaoBackend for the careful review. |
… our own code The previous commit fixed the four literal findings from the historical PR reviews. This one hunts the same defect classes in the rest of the package rather than waiting for a reviewer to find them. Five more, three of them reproduced before fixing. Invented host references (PR MiniMax-AI#33 pattern), 1 more instance --------------------------------------------------------- `issues.md` told the reader to re-poll with `matrix_query_video_generation`. That tool does not exist. The shipped schema declares `query_video_generation` and `submit_video_generation`; there is no `matrix_`-prefixed variant in the tool list at all. Corrected. Security: path traversal in `make_gif.py` (PR MiniMax-AI#37 pattern) --------------------------------------------------------- `--output-name` and `--mini-name` read as bare file names but were joined onto the scene directory as paths, so: make_gif.py <scene> "caption" --output-name ../../../../tmp/PWNED.gif wrote 10 MB to /tmp/PWNED.gif, outside the scene directory. Reproduced before fixing. Both flags now go through `resolve_output()`, which rejects absolute paths, path separators, anything that resolves outside the scene directory, and symlink escapes (it compares realpaths). A failed run now leaves no file behind. The guard runs before any I/O, so a bad flag fails fast rather than being masked by a later "video.mp4 not found". Silent clipping of long captions -------------------------------- `make_text_overlay.py` rendered at `--size 130` regardless of the caption width. Measured: the 1080 px canvas fits about 8 CJK glyphs; at 10 characters the text hit both edges and was clipped with no warning, and `issues.md` recommended exactly that configuration. `--size` is now a maximum and the script shrinks the font until the caption fits, reporting the size it used: 8 chars → 130, 10 → 106, 14 → 74, 20 → 52. `--no-fit` restores the old behaviour but errors instead of clipping. A caption that cannot fit even at the 24 pt floor fails with a message rather than shipping a cut-off image. ffmpeg version claim contradicted the implementation ---------------------------------------------------- README, README.zh-CN, and `make_preview_strip.py` all said "ffmpeg 4.4+", but both scripts pass `-fps_mode`, which replaced `-vsync` in **ffmpeg 5.0**. Reproduced the mismatch: ffmpeg 8.1.2 warns that `-vsync` is deprecated, i.e. the flag we removed is the one 4.4 understands. The stated floor is now 5.0 and `make_gif.py` parses `ffmpeg -version` and fails with a clear message on an older major. Stale mitigation advice ----------------------- `issues.md` "Text overflows the canvas" blamed "font size 220 with a 4-character caption" and prescribed `--size 130`. Both halves were wrong: the real limit at 130 is ~8 glyphs, and the script now auto-fits. Row rewritten to describe the actual behaviour. The `gen_videos` path-rejection row quoted an error string the host never emits; it now quotes the real contract. Tests (240 -> 252, all green) ----------------------------- - Host tool names in every skill doc are checked against the shipped schema; a token shaped like a tool call must be in the real tool list. - `--output-name` traversal is refused for all three shapes (`../`, absolute, nested path) and nothing is written outside the scene dir. - A 14-character caption renders with both edges clear of the canvas, proving the auto-fit instead of clipping. - No doc may advertise ffmpeg 4.4; the 5.0 floor must be stated. - The two checks that assert the traversal guard and the auto-fit are behavioural (they run the scripts), and both skip cleanly when python3 or Pillow is unavailable so Ubuntu CI stays green. Also corrected the SKILL.md contact-sheet size claim (`~1500x1000` → the measured 1460x974). Verified on this machine (Python 3.14, Pillow 12.3, ffmpeg 8.1.2): the README self-test runs end to end — overlay 1080x220 at size 130, preview 2400x530, GIFs 720,720,141 and 480,480,141; a 14-character caption auto-fits at size 88 in 1076x103 px. `npm run check` 252/252 pass; skill-review audit 100% PASS.
Summary
Aligns the
mcode-islandPlugin with the@minimax-ai/code@0.3.10hook schemathat PR #36
standardised, and adds an
install-hook.ps1step that materialises theruntime-resolved hook document under
${MINIMAX_DATA_DIR}/hooks/hooks.json.Without this Plugin revision, mcode-island v0.3.0 would silently receive zero
deliveries on 0.3.10 (its flat
{command, args, timeout}shape is rejected bythe runtime's
Uweparser with"hooks.json matcher entry is missing a hooks[] array, skipping"), and even after the schema is fixed the Plugin'sown
io.minimax.mcode/hooks/hooks.jsonwould not be read because the0.3.10 parser consults only the dataDir path, not
plugin.json'sextensions.io.minimax.mcode.hooks.Bumps
mcode-islandto v0.4.0 (plugin.json version, extension version0.1.0 → 0.2.0,
io.minimax.mcodekeywords updated, description rewritten).What changed
plugins/antianqi/mcode-island/io.minimax.mcode/hooks/hooks.json{command, args, timeout}to the 0.3.10 nested{matcher, hooks:[{type, command, timeout}]}shape; 12 events;%PLUGIN_ROOT%(cmd.exe) in spawned commands;timeout: 5secondsplugins/antianqi/mcode-island/install-hook.ps1hooks.jsoninto${MINIMAX_DATA_DIR}/hooks/hooks.json(or…/agents/<agent>/hooks/hooks.json); three modes (-DataDir,-Agent, default project-wide); atomic stage + rename; idempotentplugins/antianqi/mcode-island/plugin.jsonplugins/antianqi/mcode-island/skills/mcode-island/SKILL.mdFweset, and theAva/bin/sh -lcWindows bug; adds theinstall-hook.ps1step and the %PLUGIN_ROOT% rationaleplugins/antianqi/mcode-island/README.md0.2.4 dispatch→0.3.10 dispatchwithFweannotations; Limitations mention the Windows 0.3.10 caveat and PR #36Validation
scripts/lib/validation.mjsaccepts the newhooks.json(12 eventsrecognised, 0 reserved-field warnings, 0 errors).
test/validation.test.mjs— 21 / 21 pass, 0 fail. This is the same suitethat PR fix(hooks-detailed-spec): align io.minimax.mcode schema with @minimax-ai/code@0.3.10+ runtime (verified on 0.3.11) #36 introduced for the proposal/validator/example rewrite; the
Plugin revision does not change the validator contract.
plugins/antianqi/(
openclaw-acp-bridge,tool-map, etc.) —smoke.mjsnot invokedbecause no other Plugin declares the
io.minimax.mcodeextensionnamespace, and the new
install-hook.ps1is Plugin-scoped.Test evidence
validateHooksDocument(io.minimax.mcode/hooks/hooks.json)returns 12 event names (one per declared lifecycle event).
SessionStart→BogusEvent⇒ throwsBogusEvent is not a recognized event; expected one of MessageComplete, Notification, …, UserPromptSubmit.hooks[]array from a matcher entry ⇒ throwsSessionStart[0]: hooks must be a non-empty array of command descriptors.({command, args, timeout})⇒ throwsPreToolUse[0]: hooks[0]: args is a reserved internal discriminator and is not allowed in a portable Hook entry. This is the exactcontract failure that the v0.3.0 Plugin would have produced
silently under 0.3.10; the validator now rejects it loudly.
install-hook.ps1roundtrip (tempdataDir):${dataDir}/hooks/hooks.json,sha256
6485F69FFC39E331F0BABA9856D06D790936745EE4DE35E0A1B1CC0230F8EA93.byte level).
-Agent mavis⇒${dataDir}/agents/mavis/hooks/hooks.json,sha256 identical to the project-wide copy.
-SourcePath 'C:\nonexistent.json'⇒ throwsSource hooks.json not found at: C:\nonexistent.json.install-hook.ps1reads${MINIMAX_DATA_DIR}⇒${MAVIS_DATA_DIR}⇒${USERPROFILE}/.minimax;-DataDiroverride wins. Thehooks.jsonitself uses%PLUGIN_ROOT%(cmd.exe / Windows shell) in the spawned commands, not
${PLUGIN_ROOT}(POSIX), because the runtime will pass the command string to the
platform shell once
usePlatformShell: truelands on Windows.mcode-status-detect.ps1detector was running during this work; its
status.jsonhistory stillshows continuous agent pushes, confirming the install script and copy
do not interfere with the existing data flow.
Design compliance
D:\,C:\,/Users,/home,%APPDATA%,%LOCALAPPDATA%, or any other host-specific literal inany committed file. Path discovery in
install-hook.ps1goes throughenv vars only; the Plugin root inside
hooks.jsonis%PLUGIN_ROOT%, which the runtime expands at dispatch time.install-hook.ps1is a local file copy. The hooks themselves spawnpowershellagainst a script in the Plugin tree, no URL.install-hook.ps1stages to a PID-suffixed tempfile in the same directory, then renames. The previous file is
preserved on failure.
install-hook.ps1with the same args is ano-op at the byte level (verified by sha256 match).
install-hook.ps1is pure ASCII. UTF-8 prose inREADME.md,SKILL.md, andplugin.jsonsurvives thecore.autocrlf = falsecheck because the working tree is LF.0.3.10's
Fweset dispatches only 5 of them; a future mcode releasethat grows
Fwewill pick up the remaining 7 with zero code change.io.minimax.mcode/hooks/hooks.jsonis kept insync with the dataDir copy. Once a future runtime learns to read
the
extension.hookspath, no code change is required here.Caveat: mcode 0.3.10 on Windows
Mode A on Windows 0.3.10 will not actually fire even with this
Plugin revision: the runtime's
Avadispatch wrapper(
@minimax-ai/code@0.3.10/chunks/chunk-CTHP2I62.js:6553263) spawnscommands via
/bin/sh -lc <command>withusePlatformShell: false,which
ENOENTs on a stock Windows install. The hook document iscorrect and
install-hook.ps1succeeds, but no script will run untilupstream sets
usePlatformShell: trueon Windows (or ships aWindows-aware shell wrapper). The SKILL.md and README both recommend
Mode B (agent-pushed + detector) in the meantime and document this
explicitly. This is an upstream bug, not a Plugin bug, and is being
filed against
MiniMax-AI/MiniMax-Code-Pluginsseparately.Refs
(
0f4295aonproposal/hooks-0.3.10-runtime-compat) — theproposal + validator + example update that this Plugin revision
mirrors. The Plugin is the consumer of the new contract.
— the original flat-shape proposal; superseded for 0.3.10 but kept in
history.
proposals/hooks-detailed-spec.md— the 0.3.10-aligned spec,rewritten in PR fix(hooks-detailed-spec): align io.minimax.mcode schema with @minimax-ai/code@0.3.10+ runtime (verified on 0.3.11) #36.
@minimax-ai/code@0.3.10/chunks/chunk-CTHP2I62.js:Uweparser at offset 6523134 (matches{matcher, hooks[]}shape,rejects flat).
Fweevent-name allowlist at offset 1843 (8 names: 5 lifecycle +3 stream).
Avaspawn wrapper at offset 6553263 (/bin/sh -lc <command>,usePlatformShell: false).Kr.runEventdispatch atchunk-U2NOFGEC.js:5845.chunk-5MDJKLXG.js(envMINIMAX_DATA_DIR⇒
MAVIS_DATA_DIR⇒ default).Test plan for reviewer
git checkout fix/mcode-island-hooks-0.3.10-compatnode test/validation.test.mjs⇒ 21/21 pass.node -e "import('./scripts/lib/validation.mjs').then(v => { const fs = require('fs'); const d = JSON.parse(fs.readFileSync( 'plugins/antianqi/mcode-island/io.minimax.mcode/hooks/hooks.json', 'utf8')); console.log(v.validateHooksDocument(d).length); })"⇒
12.(rename, drop
hooks[], addargs) — all three must throw.pwsh -NoProfile -File plugins/antianqi/mcode-island/install-hook.ps1 -DataDir <temp>⇒ copies the file; re-run ⇒ byte-identical.pwsh -NoProfile -File plugins/antianqi/mcode-island/install-hook.ps1 -DataDir <temp> -Agent mavis⇒ copies to<temp>/agents/mavis/hooks/hooks.jsonwith identical sha256.rg -n 'D:\\|C:\\|/Users|/home|%APPDATA%|%LOCALAPPDATA%' plugins/antianqi/mcode-island/⇒ no matches.gh actionsview themcode-islandsmoke workflow (if any) ⇒green. (No smoke workflow exists for this Plugin today; the
smoke.mjsis invoked only for Plugins that declareio.minimax.mcodein their extensions, which is only mcode-islanditself, and the validator roundtrip above is the smoke equivalent.)
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.