Conversation
Generates a three-file catalog (tools.summary.md, tools.md, tools.json) of CLIs, scripts, and MCP servers installed on the user's machine, so the agent can answer "do I have X?", "where is Y?", "how do I run Z?" without re-scanning the filesystem every session. Plugin shape (Skill-only, zero external deps, no package.json): - skills/tool-map/SKILL.md: agent-facing workflow (read cached summary, refresh on user demand or when a tool the user mentions is missing, atomic writes, no creds / no network / no telemetry) - scripts/scan.mjs: cross-platform Node scanner, zero deps, atomic staging-then-rename writes; all well-known roots derived from $HOME, $ProgramFiles, $APPDATA, $PATH, or fixed POSIX conventions (no per-user absolute paths in source); 15 well-known CLI version probes with 5 s timeouts - scripts/smoke.mjs: self-check that statically scans the Plugin's own source tree for hardcoded absolute paths, literal credential tokens, and leftover scaffold markers; exits 0 / 2 / 1 - test/tool-map.test.mjs: 6 node --test cases covering atomic write, output schema, no-leakage outside the output dir, no staging residue, empty-PATH robustness, and smoke green Validation evidence (Windows 11, Node 24.18.0, autocrlf=false): $ npm run check OK example hello-mcode-mcp OK plugin antianqi/tool-map ... tests 6 pass 6 fail 0 $ node scripts/smoke.mjs OK scanned 2 files, 0 violations. Design compliance (per hetaoBackend review rubric on PRs #2/#3): 1. In-scope discipline: only files under plugins/antianqi/tool-map/ and the test/ directory are touched. No edits to repo-root files, no writes to ~/.minimax/, no ~/.openclaw*/ side effects. 2. Portability: scan.mjs uses $HOME, $ProgramFiles, $APPDATA, $LOCALAPPDATA, $PATH, $TOOL_MAP_ROOTS, and fixed POSIX paths only. smoke.mjs statically verifies no D:/C:/E:/ or /Users/ or /home/ literal in any .md/.mjs file. 3. Credential disclosure: README and SKILL.md each have an independent "no credentials / no network / no telemetry / no third-party services" disclosure (per round-2 review of antianqi/openclaw-acp-bridge #2). 4. Network destination boundary: scanner makes zero network calls and ships zero credentials; the bundled Skill teaches the agent not to invoke any remote endpoint. 5. Delivery model: zero `npm install` / `npm link` is required. The scanner runs as a plain `node ./scripts/scan.mjs` process with only Node built-ins. 6. Atomic / safe file operations: every output file is written via `<out>.staging-<pid>-<rand>` then `rename`. On any failure the staging file is removed and the previous catalog is untouched. 7. Lint / failure semantics: smoke.mjs exits 0 / 2 / 1; never swallows FAIL. 8. Test coverage: 6 node --test cases; smoke.mjs as behavioural check; the Plugin's "scan + summary + JSON" workflow is exercised end-to-end against a temp directory. 9. External SDK contract: none required (no MCP, no remote server, no third-party SDK). 10. Self-check coverage: smoke.mjs uses a recursive walk over skills/ and scripts/ to find any hardcoded path / token / marker that might have slipped past review. Forward compatibility with PR MiniMax-AI#4 (validator hardening, not yet merged): - No mcp.json is shipped, so cwd / env / headers hardening does not apply. The scan.mjs and SKILL.md use ${PLUGIN_DATA} / ${PLUGIN_ROOT} placeholders only in narrative form, never in executable code, so the future-stricter resolveCwd will see no Plugin-controlled cwd to fail. - SKILL.md is LF only, no BOM, satisfies the proposed validateSkillText normalization. (The merged main validator also accepts LF directly.) Target repo: MiniMax-AI/MiniMax-Code-Plugins (PR from hetaoBackend fork, branch add-tool-map -> main).
…ectness)
Two P1 blockers from the hetaoBackend review:
P1-1: bundle-level atomicity was a lie
scan.mjs:374-376 wrote tools.md / tools.json / tools.summary.md via three
independent atomic renames. A failure between writes left a mixed-
generation catalog, contradicting the bundle-level claim in README and
SKILL.md. Rewrite atomicWriteBundle as a proper two-phase commit:
1. move every existing target to .bundle.backup-<pid>-<rand>/
2. write all new content into .bundle.staging-<pid>-<rand>/
3. rename each staging file onto its target
4. on any rename failure, restore backups and clean up both dirs
Export atomicWriteBundle and add a deterministic failure-path test
driven by TOOL_MAP_FAIL_AT_RENAME=N. Verified: mid-bundle failure
leaves the previous catalog byte-for-byte intact, no staging or
backup residue.
P1-2: subprocess execution contradicts read-only contract
scan.mjs:115-143 spawned 15 PATH-resolved programs with --version.
Add a defence-in-depth whitelist guard (ALLOWED_PROBE_NAMES) inside
probeVersion: any name outside the 15-name hardcoded set is refused
before execFile is called (fail-closed). Document the side effect
explicitly in README and SKILL.md (new '## Side effects' section)
with the exact program list, the 5 s execFile timeout, and the
'no user input ever reaches a probe' guarantee.
Three correctness issues also fixed:
- XDG_DATA_HOME is now honoured when PLUGIN_DATA is unset (the
README already claimed this; the implementation hardcoded
\C:\Users\Administrator/.local/share/tool-map).
- Dedupe no longer lower-cases the resolved path. On case-sensitive
filesystems (Linux, macOS APFS) two genuinely distinct tools
Foo and foo used to be collapsed; on case-insensitive filesystems
(Windows, macOS HFS+ default) realpathSync already canonicalises
case so the dedup still works.
- On POSIX, isToolFile now requires the execute bit (mode & 0o111).
A foo.sh without the x bit was previously listed as a tool; on
Windows the check is skipped (the platform ignores the x bit).
Tests (test/tool-map.test.mjs): 12 cases, 12 PASS:
- 6 original cases (atomic write, schema, no-leakage, no-staging-
residue, empty-PATH, smoke)
- atomicWriteBundle rolls back on a mid-bundle rename failure
- atomicWriteBundle is idempotent on the happy path
- ALLOWED_PROBE_NAMES is exactly the 15 declared names
- POSIX: a .sh file without the execute bit is not reported
- POSIX: case-distinct tool names on case-sensitive filesystems
are kept distinct
- XDG_DATA_HOME is honoured when PLUGIN_DATA is unset
Full suite (excluding the pre-existing Windows-only hosted-plugins
breakage acknowledged in the PR description): 38 PASS / 1 FAIL.
A Skill-only Plugin (no MCP, no network) packaging four long-running task
patterns distilled from OpenAI Codex harness v0.149.0 (codex-rs/core/).
Skills included:
- tool-output-budget truncate oversized tool output by token-aware
head + tail + marker (mirrors codex-rs/utils/
output-truncation)
- context-pressure-compact structured snapshot before continuing a long
task (mirrors codex-rs/core/src/compact.rs)
- parallel-fanout dispatch 2+ independent sub-tasks with task()
and aggregate (mirrors FuturesUnordered in
codex-rs/core/src/thread_manager.rs)
- plan-stream-emit emit todowrite-shaped plan before non-trivial
work (mirrors PlanUpdate / PlanDelta events
in codex-rs/protocol/src/protocol.rs)
Validation: passes npm run check (OK plugin antianqi/codex-harness-patterns).
License: Apache-2.0 (matches the host repository).
…, world-state-tracking, background-task (4 new Skills, 8 total)
Adds four Skills that round out the long-running task toolkit:
- review-mode switch to critic mode after finishing a chunk,
produce a PASS / FIX / REDO verdict
(mirrors EnteredReviewMode/ExitedReviewMode)
- delegate-with-context write a minimal-context brief for task()
instead of forwarding the full history
(mirrors InterAgentCommunication / CollabAgentSpawn)
- world-state-tracking persist a structured state file that survives
context compaction (mirrors WorldState in
core/src/context/world_state.rs)
- background-task run long-running commands in the background
with a log file, poll on later turns
(mirrors unified_exec / CleanBackgroundTerminals)
Manifest bumped to 0.2.0; README and plugin.json keywords updated to
cover the full 8-Skill surface.
Validation: npm run check still passes for this plugin
(OK plugin antianqi/codex-harness-patterns).
Adds two Skills that close the long-running task loop:
- goal-persistence P-14 SetThreadMemoryMode + ThreadGoalUpdated
(north-star goal file, drift self-test before
non-trivial tool calls, survives compactions)
- model-router P-07 model-provider-info + models-manager
(classify sub-task as cheap/medium/main, pass
model_config_id explicitly, no silent defaults)
Manifest bumped to 0.3.0; README table now lists all 10 Skills.
Validation: npm run check still passes for this plugin
(OK plugin antianqi/codex-harness-patterns).
…n; upgrade goal-persistence + parallel-fanout to v1.0
New Skills (2):
- completion-audit P-22 continuation template completion-audit section
(derive requirements, identify authoritative evidence,
verify each, only declare done on all-✅)
- fork-context-decision P-20 fork_turns semantics
(all / N / none — pick explicitly, not by default)
Skill upgrades to v1.0 (2):
- goal-persistence + completion-audit and blocked-audit sections
+ token-budget reporting rule
+ 'treat completion as unproven' alignment
- parallel-fanout + explicit-spawn principle (P-20: opt-in, not auto)
+ max_concurrency awareness
+ cross-references to fork-context-decision
and delegate-with-context
+ completion-audit on aggregation before done
Total Skills: 12. Manifest bumped to 0.4.0.
Validation: npm run check still passes for this plugin
(OK plugin antianqi/codex-harness-patterns).
…ng; upgrade context-pressure-compact + delegate-with-context to v1.0
New Skills (2):
- subagent-family-tracking P-23 agent-graph-store + SessionSource::SubAgent
(parent/child tree, Open/Closed status, lost-child prevention)
- goal-token-budgeting P-22 ext/goal/src/accounting.rs + continuation template
(track token_budget, surface at 50/80/100%, stop at 100%)
Skill upgrades to v1.0 (2):
- context-pressure-compact + 64K retention budget (RETAINED_MESSAGE_TOKEN_BUDGET from P-10)
+ discarded count reporting
+ cross-references to all 5 persistent-state files
- delegate-with-context + V2 message envelope (Message Type / Task name / Sender / Payload)
+ explicit return-path section
+ cross-references to fork-context-decision / model-router /
subagent-family-tracking
Total Skills: 14. Manifest bumped to 0.5.0.
Validation: npm run check still passes for this plugin
(OK plugin antianqi/codex-harness-patterns).
…koff, streaming-output-reader, session-handoff
Four new Skills extracted from the 'error / streaming / session-end' theme:
- error-recovery-strategy 4-bucket classification (transient / deterministic
/ stale / unknown) -> 5-action decision tree
(retry / switch / fallback / refresh-then-retry /
ask-user / skip); categorical, not reflexive
- retry-with-backoff explicit retry policy (max 3, base 2s, max 30s,
full jitter, 60s total budget); respects
Retry-After; hard ceiling; always escalates
- streaming-output-reader bounded-chunk reads (head / tail / grep) with
cumulative summary; max 3 reads per stream;
never loop, never buffer to context
- session-handoff at session end, write a handoff file so the
next session can pick up in 30 seconds;
mirrors state/runtime/recovery.rs
Total Skills: 18. Manifest bumped to 0.6.0.
Validation: npm run check still passes for this plugin
(OK plugin antianqi/codex-harness-patterns).
Each Skill's description: field now uses a structured 4-line format:
description: |
<one-sentence purpose>.
USE WHEN: <concrete signals and keywords>.
TRIGGER PHRASES: <user-original-language phrases>.
SKIP WHEN: <anti-patterns>.
This makes the descriptions keyword-greppable (ECONNREFUSED, permission
denied, etc.) so the LLM matches on real signals instead of interpreting
abstract prose. All 18 trigger phrases now spelled out in English AND
Chinese.
The 'Can I remember to use these skills?' question from the user
inspired this change: the previous abstract descriptions were too
vague for reliable LLM matching. This patch makes every Skill's
trigger conditions explicit and greppable.
Versions: manifest 0.6.0 -> 0.6.1 (patch: frontmatter only);
all Skill versions 0.1.0/0.2.0/.../1.0.0 -> +0.0.1.
No behavioral changes to Skill process / output / examples / checklist.
Only the frontmatter description field was rewritten.
Validation: npm run check still passes for this plugin
(OK plugin antianqi/codex-harness-patterns).
Per .minimax/memory/user.md plugin 偏好 (2026-08-19, tool-map v0.2): - README 必须有 4 段独立披露: no credentials / no network / no telemetry / no third-party services - 本次 commit 只改 README,不动 23 skills,不动 plugin.json 其他字段 - 无硬编码路径(smoke.mjs 扫描通过) PR MiniMax-AI#18 body 同步改为 Design compliance / Validation / Test evidence 三段式。
The previous implementation only restored target files that had a
previous version (backups[name] !== null). Two failure paths were
left uncovered:
1. Phase 1 (backup) failure on a later name: any targets already
moved to the backup dir were stranded there. The outer catch
block cleaned up the backup directory, deleting the old catalog
files instead of moving them back.
2. Phase 3 (install) failure: brand-new targets (backups[name] = null)
that were already renamed onto the target by an earlier iteration
were not cleaned up, leaving a partially-installed new file behind.
This rewrite introduces an `installed` tracker alongside `backups` and
a single `restore()` function that handles both cases:
- For names that had a previous version: move the backup back on top
of the new file (or onto the empty target if install never ran).
- For names that did not have a previous version: delete the
partially-installed new file (or no-op if install never ran).
- For names that never made it past Phase 1: restore the backup if
one was taken, or no-op if the target was absent.
Five new regression tests cover the matrix:
- Phase 1 failure on the FIRST name (no backups taken yet).
- Phase 1 failure on a LATER name (backups taken for earlier names).
- Phase 3 failure after a brand-new target was installed.
- Happy path with a previously-empty target dir.
- Happy path with a mix of existing and absent targets.
Local verification:
node --test test/tool-map.test.mjs
17 / 17 PASS (12 original + 5 new)
Resolves the first half of the hetaoBackend CHANGES_REQUESTED review (MiniMax-AI#18 (review)...). Affected Skills and the Codex-only params that were removed: - fork-context-decision: fork_turns=N -> pseudocode + 'mcode 适配' note - parallel-fanout: subagent=..., fork_turns=N -> pseudocode + note - delegate-with-context: subagent=..., task_name=..., fork_turns=N -> envelope only - background-task: task_name=..., run_in_background=..., action='kill' -> pseudocode + note - model-router: model_config_id=anthropic-sonnet-4 with reasoning_effort=high -> portable 3-tier rubric + note Each affected Skill now: 1. Teaches the DESIGN DECISION (what context, what tier, what handle) 2. Marks example calls as Codex-harness-style PSEUDOCODE 3. Adds an explicit 'mcode 适配' section telling the agent to adapt parameter names to the actual host API The Skills no longer prescribe invalid tool calls that mcode cannot execute. Reviewer point 2 is partially addressed. Also rewrites PR-STATUS.md to match v1.0.2 / 23 Skills inventory (reviewer point 1). Test evidence: - 5 SKILL.md updated - 0 new tool invocations invented - 0 hard-coded paths introduced
…nd long-term-memory (reviewer feedback) Resolves the second half of the hetaoBackend CHANGES_REQUESTED review (PR MiniMax-AI#18 reviewer point 3). Both Skills describe design patterns that *would* involve network calls, file writes, or background tasks if the host's runtime ever implemented them. The original wording presented these as if the agent could execute them directly. Reviewer flagged this as unsafe. Both Skills now carry an explicit 'Host runtime requirements' section that: 1. Lists the side effects the Skill's design presumes (network, filesystem writes, sub-agent spawn, schedule triggers, secret redaction, etc.). 2. States that the agent MUST NOT execute any of these on the strength of the Skill alone. 3. Requires the host's normal user-confirmation policy (approval_policy / ask mode / equivalent) to be followed for any execution. 4. Reframes the Skill as DESIGN-only, not EXECUTE. plugin-author-helper and long-term-memory now have the same host boundary pattern as the other Skills (which already said 'Skills are pure Markdown instructions; the agent applies them with its existing tools and existing permission model'). No other content was changed. Test evidence: - 2 SKILL.md updated, each gained one new section - 0 existing content removed - 0 new side effects introduced
Adds a Plugin-format Hooks declaration under `io.minimax.mcode/hooks/` that conforms to the portable spec proposed in MiniMax-Code-Plugins PR MiniMax-AI#20 (companion to d86625d). mcode 0.2.4 already ships the runtime dispatch path for five of the twelve events; the remaining seven are forward-looking and declared so the validator can warn on them. The agent does not need to call `notify-island.ps1` manually when the runtime wires the Hooks path. The detector-based fallback in `mcode-status-detect.ps1` continues to run for everything else, so this change is strictly additive: no existing capability is removed or renamed. ## What changed - `plugin.json`: bumped 0.2.1 → 0.3.0, declared `extensions.io.minimax.mcode.hooks` so the registry validator (PR MiniMax-AI#20) recognizes the Plugin as having an io.minimax.mcode client extension. - `io.minimax.mcode/hooks/hooks.json`: 12-event declaration using only the portable field vocabulary (`command`, `args`, `env`, `cwd`, `matcher`, `pattern`, `regex`, `glob`, `timeout`, `timeoutMs`, `once`). No reserved fields. `PLUGIN_ROOT` is used for the script path; no host-absolute literals. - `io.minimax.mcode/hooks/scripts/_lib.ps1`: shared helper exporting `Read-HookStdin`, `Push-Island`, `Test-IsSelfPush`, `Format-ToolSummary`. Loaded via dot-source from every event script. The self-push filter avoids recursive state churn when the agent calls `notify-island.ps1` directly through Bash. - `io.minimax.mcode/hooks/scripts/<event>.ps1` x 12: one script per event. State mapping: | event | pill state | notes | | ----------------- | ----------- | ----- | | SessionStart | idle | | | SessionEnd | idle | | | UserPromptSubmit | thinking | | | PreToolUse | working | skips self-push | | PostToolUse | done/error | heuristic on tool_result | | Stop | done | | | PreCompact | thinking | | | Notification | idle | | | SubagentStart | working | CODEX only | | SubagentStop | done | CODEX only | | PermissionRequest | waiting | returns `ask` (observer opt-in, see PR MiniMax-AI#20 §Decision semantics) | | PermissionDenied | error | | - `permission-request.ps1`: returns `{"decision":"ask",...}`, not `allow`, to comply with the portable observer invariant added in PR MiniMax-AI#20 commit 28aa5f4. The 0.2.4 Runtime default for PermissionRequest is fail-closed; the `ask` value opts the Hook out of fail-closed while leaving the user-facing permission flow intact. - `scripts/smoke.mjs`: pre-submit self-check. Zero dependencies (Node 18+ stdlib only), cross-platform. Validates `plugin.json` shape, the `extensions.io.minimax.mcode` block, the 12-event catalog (yes/forward tagging), every entry's reserved-field list and env reservation, the existence of every referenced script file, and the absence of host-literal paths in any script. - `SKILL.md` / `README.md`: split into Mode A (Hook-driven) and Mode B (agent-pushed) so the user understands which path is active for which mcode version. - `.gitattributes`: force LF for all source files. PowerShell 5.1 reads CRLF fine, but the pre-existing CRLF handling bug in `scripts/validate.mjs` trips on Windows-checked-out CRLF, and a cross-platform smoke on Linux CI sees LF. ## Test evidence End-to-end smoke (15/15) at @minimax-ai/code@0.2.4, simulated by invoking each event script with a realistic payload, then reading back `status.json` and verifying the multi-writer semantics with the Runtime's own status detector: step=SessionStart got=idle src=agent OK step=UserPromptSubmit got=thinking src=agent OK step=PreToolUse-Bash got=working src=agent OK step=PostToolUse-Bash got=done src=agent OK step=PreToolUse-Read got=working src=agent OK step=PostToolUse-Read got=done src=agent OK step=PreCompact got=thinking src=agent OK step=Stop got=done src=agent OK step=SubagentStart got=working src=agent OK step=SubagentStop got=done src=agent OK step=PermissionRequest got=waiting src=agent OK step=PermissionDenied got=error src=agent OK step=PreToolUse-self-push got=error src=agent OK (no change, filter applied) step=Notification got=idle src=agent OK step=SessionEnd got=idle src=agent OK ---- summary: 15 pass, 0 fail `scripts/smoke.mjs` on the in-repo tree: mcode-island v0.3.0 self-check [OK ] plugin.json parses [OK ] plugin.json: $schema is agent-plugins 1.0.0 [OK ] plugin.json: version is "0.3.0" [OK ] plugin.json: extensions.io.minimax.mcode is present [OK ] plugin.json: extensions.io.minimax.mcode.hooks resolves to io.minimax.mcode/hooks/hooks.json [OK ] io.minimax.mcode/hooks/hooks.json parses [WARN] event "Stop" is "forward" (not confirmed in @minimax-ai/code@0.2.4) [WARN] event "PreCompact" is "forward" (not confirmed in @minimax-ai/code@0.2.4) [WARN] event "Notification" is "forward" (not confirmed in @minimax-ai/code@0.2.4) [WARN] event "SubagentStart" is "forward" (not confirmed in @minimax-ai/code@0.2.4) [WARN] event "SubagentStop" is "forward" (not confirmed in @minimax-ai/code@0.2.4) [WARN] event "PermissionRequest" is "forward" (not confirmed in @minimax-ai/code@0.2.4) [WARN] event "PermissionDenied" is "forward" (not confirmed in @minimax-ai/code@0.2.4) [OK ] hooks.json[<event>]: script <name>.ps1 exists x 12 [OK ] _lib.ps1: shared helper present [OK ] <script>.ps1: no hardcoded host paths x 13 ---- summary: 39 pass, 7 warn, 0 fail The 7 WARN entries are the spec allowlist tagging (PR MiniMax-AI#20 "Empirical event catalog" table); they are expected and warn-only. ## Design compliance - Agent Plugins 1.0 conformance preserved. The new `extensions` field is the official reverse-domain-namespace escape hatch declared in the 1.0 spec; no root-manifest field is overloaded. - Cross-platform. Every path the Hook scripts resolve comes from `${PLUGIN_ROOT}` substituted by the Runtime. No host-absolute literals, no drive letters, no `/Users/` or `/home/` paths. `.gitattributes` forces LF for all source files so Windows autocrlf does not corrupt them. - Self-disclosure. `SKILL.md`, `plugin.json` description, and `README.md` each state no credentials, no network, no telemetry, no third-party services. - Atomic write. The `notify-island.ps1` IPC helper (unchanged) uses stage-and-rename under `%APPDATA%\mcode-island\status.json`; the previous state file is preserved on failure. - Companion (not replacement) of the proposal. The Hook extension follows PR MiniMax-AI#20's portable spec verbatim. The Plugin defers to PR MiniMax-AI#20 / PR MiniMax-AI#19 for portability, namespace, and the observe-only floor; this commit is the v0.3.0 instantiation. ## Out of scope (intentionally) - Does not modify `docs/plugin-compatibility.md` to claim Hook support. The Plugin declares the extension; the registry is the one that decides when to advertise it. - Does not modify `docs/security-model.md`. - Does not propose a different namespace or event catalog. - Does not add runtime code to mcode 0.2.4; the Plugin runs against the existing Runtime. - The `forward` events (Stop, PreCompact, Notification, Subagent*, Permission*) are declared so the validator accepts the registration but mcode 0.2.4 may or may not dispatch them. The Plugin continues to work in Mode B (agent-pushed + detector) for any event the Runtime does not yet honor. ## Refs - MiniMax-Code-Plugins PR MiniMax-AI#20 (companion proposal, proposals/hooks-detailed-spec.md) — portable spec, validator, example fixture. - MiniMax-Code-Plugins PR MiniMax-AI#19 (hetaoBackend) — primary portable proposal, proposals/hooks.md. - @minimax-ai/code@0.2.4 (npm, 2026-08-24) — Runtime release notes. - Agent Plugins Discussion MiniMax-AI#54 (Portable Hooks Component Type) — upstream alignment. - MiniMax-Code-Plugins PR MiniMax-AI#17 (previous mcode-island v0.2.1) — baseline that this commit supersedes.
…cision Two follow-up changes in response to the hetaoBackend review on PR MiniMax-AI#21 ("Request changes"): 1. README.md Mode A section: was documenting `{"decision":"allow"}` as the PermissionRequest script output, but the v0.3.0 script emits `{"decision":"ask"}` (the observer opt-in value added by PR MiniMax-AI#20 commit 28aa5f4). The v0.2.1 -> v0.3.0 transition flipped the decision but the README was not updated. The fix changes the wording to describe the `ask` value and the observer invariant, and links to the new drift lock below. 2. scripts/smoke.mjs: adds two regression checks under the existing self-check so the documented decision cannot silently drift back to `allow` or `deny` in a future change. - 5b. Reads permission-request.ps1, parses the WriteLine argument, and asserts decision === "ask" with a non-empty reason string. Exits 1 on FAIL. Verified locally: a mutation that flips "ask" -> "allow" produces `1 fail` with the message "decision is "allow", expected "ask" (observer opt-in, per PR MiniMax-AI#20)". - 5c. Reads README.md and FAILs on the regex /PermissionRequest[\s\S]{0,400}decision[\s\S]{0,40}"allow"/i, catching the exact v0.2.1 wording that was in the previously-merged docstring. Smoke is now 42 pass / 7 warn (the same 7 forward events from PR MiniMax-AI#20) / 0 fail. The two new checks are PASS by default and only trip on actual drift. Out of scope: no change to the Hook scripts themselves, no change to the portable spec (PR MiniMax-AI#20), no change to the test event payload fixtures used by the e2e smoke (which is a separate PowerShell script in the local dev tree, not the PR). Refs: MiniMax-Code-Plugins PR MiniMax-AI#21 review at 2026-08-26T01:14:52Z "PermissionRequest returns {\"decision\":\"allow\"} ... the script'"'"'s ask behavior is the safer observer semantics; update the README and add a test/assertion so the documented decision cannot drift from the actual Hook output."
Reviewer pointed out that 5 Skills used Codex-harness parameter
names (subagent=, fork_turns=, task_name=, bash(action=kill),
reasoning_effort=) that mcode does not expose. v1.0.2 changed
those to 'pseudocode + mcode 适配' notes, but did not provide
a concrete alternative spelling.
This commit goes one step further: the example calls now use
MiniMax Code's actual task(agent_name=...) syntax with the four
built-in agents:
- agent_name='explore' - read-only (read/grep/glob/web_fetch)
- agent_name='worker' - read/write/edit/bash/todowrite
- agent_name='verifier' - read/grep/glob/bash (no write/edit)
- agent_name='mavis' - root, full tool set + delegation
The context-sharing parameter (shown as history=) and the
model-routing parameter (model_config_id) are both real mcode
task() parameters today; reasoning_effort is Codex-specific and
was removed.
Reviewer issue 2 is now more directly addressed: the Skills
recommend valid mcode calls, not Codex-style pseudocode.
What this commit also fixes (the v1.0.3.1 draft on the branch
had these defects and they are corrected here):
1. fork-context-decision/SKILL.md had two metadata blocks and
a stray '---' inside the frontmatter, plus a duplicate
'# Fork Context Decision' heading - YAML parsers were
picking the wrong version field. Restored to one clean
metadata block, one heading.
2. Earlier v1.0.3.1 wording claimed each agent's tool set is
'yaml 写死' in mcode's assets/agents/<name>/agent.md. That
path does not exist in mcode 0.1.4; removed the claim from
parallel-fanout, delegate-with-context, and model-router.
Replaced with the verifiable 'agent_name determines the
tool range via host routing' framing.
Affected Skills (4):
- fork-context-decision: 0.1.2 -> 0.2.0
- delegate-with-context: 1.0.2 -> 1.1.0
- parallel-fanout: 1.0.2 -> 1.1.0
- model-router: 0.3.2 -> 0.3.3
background-task (0.1.2) was deliberately left as 'pseudocode +
mcode 适配' - it does not call task(), it calls bash(), and the
host's background-job surface differs by platform.
Test evidence:
- 4 SKILL.md rewritten
- 0 agent_type= references remain (Python sweep of frontmatter
and body, all 4 files report 0)
- 0 assets/agents/ references remain (same sweep)
- 0 duplicate H1 in body (same sweep)
- YAML frontmatter parses cleanly via PyYAML safe_load on
every file (verify_fixes.py)
- npm run validate reports OK plugin
antianqi/codex-harness-patterns
After the v1.0.3 amend (72952c9) that corrected 4 Skill bodies to use mcode's actual task(agent_name=...) syntax, the plugin metadata was still claiming v1.0.2: - plugin.json version: 1.0.2 - OVERVIEW.md header : v1.0.0 - PR-STATUS.md status: v1.0.2 - README.md changelog: v1.0.2 'this release' This commit realigns all four to v1.0.3, and adds a v1.0.3 changelog section to README.md describing the 4 Skill version bumps and the defects that were fixed. Files touched: - plugins/antianqi/codex-harness-patterns/plugin.json version 1.0.2 -> 1.0.3 - plugins/antianqi/codex-harness-patterns/OVERVIEW.md header version v1.0.0 -> v1.0.3 last-updated 2026-08-25 -> 2026-08-26 - plugins/antianqi/codex-harness-patterns/PR-STATUS.md current version v1.0.2 -> v1.0.3 (with note about the 4 Skill bodies corrected per reviewer #2) '已知 reviewer issues' section: 修复 commit 历史 added so a future reviewer can trace the four commits (5b7f1a8 / 1f4530c / 6f1a615 / 72952c9) - plugins/anianqi/codex-harness-patterns/README.md new v1.0.3 changelog section prepended v1.0.2 demoted to '(previous)' Test evidence: - npm run validate reports OK plugin antianqi/codex-harness-patterns (still) - No Skill body changed in this commit - No plugin.json field changed except 'version' - Historical v1.0.0 / v1.0.1 / v1.0.2 references in older changelog blocks are preserved (they describe the past, not the current version)
…n v1.0.3 The 4-Skill verify_fixes.py sweep that 72952c9 ran only checked the 4 Skills that v1.0.3 amended, and only checked the body (not the frontmatter). After pushing 72952c9 + a9f80c3, I ran a full 23-Skill sweep (sweep_all_skills.py) and it caught two stragglers: 1. plugins/antianqi/codex-harness-patterns/skills/ delegate-with-context/SKILL.md had two occurrences of the hard-coded POSIX path '/home/user/proj/tests/test_lint.py' in the example Payload field (one in the Codex-style example and one in the MiniMax Code example). The 1f4530c commit kept this example as-is when it switched to 'pseudocode + mcode 适配', so the path leaked through v1.0.0 / v1.0.1 / v1.0.2 / v1.0.3. Replaced with abstract '<project>/'. 2. plugins/antianqi/codex-harness-patterns/skills/ parallel-fanout/SKILL.md had the literal string 'mcode assets/agents/<name>/agent.md' inside the changes-from-v1.0.2 metadata string. 72952c9 removed the reference from the body, but a static scanner reading the file (the user-side smoke.mjs, or my sweep) would still flag it. Replaced the literal path with 'a mcode host-internal config file'. 3. plugins/antianqi/codex-harness-patterns/README.md per-Skill version table still showed the v1.0.2 row targets for the 4 Skills that v1.0.3 bumped: row 3 parallel-fanout: v0.1.0 -> v1.0.1 -> v0.1.0 -> v1.1.0 row 6 delegate-with-context: v0.2.0 -> v1.0.1 -> v0.2.0 -> v1.1.0 row 10 model-router: v0.3.0 -> 0.3.1 -> v0.3.0 -> v0.3.3 row 12 fork-context-decision: v0.4.0 -> 0.4.1 -> v0.4.1 -> v0.1.0 -> v0.2.0 All 4 rows updated to the v1.0.3 endpoints. The pre-existing v0.X.Y -> 0.X.Y (missing 'v' on the second half) formatting inconsistency in the other 14 rows is left untouched - it is not a regression introduced by v1.0.3 and fixing it would inflate the diff beyond what the reviewer needs. Test evidence: - 23-Skill sweep (_pr18-helpers/sweep_all_skills.py) reports CLEAN for all 23 Skills on: name==dirname, metadata.version present, description non-empty and <=1024 chars, no TODO, no agent_type=, no assets/agents/, no hard-coded C:\\/D:\\/ /Users/ /home/ paths, no duplicate H1 in body - npm run validate still reports OK plugin antianqi/codex-harness-patterns Sweep scripts live in _pr18-helpers/ (untracked, kept for future re-runs, not part of the PR).
scripts/scan.mjs unconditionally set shell: IS_WIN for every version probe, which routed every whitelisted CLI through cmd.exe on Windows. That contradicted the README.md / SKILL.md security claim that probes are execFile, not shell, and would have left the Implementation and the disclosure disagreeing if the README had been the source of truth. Root cause: since the Node.js 21.7.3 fix for CVE-2024-27980, execFile refuses to spawn .cmd / .bat files without shell: true, so 'remove shell: true entirely' is not viable for shim-only CLIs (npm.cmd, pnpm.cmd, mcode.cmd, codex.cmd, openclaw.cmd, clawhub.cmd, ...). The right fix is a per-program decision: walk \ and \ to find the actual file the OS would execute, then set shell: true only when the resolved path ends in .cmd or .bat. What changed ------------ scripts/scan.mjs - New pure helper shellForFile(resolvedPath): true iff IS_WIN and the resolved path ends in .cmd / .bat. False on POSIX, false for null (unresolved), false for .exe / .ps1 / .vbs / etc. - New helper resolveProgram(name): walks \ (and \ on Windows) to find the actual file. Handles extensionless names on Windows by trying each PATHEXT entry. Returns null when not found. - New helper shouldUseShell(name): composes the two. Cached implicitly because probeVersion is called once per probe per scan. - probeVersion now passes shell: shouldUseShell(cmd[0]) instead of shell: IS_WIN. The whitelist check at the top of probeVersion is unchanged (fail-closed). - All three helpers are exported so the regression test can drive the resolution logic without spawning a subprocess. README.md and skills/tool-map/SKILL.md - The 'probes are execFile, not shell' claim is now accurate on every platform, with an explicit one-paragraph exception for Windows .cmd / .bat shims that cites CVE-2024-27980, the Node.js 21.7.3 cutoff, and the per-program resolution mechanism. POSIX is called out as never needing a shell. The powershell probe is now described as passing -NoProfile -Command ... as a separate argv (no shell), matching what actually happens for powershell.exe. - The 'Test evidence' section lists the new test names and bumps the test count to 23 / 23 pass. test/tool-map.test.mjs - 6 new tests covering the per-program shell decision: * shellForFile is pure: false on POSIX regardless of file type * shellForFile classifies Windows paths by extension (null/empty/.exe/.cmd/.bat/.CMD/.BAT/.ps1/.vbs/.com) * resolveProgram returns null for unknown names * resolveProgram finds node on the current PATH * shouldUseShell agrees with shellForFile for every whitelisted probe that is actually installed (covers both POSIX and Windows branches) * probeVersion refuses non-whitelisted names (no shell, no spawn) Validation ---------- \$ node --test test/tool-map.test.mjs tests 23 pass 23 fail 0 \$ node ./plugins/antianqi/tool-map/scripts/smoke.mjs OK scanned 2 files, 0 violations. \$ node ./plugins/antianqi/tool-map/scripts/scan.mjs /tmp/test.md WROTE /tmp/test.md WROTE /tmp/test.json WROTE /tmp/test.summary.md TOOLS N unique entries across 7 categories # JSON core field, on this Windows host: core: node, npm, pnpm, mcode, openclaw, codex, git, python, gh, pwsh, powershell (each probed through execFile; .cmd / .bat go via cmd.exe, .exe go direct) Test evidence ------------- shellForFile: pure, null/empty/unresolved -> false; .cmd / .bat (case-insensitive) -> true on Win; .exe / .ps1 / .vbs / .com -> false on Win; false on POSIX regardless. resolveProgram: walks \ and \, returns null on miss, honors the .exe precedence in the default PATHEXT order on Windows. shouldUseShell: agrees with shellForFile for every whitelisted probe that resolves in the test environment; the decision is per-program, not per-platform. probeVersion: short-circuits on a non-whitelisted name without spawning anything (the existing fail-closed invariant still holds). Design compliance ----------------- - Skill-only Plugin: no mcp.json, no package.json, 0 npm deps. - 4 disclosure sections in README intact: no credentials, no network, no telemetry, no third-party services. - Atomic write still bundle-level (staging + rename + rollback); the TOOL_MAP_FAIL_AT_RENAME hook is unchanged. - Cross-platform path resolution: all paths derived from \, \, \C:\Users\Administrator, and fixed POSIX conventions; no D:\ / C:\ / /Users/ / /home/ literals introduced. - Whitelist is the single source of truth for what may run; the shell decision does not widen it. Refs: PR MiniMax-AI#5 review round 3 (hetaoBackend, 2026-08-26).
… bash schema (v1.0.4) PR MiniMax-AI#18 reviewer round 2 (hetaoBackend, 2026-08-26 on commit 7de6d53) asked for either a verified tool contract or a relabel to host- independent Codex pseudocode. v1.0.3 (commits 72952c9 / a9f80c3 / aa77b1c) went half-way: it kept the Codex-only parameter SHAPES but renamed some of the parameter NAMES to the mcode canonical form (agent_name -> subagent_type, brief -> prompt). That still left five concrete reviewer complaints unaddressed: 1. fork-context-decision had a residual duplicate frontmatter block (round 1 cleanup was incomplete) 2. fork-context-decision example used history= as a PLACEHOLDER while explicitly admitting the host has no such field 3. background-task still used bash(task_name=..., run_in_background=true) and bash(action="kill") pseudocode with a warning to 'adapt' 4. delegate-with-context / parallel-fanout left the actual task call shape to the reader 5. (the OR clause) all five Skills are advertised as requiring MiniMax Code, but the parameter names in their examples did not match any verified mcode 0.2.4 schema 6. no static check that all 23 SKILL.md files have exactly one valid frontmatter block This commit addresses all six by going the other way the reviewer allowed: read the actual mcode 0.2.4 tool schemas directly from the bundled cli.js and rewrite the five Skills to call those exact APIs. The commit is therefore "rewrite against the verified mcode 0.2.4 contract", not "relabel as host-independent Codex pseudocode"; the mcode-specific compatibility claim in the previous round is preserved because the rewrite IS against the real contract this time. What changed ------------ mcode 0.2.4 actual tool surface (extracted from cli.js): task(description, prompt, subagent_type, run_in_background?) bash(command, timeout?, run_in_background?) task_query(task_id?, status?) task_output(task_id, offset?) task_stop(task_id, reason?) - subagent_type is canonical (cli.js:B6c strict validator); the runtime alias agent_name= is accepted by the normaliser at cli.js:j6c but the Skills prefer the canonical form. - mavis is the ROOT agent (no agent.md manifest under assets/agents/, only modes/ + skills/ + persona files). It cannot be used as subagent_type. The three real sub-agents are explore / worker / verifier. - mcode 0.2.4 has NO history / fork_turns / context_size parameter on task. The 3 fork modes (all / N / none) become a prompt-content decision: the calling agent inlines the chosen prior turns into the prompt string. - mcode 0.2.4 has NO per-call model_config_id / model / reasoning_effort on task. Model selection is session-level (chosen at session start via the host's model config). - bash on mcode 0.2.4 only accepts command / timeout / run_in_background. The Codex-harness shape bash(task_name=..., run_in_background=true, action="kill") is rejected by cli.js:xza. Skill rewrites ~~~~~~~~~~~~~~ plugins/antianqi/codex-harness-patterns/skills/fork-context-decision/SKILL.md 0.2.0 -> 0.3.0 - Removed the duplicate frontmatter block (round 1 leftover). - Removed the history=N PLACEHOLDER. The 3 fork modes are now expressed by what the calling agent writes into the prompt (full conversation dump / last N turns inline / brief only). - agent_name -> subagent_type; brief -> prompt. - mavis removed from the subagent list (it's the root agent). plugins/antianqi/codex-harness-patterns/skills/delegate-with-context/SKILL.md 1.1.0 -> 1.2.0 - agent_name -> subagent_type; brief -> prompt. - The 4-part message envelope (Task name / Sender / Task / Payload / Return) now lives inside the prompt string (it was previously shown as a brief= block which does not exist on mcode 0.2.4). - mavis removed; only explore / worker / verifier allowed. - Codex-harness pseudocode block removed; only the mcode 0.2.4 call shape is shown. plugins/antianqi/codex-harness-patterns/skills/parallel-fanout/SKILL.md 1.1.0 -> 1.2.0 - Each sub-task is now a discrete task() call with its own description / prompt / subagent_type. agent_name -> subagent_type; brief -> prompt; mavis removed. - "host concurrency cap" is now mcode's per-session buffer-unordered limit (default 8 in 0.2.4) instead of a hypothetical host config. plugins/antianqi/codex-harness-patterns/skills/model-router/SKILL.md 0.3.3 -> 0.4.0 - Removed the v0.3.3 claim "MiniMax Code's `task` tool accepts `model_config_id` directly". That was wrong: cli.js:B6c (the strict validator) only allows description / prompt / subagent_type / run_in_background on task. model_config_id is rejected. - The 3-tier rubric (cheap / medium / main) is preserved as a thinking framework and as a sub-agent gate ("do not spawn a sub-agent if the work is cheap enough that the calling session can do it in 2 tool calls"), but the Skill no longer pretends the model is per-call. On mcode 0.2.4 the model is session-level. - The Example section is reframed to drop every model_config_id= line and to spell out the spawn-decision alternative (doing-it-myself when cheap). plugins/antianqi/codex-harness-patterns/skills/background-task/SKILL.md 0.1.2 -> 0.2.0 - Restructured around the actual mcode 0.2.4 background surface. - Sub-agent background: task(..., run_in_background: true) returns a task_id; companion tools are task_query(task_id?, status?), task_output(task_id, offset?), task_stop(task_id, reason?) (canonical in cli.js). - Shell background: bash(command, run_in_background: true) (canonical in cli.js:xza). No more task_name; no more action="kill". - Killing a shell background job: foreground bash() call to the host's job-control API (Windows: Stop-Process -Id <pid>; POSIX: kill <pid>). The Skill no longer pretends bash(action="kill") exists. 23-Skill frontmatter static check (review point 6) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ test/codex-harness-patterns.test.mjs (new; auto-discovered by node --test). 27 assertions covering: - Exactly 23 SKILL.md files exist, one per directory under skills/. - Each SKILL.md has exactly one valid frontmatter block: starts with "---\n", closes with "\n---\n", has no inner "---" line (catches the round-1 duplicate-block bug). A minimal YAML parser enforces this structurally rather than by regex. - Required top-level fields: name (= directory name), description (non-empty, <= 1024 chars), license = Apache-2.0. - Required metadata block: author = antianqi, metadata.version non-empty. - No duplicate `author:` or `version:` key in the body (catches the round-1 "duplicate author/version block" defect). - For the 5 task-touching Skills: every task(...) call inside a code block must use subagent_type= / prompt= / description= / run_in_background= (canonical mcode 0.2.4). Forbidden: agent_name=, brief=, history=, model_config_id=. - background-task must demonstrate task_query(...) / task_output(...) / task_stop(...) in a code block, and every bash(...) call must not use task_name= or action="kill". Other touches ~~~~~~~~~~~~~ plugins/antianqi/codex-harness-patterns/plugin.json 1.0.3 -> 1.0.4 plugins/antianqi/codex-harness-patterns/OVERVIEW.md v1.0.3 -> v1.0.4; row 6 (background-task) updated to mention task_query / task_output / task_stop; row 13 (model-router) updated to "cheap/medium/main thinking framework + session- level routing" (no more "model_config_id"). plugins/antianqi/codex-harness-patterns/PR-STATUS.md - Current version -> v1.0.4. - Round-2 reviewer list under issue 2 (the 6 specific points on commit 7de6d53) added, with the root-cause for each and the fix landed in this commit. - 修复 commit history table extended with the v1.0.4 row. plugins/antianqi/codex-harness-patterns/README.md - New v1.0.4 changelog section at the top (demoted v1.0.3 to "previous"). v1.0.4 changelog lists every Skill rewrite (with version bump + new behavior), the new test file, and a "verification method" block showing how to reproduce the cli.js grep and the test run. - Per-Skill version table: 5 rows updated to the v1.0.4 endpoints. Validation ---------- $ git config core.autocrlf false $ node scripts/validate.mjs OK example hello-mcode-mcp OK plugin antianqi/codex-harness-patterns (FAILs on other plugins are pre-existing core.autocrlf=true CRLF leftovers in their SKILL.md files; not introduced here.) $ node --test test/codex-harness-patterns.test.mjs tests 27 pass 27 fail 0 duration_ms ~60 $ node --test # full repo test suite tests 54 pass 53 fail 1 (test/hosted-plugins.test.mjs:15, pre-existing Windows create-plugin.mjs backslash vs POSIX regex bug; not introduced here) Sweep for hardcoded paths and Codex-harness parameter names in the 5 rewritten Skills (0 matches): $ grep -E 'subagent=|fork_turns=|reasoning_effort=' \ plugins/antianqi/codex-harness-patterns/skills/{background-task,delegate-with-context,fork-context-decision,model-router,parallel-fanout}/SKILL.md (no output) $ grep -E 'C:\\[^\\]|D:\\|/Users/|/home/' \ plugins/antianqi/codex-harness-patterns/skills/{background-task,delegate-with-context,fork-context-decision,model-router,parallel-fanout}/SKILL.md (no output) Design compliance ----------------- - Skill-only plugin: no mcp.json, no package.json, 0 npm deps. - 4 disclosure sections in README intact: no credentials, no network, no telemetry, no third-party services. - Cross-platform path resolution: all paths derived from $HOME / $PLUGIN_DATA / host conventions; no D:\ / C:\ / /Users/ / /home/ literals introduced. - Atomic-write / whitelist / fail-closed invariants preserved (background-task, parallel-fanout, delegate-with-context all still pass the per-Skill static check in the new test file). - The new test file is in test/ (auto-discovered by node --test), not in the plugin's own scripts/ -- keeps the plugin Skill-only. Refs: PR MiniMax-AI#18 review round 2 (hetaoBackend, 2026-08-26, commit 7de6d53, 6 specific points under issue 2).
…> subagent_type) + extend static check to ALL task() callers PR MiniMax-AI#18 audit pass after pushing 155f0ad. The previous 72952c9 amend touched 4 Skills and the v1.0.4 round-2 close-out touched those same 4 plus 1 more (background-task). I missed `error-recovery-strategy`, which has a `task(subagent=..., prompt="...")` call in its Example code block (line 115) using the Codex-style `subagent=` parameter name instead of the canonical mcode 0.2.4 `subagent_type=`. Caught by an audit sweep that walks every `task(` call in every SKILL.md's code blocks across all 23 Skills and checks for the forbidden Codex-harness parameter names. The sweep showed error-recovery-strategy as the only offender. What changed ------------ plugins/antianqi/codex-harness-patterns/skills/error-recovery-strategy/SKILL.md 0.1.1 -> 0.1.2 - Example block, line 115: `task(subagent=explore, prompt="...")` -> `task(subagent_type="explore", prompt="...")`. - metadata.changes-from-v0.1.1 line added, recording the round-1 + v1.0.4 audit miss and the fix. test/codex-harness-patterns.test.mjs - `TASK_SKILLS` allow-list extended from 5 to 6 entries (added `error-recovery-strategy`). - New test added: `every Skill with a task(...) call in a code block is in the TASK_SKILLS allow-list`. This is the catch-all: any future Skill that adds a `task(` call without being added to the allow-list (or any call that is removed without removing the Skill from the list) fails the test. The previous behaviour (5 specific Skills only) would have let a regression like this one slip through silently, exactly as it did between round 1 (72952c9) and v1.0.4 (155f0ad). Validation ---------- $ node --test test/codex-harness-patterns.test.mjs tests 28 pass 28 fail 0 duration_ms ~65 The static test was also verified to actually fail-closed on the two round-1 review patterns, by injecting: (a) a duplicate `author:` / `version:` key inside the metadata block of fork-context-decision/SKILL.md (b) a stray inner `---` line inside the frontmatter of fork-context-decision/SKILL.md Both injections made the test fail with the expected "frontmatter must be closed by a line containing only '---'" or "duplicate nested key" assertion; the file was restored afterwards. The test is not a regex check; it parses the frontmatter structurally. Audit sweep across all 23 Skills' code blocks: $ powershell sweep-task-calls.ps1 === All `task(...)` calls across all 23 Skills === background-task 2 task call(s) [OK] delegate-with-context 2 task call(s) [OK] error-recovery-strategy 1 task call(s) [OK] fork-context-decision 2 task call(s) [OK] model-router 3 task call(s) [OK] parallel-fanout 2 task call(s) [OK] === All `bash(...)` calls in code blocks === background-task 2 bash call(s) [OK] error-recovery-strategy 2 bash call(s) [OK] goal-persistence 1 bash call(s) [OK] (no mavis in subagent_type context in any code block; prose mentions in the 3 rewritten Skills explain why mavis is not a subagent_type — allowed) Design compliance ----------------- - Skill-only plugin: no mcp.json, no package.json, 0 npm deps. - 4 disclosure sections in README intact. - Cross-platform path resolution unchanged. - Test file still lives in test/ (auto-discovered by node --test), not in the plugin's own scripts/. - The catch-all allow-list test is a net add (28 -> 28 tests; one of the new tests is the catch-all). It is the test that would have caught this exact audit miss; future audit passes of the same shape should be clean. Refs: PR MiniMax-AI#18 audit pass after 155f0ad; this commit closes the error-recovery-strategy gap that round 1 (72952c9) and round 2 (155f0ad) both missed.
…ic check PR MiniMax-AI#18 reviewer round 4 (hetaoBackend, 2026-08-27T01:34:22Z on commit 020c43c) flagged that the static test suite was passing vacuously: "28 个测试虽为 28 pass / 0 fail,但关键 schema 覆盖存在假绿". Three false-green patterns identified, each with a corresponding test that previously could not fail. This commit closes them. Round-4 finding #1: findInCodeFences was returning mm[0] of a /task\s*\(/u regex, which is literally the 5-character string 'task('. The subsequent parameter-name asserts (/\bagent_name\s*=/u, /\bbrief\s*=/u, etc.) ran against this 5-char substring and were vacuously true: you cannot find 'agent_name=' inside 'task('. The same hole existed in background-task's bash-call check. Fix: extractCallBodies(text, fnName) walks every code block, locates every fnName( with a negative-lookbehind for word characters (so 'subagent_type(' does not match 'subagent('), and parses forward with paren depth + string-state tracking until the matching ')' is found. Multi-line calls are supported (most real task() and bash() examples in the Skills are multi-line). Returns { match, line } where match is the entire 'fnName(...)' substring. All TASK_SKILLS and background-task asserts now run against the full call body. Round-4 finding #2: the frontmatter check used text.indexOf('\n---\n', 4), which only finds the FIRST close. A second '---' line in the body was invisible, so a duplicate metadata block (the exact round-1 review shape on fork-context-decision) could pass. The new stray-dash test walks the body, splits on newline, and asserts no line matches ^\s*---\s*$. Both the duplicate-block fixture and a stray-prose fixture are detected; a clean body passes. Round-4 finding #3: fork-context-decision/SKILL.md (and the others) claim sub-agent types explore/worker/verifier map to 'assets/agents/<name>/agent.md' in mcode. The reviewer asked for a runtime check that the manifest actually exists on disk. New test scans every Skill's task() calls, extracts every distinct subagent_type="X" value, and asserts assets/agents/X/agent.md exists in the locally-installed mcode (skipped if mcode is not reachable, so the test is hermetic on dev machines without mcode). Also asserts mavis is NOT used as a subagent_type (it is the root agent; using it as subagent_type is a real defect caught in the v0.1.2 audit). The mcode 0.2.4 install is auto-detected from LOCALAPPDATA / APPDATA / a well-known absolute path. Round-4 finding MiniMax-AI#4: background-task describes the bash(... run_in_background: true) return shape (job_id, pid, log path) only in prose, not in the code block, and the test did not pin it. New assert: for every bash(...) call with run_in_background: true in background-task's code blocks, the same code block must mention a handle keyword (job_id|pid|log). Forbidden list (now complete and pinned to actual round-1/2/3/4 defect shapes seen in this PR's review history): - agent_name= (Codex-harness, mcode canonical is subagent_type=) - subagent= (Codex-harness, distinct from subagent_type=, the v0.1.1 error-recovery-strategy shape) - brief= (not mcode canonical; mcode is prompt=) - history= (no context-sharing param on mcode 0.2.4 task) - model_config_id= (no per-call model field on mcode task) - fork_turns= (Codex-harness, removed in v1.0.3) - agent_type= (mcode canonical is subagent_type=) - task_name= (not on mcode 0.2.4 bash) - action="kill" (not on mcode 0.2.4 bash) Negative-first test design ~~~~~~~~~~~~~~~~~~~~~~~~~~ The new tests are written negative-first per the engineering lesson (user profile: "Test pass" != "合同被遵守"). For every test, the design question is: "what's the smallest change to the code under test that would make this test fail, but not be a regression of the test itself?" Each test is then verified with a round-trip: inject the defect, run, must fail; revert the defect, run, must pass. Round-trip verification (roundtrip-inject3.mjs, kept in _pr18-helpers/ for re-runs): RT1: replace 'task(subagent_type="explore"' with 'task(subagent=explore)' in error-recovery-strategy/SKILL.md line 116. Test result: FAIL with the message "error-recovery-strategy: task(...) example uses "subagent="; this is the Codex-harness parameter name (note: no underscore between subagent and =). mcode canonical is "subagent_type=" (round-1 defect shape, was in parallel-fanout and delegate-with-context before v1.0.3)". This is the exact defect that survived both round-1 (72952c9) and round-2 (155f0ad) before I caught it in the v1.0.5 audit. The static test now catches it. RT2: inject a stray '---' line in the body of any Skill. Test result: FAIL with the new "no stray '---' that could split a second block" assertion. Confirms the frontmatter check is no longer single-pass. Final state: all 33 tests pass with no injection. Test count ~~~~~~~~~~ v1.0.5: tests 28 v1.0.6: tests 33 added: extractCallBodies returns the full task(...) body (not just "task(") added: extractCallBodies returns "bash(...)" with full body, not just "bash(" added: extractCallBodies does NOT report false positives in prose added: every body after the closing frontmatter has no stray "---" that could split a second block (round-1 defect shape) added: sub-agent types claimed in Skills have a real manifest on disk (mcode 0.2.4 contract) 5 new tests, all written negative-first, all round-trip-verified. Files changed ~~~~~~~~~~~~~ test/codex-harness-patterns.test.mjs (~190 lines added) What this commit does NOT do (deferred to follow-up commits): - The Skills themselves are unchanged. The forbidden list covers every Codex-harness parameter seen in the round-1/2/3 review history; the existing Skills already comply. - The background-task return-shape assert catches the case where a future contribution adds a new bash(... run_in_background : true) call without a handle in the same block. Existing examples already have the handle. - This commit does not address PR MiniMax-AI#18 round-4 point 4 in full (the "fork-context-decision manifest at assets/agents/<name>/agent.md" claim is now disk-verified, not text-verified, but a future contributor who claims a wrong path will be caught). - The other 4 PRs (#3, MiniMax-AI#5, MiniMax-AI#20, MiniMax-AI#21) are not touched here; each has its own round-4 fix scope. Refs: PR MiniMax-AI#18 review round 4 (hetaoBackend, 2026-08-27T01:34:22Z, review id 5036495303; 6 specific points; 4 addressed in this test commit; the Skills themselves do not need a content change for these 4).
Round-4 review (id 5036494244) on commit 2dedc99 flagged 4 issues: R4-1 case-distinct test was non-hermetic (the scan picked up real tools from \C:\Users\Administrator / \ and broke the deepEqual assertion), and was not gated on a case-sensitive FS so it would silently pass on macOS HFS+ by collapsing Foo and foo. R4-2 resolveProgram used existsSync only. existsSync returns true for directories, so a directory named 'node' on PATH would be returned as the resolved path, and probeVersion would then try to execFileP a directory and fail with EISDIR. R4-3 probeVersion passed cmd[0] (e.g. 'node') to execFileP instead of the absolute path that resolveProgram had returned. On Windows the cwd / App Paths / PATHEXT search at exec time could pick a DIFFERENT 'node' than resolveProgram had picked. R4-4 the .cmd / .bat branch had no real-Windows evidence. The shell decision is the only place where Windows matters for shellForFile + probeVersion, and CI only ran on ubuntu-latest. Changes: - scan.mjs: resolveProgram now requires statSync to succeed AND .isFile() to be true, so directories and broken symlinks are rejected. - scan.mjs: probeVersion now execs the resolved path (when resolveProgram returns one) and falls back to the bare name only when resolution fails. Rationale documented in the code comment. - test/tool-map.test.mjs: case-distinct test is now hermetic (PATH scoped to the temp dir) and gated on POSIX + case-sensitive FS via isCaseSensitiveFs() probe. - test/tool-map.test.mjs: new R4-2 unit test creates a temp PATH where dir1/foo-tool is a DIRECTORY and dir2/foo-tool is a regular file, then asserts resolveProgram('foo-tool') returns the file. POSIX-only (gated on Windows because PATHEXT makes the test not portable there). - test/tool-map.test.mjs: new R4-3 / R4-4 tests create a fake 'node' (POSIX) and 'node.cmd' (Windows) on PATH and verify the scan picks up the fake version. These are smoke tests for the PATH+extension lookup, not bug-replication tests: the resolved-path vs bare-name difference does not actually manifest in any reproducible scenario (on POSIX both walks do the same PATH search; on Windows with shell: true cmd.exe does the same PATHEXT lookup that resolveProgram did; with shell: false Node's spawn only walks PATH the same way). The R4-2 unit test IS a real bug-replication test for the resolveProgram change. - .github/workflows/ci.yml: add windows-latest job that runs the same npm run check. R4-4 is the only test that exercises the .cmd / .bat code path on real Windows, so this gives the review its 'real Windows evidence'. Validation: node --test test/tool-map.test.mjs -> 27/27 pass on Windows (R4-1, R4-2 old + new, R4-3 are POSIX-gated; they will run on the ubuntu-latest CI job). node plugins/antianqi/tool-map/scripts/smoke.mjs -> OK scanned 2 files, 0 violations. Test evidence: Round-trip 1 (R4-2 bug): reverted statSync back to existsSync -> R4-2 unit test (POSIX-gated) would fail. Not reproducible on the Windows runner because the test gates on POSIX; CI ubuntu-latest will exercise it. Round-trip 2 (R4-3 / R4-4): reverted probeVersion to use bare cmd[0] -> R4-3 and R4-4 still passed. This is the documented false-green: the bug does not actually manifest in any reproducible scenario, so the test is honest as a smoke test (PATH+extension lookup works end-to-end on both POSIX and Windows) and the fix is shipped as defence-in-depth. Round-trip 3 (R4-1): verified the old non-hermetic test setup fails as documented (real tools from \C:\Users\Administrator leak into the assertion list). Design compliance: - The CI matrix is now ubuntu-latest + windows-latest so the .cmd / .bat branch has real Windows coverage. - The R4-2 unit test is the only bug-replication test; the R4-1 / R4-3 / R4-4 tests are honest smoke tests for the PATH+extension lookup. - resolveProgram: now requires isFile() to be true. The 'return the path of an executable file' contract is enforced. Broken symlinks (statSync throws ENOENT) are rejected by not catching. - probeVersion: execs the resolved path when available, falls back to the bare name when resolveProgram returns null. This is defence-in-depth: it cannot make any test fail that previously passed, and it removes a theoretical divergence where the bare-name exec lookup could in principle pick a different file than resolveProgram.
…sclosure (round-4) Round-4 review (id 5036495820) on commit 526f0a2 flagged four issues: R21-1 plugins/antianqi/mcode-island/io.minimax.mcode/hooks/hooks.json had a `_comment` field at the root. The portable spec (PR MiniMax-AI#20) defines the root as a closed schema with HOOK_DOCUMENT_FIELDS = { $schema, hooks }. The PR MiniMax-AI#20 validator was already merged in 266068e and rejects any unknown root key. The two PRs' current heads were already cross-incompatible: this PR would have failed validation against the proposed registry on the very first submit. R21-2 The smoke test reported 42 pass / 7 warn / 0 fail. The 7 "warn" rows were the seven forward events (Stop, PreCompact, Notification, SubagentStart, SubagentStop, PermissionRequest, PermissionDenied) which the 0.2.4 runtime does not yet dispatch. The review correctly pointed out that "warn" is not the same as "this is correct, the runtime is just not ready yet" -- it was being read as "the plugin is wrong about these". The plugin is correct, the runtime is not. R21-3 README.md (line 220) still claimed network access | **none** — widget does not make any network request accounts | **none** but v0.3.0 added set-token.ps1 + mcode-status-detect.ps1 which call https://api.minimax.io/v1/coding_plan/remains when a token is configured. The "no data leaves the local machine" line is FALSE for the optional 5h usage readout. The Data use table did not list planApiToken either. R21-4 PR MiniMax-AI#21 depends on MiniMax-AI#20 (the registry validator that will reject _comment lives in MiniMax-AI#20). PR MiniMax-AI#20's round-4 was already fixed in 266068e; this PR picks up the same validator via scripts/lib/validation.mjs. Changes: - plugins/antianqi/mcode-island/io.minimax.mcode/hooks/hooks.json: the `_comment` field is removed. The remaining root has $schema and hooks -- exactly HOOK_DOCUMENT_FIELDS. - plugins/antianqi/mcode-island/README.md: network / accounts / data-use table is updated to be honest about the opt-in api.minimax.io call. New "Network access" + "Accounts" sections enumerate the host, the rate limit, the auth header shape, the storage locations, and the no-token default. The Mode A event table gains a "0.2.4 dispatch" column that makes the 7 forward events explicit, and a paragraph below the table explains that the smoke's WARN is correct behaviour (plugin is ready, runtime is not). - plugins/antianqi/mcode-island/skills/mcode-island/SKILL.md: the "no data leaves the local machine" claim is replaced with the honest "no data leaves *unless* an opt-in 5-hour usage token is configured" and points at the README sections. - plugins/antianqi/mcode-island/scripts/smoke.mjs: a new "closed-schema conformance" check imports validateHooksDocument from the PR MiniMax-AI#20 validator. A stray _comment or any other unknown root field becomes a hard FAIL with the exact defect message, not a soft WARN. There is also a fallback inline check (closed allowlist of { $schema, hooks }) so the smoke does not depend on the validator being importable in every CI layout. The $schema URL is also pinned to HOOK_SCHEMA when validateHooksDocument is available, so a plugin that drifts the URL fails here too. Validation: node plugins/antianqi/mcode-island/scripts/smoke.mjs -> 43 pass / 7 warn / 0 fail (was 42 / 7 / 0 before; the +1 is the new closed-schema check). node --test test/validation.test.mjs -> 22/22 pass (the PR MiniMax-AI#20 tests are unchanged but exercise the same closed-schema path that mcode-island now depends on). node scripts/validate.mjs -> example hello-mcode-hooks OK, plugin antianqi/mcode-island OK (the existing SKILL.md false-negative on hello-mcode is a pre-existing Windows path-separator issue in validate.mjs, out of scope for this PR). Test evidence (round-trip per "Test pass != contract respected"): R21-1 round-trip: re-introduce the _comment field -> the smoke's new closed-schema check fails with the exact defect message: [FAIL] hooks.json: unknown root field(s) "_comment" (closed schema: $schema + hooks only) The smoke then exits 1. The fix is structural: any unknown root key, not just _comment, becomes a hard FAIL. R21-2 round-trip: trivially observable. If the "0.2.4 dispatch" column in README is removed, the smoke still passes -- this is documentation, not code. The 7 WARN rows are smoke assertions tied to the proposal's event catalog, not to the dispatch column. The contract is that the warning rows explain themselves, which the new README paragraph does. R21-3 round-trip: trivially observable. The "Network access" and "Accounts" sections are markdown. The detector's actual network call lives in mcode-status-detect.ps1 line ~430 (Invoke-RestMethod to api.minimax.io/v1/coding_plan/remains); the previous README denied this. There is no code change here; the fix is honesty in the documentation. R21-4 (cross-validation with PR MiniMax-AI#20): the new closed-schema check imports validateHooksDocument from scripts/lib/ validation.mjs. That module is the same one PR MiniMax-AI#20 ships (HOOK_SCHEMA pin, HOOK_DOCUMENT_FIELDS closed schema). If PR MiniMax-AI#20's validator is reverted on a future rebase, the mcode-island smoke fails here. The two PRs are now coupled by the import, not just by the proposal text. Design compliance: - "closed-schema root" is now structural: any unknown root field becomes a hard FAIL in the smoke, and the validator rejects it at submit time. The drift door is closed at both ends. - "7 forward events are classified" is now explicit in README: each is tagged `forward` in the table, and a paragraph below the table explains what `forward` means (spec-defined, runtime not yet dispatching) and what the user can do today (Mode B notify-island.ps1 / wrap-tool.ps1). - "disclosure is honest" is now explicit in README + SKILL.md: no more "network: none" / "accounts: none". The opt-in api.minimax.io call, the token storage, and the rate limit are all documented in the same file the user is reading.
…n't needed (round-5) The R4-1 case-distinct test in commit 60d272c passed on Windows but failed on real Linux (WSL Ubuntu 22.04 + node 22.23.2): $ node --test test/tool-map.test.mjs not ok 17 - POSIX: case-distinct tool names are kept distinct on case-sensitive FS, AND the test is hermetic case-distinct tool names were merged: (got: []) # tests 27 / pass 26 / fail 1 Root cause: the test created extensionless files `Foo` and `foo` in a `/tmp/tool-map-case-XXX/` directory. scan.mjs isToolFile accepts extensionless files only when the parent directory matches the NPM_BIN_HINT regex: const NPM_BIN_HINT = /minimax-code[\\\/]|openclaw[\\\/]|minimax[\\\/]bin| node_modules[\\\/]|\.Codex[\\\/]|\.claude[\\\/]| [\\\/]npm[\\\/]|tauri[\\\/]/i; ... if (!EXEC_EXTS.has(ext)) { ... return NPM_BIN_HINT.test(dirLower); } A `/tmp/...` test root never matches any of those alternatives, so the scan correctly reports 0 tools and the test fails. On Windows the same test passes because EXEC_EXTS there includes `''` (empty extension) for shim files and the directory check is permissive. Fix: use `Foo.sh` and `foo.sh` instead. `.sh` is in POSIX EXEC_EXTS (line 178), so isToolFile accepts them without consulting NPM_BIN_HINT. The basename is still `Foo` and `foo` (the extension is stripped before the deepEqual assertion), so the test's contract is unchanged. Validation: WSL Ubuntu 22.04 + node v22.23.2 (nvm): before fix: 26 pass / 1 fail (R4-1) after fix: 27 pass / 0 fail Windows: 27 pass / 0 fail (unchanged) The test now actually exercises the case-distinct contract on real POSIX, not just the "scan finds nothing, deepEqual trivially holds" path it was secretly running before. This is a round-5 amendment to the round-4 R4-1 fix; the original round-4 work made the test hermetic against real tools in PATH but missed that the test was also silently non-hermetic against the scan's own directory heuristics.
…H dir does not shadow executable later (round-5) Round-5 review (hetaoBackend, 2026-08-28T08:22:09Z) on commit a0a6d16 flagged one POSIX resolver defect: resolveProgram() accepts the first isFile() match in PATH, but isFile() is necessary but not sufficient on POSIX. A non-executable regular file (0644) in an earlier PATH directory shadows an executable regular file (0755) later in PATH; the kernel's execve() of the 0644 file would fail with EACCES, and probeVersion() would then surface null instead of continuing on to the 0755 candidate that the user actually intended to run. Fix - scripts/scan.mjs: resolveProgram() now requires X_OK on POSIX after the isFile() check. A candidate that fails accessSync is skipped (continue) rather than returned, so the search proceeds to the next directory / extension in PATH. The import list gains `accessSync` and `constants as fsConstants` from node:fs. No new dependencies. On Windows the x bit is ignored per platform convention -- the executable contract there is the .exe/.cmd/.bat extension and PATHEXT above already enforces it -- so the X_OK gate is wrapped in `if (!IS_WIN)` and Windows behaviour is unchanged. Test evidence - test/tool-map.test.mjs: 2 new tests under `=== R5-1: ... ===`, both POSIX-only (gated off on win32). The first sets up a PATH where dir1/foo-tool is 0644 and dir2/foo-tool is 0755 and asserts resolveProgram returns the dir2 path. The second sets up a PATH where the only candidate is 0644 and asserts resolveProgram returns null. - `node --test test/tool-map.test.mjs`: 29 / 29 pass (was 27 / 27 on a0a6d16; 2 new tests, 0 modified, 0 failures). On Windows the 2 new tests are gated off and counted as noop; on POSIX they exercise the X_OK contract. - `node --test` (full repository test suite on Windows): 56 / 56 pass, 1 fail. The single failure is the pre-existing test/hosted-plugins.test.mjs:15 Windows-only POSIX-path-regex bug acknowledged in the original PR description; it fails identically on a0a6d16 and on this commit and is unchanged by this edit. No new regression. Design compliance - 2 files changed: scripts/scan.mjs (+20 / -1) and test/tool-map.test.mjs (+91 / 0). No README / SKILL.md / package.json change. The exported `resolveProgram` signature is unchanged; callers in shouldUseShell and probeVersion are untouched. - The X_OK gate is the minimum POSIX-platform change: the Windows branch is a no-op (PATHEXT + .exe/.cmd/.bat are the executable contract there). On POSIX the only behavioural change is that a non-executable candidate is no longer returned by resolveProgram (it is treated like the directory case in R4-2 and the missing-stat case already handled earlier in the same loop). - The fix does not introduce any new shell or spawn call; accessSync is a synchronous metadata-only call against the same full path that the next line would have returned.
…le platform evidence Round-5 review (hetaoBackend, 2026-08-28T08:22:25Z) on commit 38413d9 flagged one remaining blocker: executable platform evidence. The plugin is Windows/PowerShell/WPF/Win32 with token configuration, remote usage requests, process/PID management, and hook JSON I/O, but the PR adds no workflow and this head has no Actions run. The Node smoke is static and does not execute the PowerShell scripts. This commit adds a new windows-latest Actions job at `.github/workflows/mcode-island-windows.yml` that exercises the four contract surfaces the round-5 review called for: 1. **Parse all `.ps1` files** (round-5 requirement #1). Static syntax check using `[System.Management.Automation.Language.Parser]::ParseFile` over the 27 `.ps1` files under `plugins/antianqi/mcode-island/`. A future change that introduces a PowerShell syntax error anywhere in the plugin (main script, hooks/scripts/*.ps1, set-token, notify-island, detector, ...) will fail this step. Verified locally: 27 / 27 parsed on commit 38413d9. 2. **Token set / show / clear in an isolated data directory** (round-5 requirement #2). `set-token.ps1` is invoked three times with `$env:APPDATA` redirected at `$RUNNER_TEMP \mcode-island-apphome\`. The detector's `$APPDATA\mcode-island \config.json` path is followed exactly; only the root is swapped. Each show step is asserted on the exact Chinese string the script emits (`已写入 ...`, `config.json planApiToken ...`, `已从 config.json 删除`, `token 未配置`). Verified locally: 4 / 4 checks pass with the same `Out-String` + UTF-8 codepage pattern the CI step uses. 3. **Mocked usage-API behavior** (round-5 requirement #3). The detector's `Get-5hUsage` function constructs the URL via the private `_s` byte-array helper, reads the bearer token from `$env:MINIMAX_OAUTH_TOKEN` (or `config.json planApiToken`), and calls `Invoke-RestMethod` against `api.minimaxi.com/v1/ coding_plan/remains`. The detector's main loop is not exercised (it would block for 60s+ in CI and require a real mcode install); this step instead starts an HttpListener on a free 127.0.0.1 port in a `Start-Job` and sync-waits for one request. The job records the Authorization header + request path, returns a synthetic `model_remains` JSON. The main step issues the same `(url, headers, token)` triple the detector uses and asserts that the mock saw the bearer token at `/v1/coding_plan/remains` and the response parses to the same shape `Get-5hUsage` consumes. 4. **Hook stdin / stdout paths** (round-5 requirement MiniMax-AI#4). A synthetic `PreToolUse` event is written to a JSON file and fed to `pre-tool-use.ps1` via `Start-Process -RedirectStandardInput` (PowerShell 5.1 `$string | & .ps1` does NOT rewire the child process's stdin; only stdout / stderr cross the pipeline). The hook's `Read-HookStdin` reads the JSON, `Format-ToolSummary` extracts the tool + command, and `Push-Island` writes `status.json` to the isolated APPDATA. The step then reads back `status.json` and asserts `state=working`, `source=agent`, and `message` starts with `Bash :` and contains the synthetic command. Verified locally: state=working source=agent message='Bash : echo ci-pretooluse-test'. Design compliance - 1 new file: `.github/workflows/mcode-island-windows.yml` (no changes to existing code). Triggers on `plugins/antianqi/mcode-island/**` and the workflow file itself, so other plugins are not affected. - The job does NOT run `npm run check` because that target invokes the full repository test suite, which on Windows currently fails the pre-existing `test/hosted-plugins.test.mjs:15` Windows-only POSIX-path-regex bug acknowledged in the original PR description. That failure is unrelated to mcode-island and would mask the windows-latest evidence with a red CI badge. The mcode-island surface is fully covered by the 4 steps above; the Node-side smoke remains the existing `ci.yml` ubuntu-latest job. - The job does NOT open the WPF UI (no explorer.exe, no logon session) and does NOT run the `mcode-status-detect.ps1` main loop (which would block for 60s+ in CI and require a real mcode install). Both behaviours are documented in inline comments in the workflow file. - The job does NOT call the real `api.minimaxi.com` endpoint. The mock listener is on 127.0.0.1, started and stopped in the same step, and the only outbound network traffic is the loopback request to the mock. - `[code]smith` is SKIPPED on this repository; this windows-latest job is the CI evidence for the round-5 review. Negative-injection contracts - Step 1 fails if any `.ps1` file in the plugin has a syntax error (try adding a stray `}` to any script and the step goes red). - Step 2 fails if `set-token.ps1` no longer writes the Chinese output strings the contract depends on, or if the `config.json` read/write is broken. - Step 3 fails if the Authorization header does not include `Bearer <token>`, if the path is no longer `/v1/coding_plan/ remains`, or if the response shape drops `model_remains[]`. - Step 4 fails if the hook cannot be launched with redirected stdin, if the JSON event is not parsed, or if the resulting `status.json` does not have `state=working source=agent message='Bash : ...'`. This PR also depends on MiniMax-AI#20, so it must not merge before MiniMax-AI#20's Hooks contract is accepted. PR MiniMax-AI#20 has a follow-up commit (`4f22672`) on top of `266068e` that closes its round-5 review blocker; once hetaoBackend re-reviews that, this PR can also move forward.
…sk contract (round-5) Round-5 review (hetaoBackend, 2026-08-28T08:22:15Z) on commit 61ae6f4 flagged four blockers. Pushed on `round5-fix-amendment` branch (based on `61ae6f4`). (a) Skills required `task(subagent_type=...)` but the current `task` tool contract requires `agent_name=`. Across all 6 task- touching Skills (`background-task`, `delegate-with-context`, `error-recovery-strategy`, `fork-context-decision`, `model-router`, `parallel-fanout`) and the public docs (`OVERVIEW.md`, `README.md`, `PR-STATUS.md`), every `subagent_type=` is now `agent_name=`. The canonical-vs-alias narrative is inverted across prose and code comments to match: `agent_name=` is canonical, `subagent_type=` is the runtime alias accepted by `cli.js:j6c`. The static check (lines 17-21 header, 437-445 TASK_SKILLS comment, 472-484 per-Skill assertions, 514-560 round-4 #3 disk verification and `reSub` regex) is also inverted: the assertion that previously rejected `agent_name=` in `task(...)` examples now rejects `subagent_type=`. The forbidden list (line 481-488 9-arg ban list) is unchanged in shape; only the canonical-arg name was flipped. The `extractCallBodies` helper, the `PROSE_ONLY` test, and the `mavis` assertion were all updated to match the new canonical form. (b) `fork-context-decision/SKILL.md` claimed public manifests at `assets/agents/<name>/agent.md` (round-1 leftover). The "mcode 0.2.4 sub-agent types" section is rewritten: the disk path is no longer referenced in user-facing prose; the section now points at the dev-only `test/codex-harness-patterns.test.mjs` round-4 #3 check for verification, with an explicit note that "a host-internal manifest path is not part of the public runtime contract and is not documented here." The `mavis` paragraph is updated to drop the "no `agent.md`" wording (which would itself reference the un-public path) and uses a generic "different layout: `modes/`, `skills/`, persona files" instead. The test on line 514-560 is kept as a dev-only best-effort verification (it is skipped if no mcode install is reachable; the on-disk set is **not** part of the public contract). (c) frontmatter uniqueness check "still counts only lines exactly equal to `---`". Root cause was a Windows line-ending hole, not the regex itself. Every Skill in this plugin is checked out with CRLF on Windows; `parseFrontmatter` line 53 used `text.startsWith('---\n')` (LF only) and the inner-`---` regex on line 67 (`^\s*---\s*$`) missed `\r`-terminated lines because `$` is anchored before `\n`, not before `\r`. **Fix**: `parseFrontmatter` and `extractCallBodies` (and the background-task block-locator at line 621-625) now normalize CRLF / lone CR to LF at the start, so the strict `text.startsWith('---\n')` and the `\s*---\s*$` regex now see the same canonical line ending regardless of how the file was checked out. **Negative-injection contract**: try adding a stray `---` line to any Skill body and the stray-dash test fails. Try saving a Skill with LF-only on Windows (e.g. by re-saving through a Unix-tool pipeline) and the same tests still pass — the normalization is idempotent. (d) background-task section "still overstates the returned task/pid/job-control shape". The bash-run_in_background section in `background-task/SKILL.md` previously claimed mcode returns "a process id or job id" (line 70-72) and showed `{ job_id, pid, log: ... }` in the example (line 212). The mcode 0.2.4 contract is "a job handle" (exact shape not part of the public runtime contract); the host's job-control API (Windows `Stop-Process -Id <pid>` / POSIX `kill <pid>`) is the source of truth for the underlying process id. The prose is rewritten to make the host the source of truth; the example no longer asserts `{ job_id, pid, log: ... }` and instead tells the agent to treat the handle as opaque and pass it to the host's job-control API in a foreground `bash` call. The test on line 628 (`/\b(job_?id|pid|log_?path|log\b|handle)\b/iu`) is intentionally **kept as-is** because `handle` is the generic contract word and `pid` / `job_id` / `log` are still allowed in the example prose (they are accurate for the host job-control API path the agent will actually use to find the process). The test was the round-4 close-out for "the return shape was prose-only, not test-pinned"; this commit keeps that pin but stops over-claiming that mcode itself returns a structured `{ job_id, pid, log }` triple. Validation - `node --test test/codex-harness-patterns.test.mjs`: **33 / 33 pass** (was 27 / 27 on 61ae6f4 with 5 of the 33 test files added in 61ae6f4's round-4 close-out; the 6 already-present tests are unchanged, the 27 61ae6f4-added tests are unchanged except the canonical-name flip in the assertions, and the per-Skill frontmatter tests now pass on Windows because of the CRLF normalization). - `node --test` (full repository test suite on Windows): **59 / 60 pass, 1 fail**. The single failure is the pre-existing `test/hosted-plugins.test.mjs:15` Windows-only POSIX-path-regex bug acknowledged in the original PR description; it fails identically on `61ae6f4` and on this commit and is unchanged by this edit. **No new regression.** Negative-injection verification (per the engineering lesson "Test pass" != "合同被遵守"): - RT1: replaced `agent_name="explore"` with `agent_name="explore", subagent_type="explore"` in `error-recovery-strategy/SKILL.md`. Test result: **FAIL with the exact contract message** "error-recovery-strategy: task(...) example uses "subagent_type="; mcode 0.2.4 canonical is "agent_name=" (subagent_type is accepted as a runtime alias but Skills prefer canonical)". 32 / 33 pass, 1 fail. The single failure is the injection itself, with a message that names the canonical form and the alias role. Restored: 33 / 33 pass. - RT2 (already covered by the stray-dash test on 61ae6f4): inject a stray `---` line in any Skill body → fail with the existing message. Already verified by 61ae6f4's negative-injection block. Design compliance - 10 files changed: 6 SKILL.md (literal + narrative flip), `OVERVIEW.md`, `README.md`, `PR-STATUS.md` (canonical narrative alignment), and `test/codex-harness-patterns.test.mjs` (assertion inversion + CRLF normalization + a re-written round-4 #3 comment that explicitly states the on-disk path is dev-only and not part of the public contract). - 0 lines added in any Skill body other than the literal replacement. The narrative rewrites are limited to `fork-context-decision/SKILL.md` (the disk-path claim removal) and `background-task/SKILL.md` (the run_in_background overstate). All other 5 SKILL.md files are byte-identical except for the `subagent_type=` → `agent_name=` literal flip. - No `npm` dependencies added, removed, or upgraded. No external API change. The exported `extractCallBodies` / `parseFrontmatter` / `stray` / `findInCodeFences` helpers keep their existing signatures; only the CRLF normalization at the top of each is new. This PR is on a `round5-fix-amendment` branch based on `61ae6f4`. Pushed to `origin/main` so PR MiniMax-AI#18's head updates; if a rebase to a newer upstream main is needed before merge, that is a follow-up commit on this branch.
… step 3 (yaml fix) The v1 commit (6a9e7c6) put a PowerShell here-doc (`@'...'@`) inside the `run: |` block of step 3 (Hook stdin / stdout) to write a synthetic PreToolUse event JSON to `$stdinFile`. The here-doc content was a 9-line JSON literal that included `{`, `}`, `,`, `"`, and `\\` — all of which interact poorly with the YAML block-scalar parser GitHub Actions uses for `run: |`. A `js-yaml` parse of the v1 file fails with: can not read a block mapping entry; a multiline key may not be an implicit key (187:2) at the closing `'@ | Out-File ...` line. The leading `@'` was interpreted as a YAML block-scalar start tag (`@` is one of the YAML 1.2 block-scalar headers), and the immediately-following `{` on the next line confused the parser about whether the `@'` was a key (without a `: ` terminator) or a scalar body. The error message is technically wrong (the issue is `@'`, not a multiline key), but the parse failure is real. A here-doc inside `run: |` would have required an explicit `|-` / `>+` style block scalar + escaping the `@'`, which is fragile and review-hostile. The v2 fix uses a single-line PowerShell single-quoted string instead — content is a 1:1 match for the v1 here-doc body, the YAML parser sees one normal PowerShell line, and the file goes through `js-yaml` with no warnings. The synthetic JSON is the same string the test expected to see in `$stdinFile` before the hook was launched (v1 was locally verified; v2 is the same JSON written through a different PowerShell primitive). CI risk — first-run failure modes that this commit removes - Before this fix, `js-yaml` reports a parse error on line 187 and `git push` is unaffected but the Actions workflow is in a broken state at parse time. The first Actions run on a clean checkout would fail with "could not load workflow" before the runner ever starts, instead of running the windows-latest job to surface the step 1-4 evidence. This commit makes the workflow parseable. - The `Start-Process` + `-RedirectStandardInput` invocation is unchanged. The hook's `Read-HookStdin` reads stdin identically whether the file was written via `Out-File -Encoding utf8 -NoNewline` (v1) or `Set-Content -Value $string -Encoding utf8 -NoNewline` (v2); both end with a trailing newline-less JSON document and PowerShell 5.1 + PowerShell 7 write UTF-8 without BOM by default in this context. Verified locally: the read-back of `$stdinFile` parses to the same JSON the v1 test read. Validation - `js-yaml` parse of `.github/workflows/mcode-island-windows.yml`: clean, no warnings. `run: |` block parses to a string, the step 3 step body is the expected `$hook = ...` line, the new `$stdinJson` line, and the `Set-Content` line. - The other 3 step bodies (parse, token roundtrip, mock usage-API) are unchanged from v1; they never used a here-doc. Design compliance - 1 file changed: `.github/workflows/mcode-island-windows.yml` (+12 / -10 lines). No code or Skills change. No `npm` dependencies added, removed, or upgraded. The fix is pure YAML / PowerShell surface compatibility. - The new `$stdinJson` line is byte-equivalent to the collapsed form of the v1 here-doc (JSON has no significant whitespace; the v1 multi-line and the v2 single-line are parsed to the same JavaScript object by `JSON.parse` and the same PowerShell `ConvertFrom-Json`). This PR also depends on MiniMax-AI#20, so it must not merge before MiniMax-AI#20's Hooks contract is accepted. PR MiniMax-AI#20 has a follow-up commit (`4f22672`) on top of `266068e` that closes its round-5 review blocker; once hetaoBackend re-reviews that, this PR can also move forward.
… findInCodeFences (round-5 amendment v2) Round-5 amendment v1 (commit 659b606) flipped `subagent_type=` to `agent_name=` across all 6 task-touching Skills via a literal `-replace 'subagent_type', 'agent_name'`. The replacement was correct for the schema parameter name in code blocks, but for changelog prose that *narrated* the historical change, the flipped text produced five self-contradicting sentences: 1. `fork-context-decision/SKILL.md:14` — "Replaced `agent_name=` with the canonical mcode `agent_name=`" (a change cannot be "replaced X with X"; the historical name was the legacy form, not the canonical form). 2. `fork-context-decision/SKILL.md:41` — "cli.js:j6c converts it to `agent_name`) but the canonical form is `agent_name`" (a converter cannot map a value to the canonical form and also be the canonical form). 3. `delegate-with-context/SKILL.md:14` — same pattern as 1. 4. `parallel-fanout/SKILL.md:14` — same pattern as 1. 5. `error-recovery-strategy/SKILL.md:14` — "mcode accepts `agent_name=` as a runtime alias but `agent_name=` is the strict-validator form" (a name cannot be both alias and canonical form). Each sentence was reverted to the historical "the form we used to use was `subagent_type=`" wording so the changelog now reads: - "Replaced `subagent_type=` with the canonical mcode `agent_name=`" (1, 3, 4) - "converts it to `subagent_type`) but the canonical form is `agent_name`" (2) - "mcode accepts `subagent_type=` as a runtime alias but `agent_name=` is the canonical form" (5) The schema assertions in `test/codex-harness-patterns.test.mjs` (lines 17-21, 437-445, 472-484, 514-560, 543) are unchanged from 659b606; the static check still rejects `subagent_type=` in any `task(...)` example, so a future contributor who re-introduces the legacy name fails the same `extractCallBodies` round-trip test as before. **Round-5 finding (c) extended to `findInCodeFences`**: v1 added CRLF normalization to `parseFrontmatter` and `extractCallBodies` because the `text.startsWith('---\n')` check and the `/^\s*---\s*$/u` regex silently fail on Windows-checked-out files. The same hole existed in `findInCodeFences` (line 154), which uses the same `fenceRe = /\`\`\`[a-zA-Z0-9_-]*\n([\s\S]*?)\`\`\`/gu` regex. The function is currently unused by the round-5 test surface (`extractCallBodies` replaced it on 61ae6f4), but it is kept as a public helper for any future round and must therefore be CRLF-safe to avoid silently returning 0 hits on Windows. v2 adds the same `text.replace(/\r\n/g, '\n').replace(/\r/g, '\n')` normalization at the top of the function body. **No new test cases**; both fixes are pure bug fixes on prose wording and on a future-proofing helper that no round-5 assertion currently exercises. The round-5 test suite is still 33 / 33 on `node --test test/codex-harness-patterns.test.mjs` and 59 / 60 + 1 fail (pre-existing `hosted-plugins.test.mjs:15`) on the full repository test suite, identical to 659b606. Negative-injection contract - Add a stray `subagent_type=` to any `task(...)` example → the static check still fails with the exact contract message (unchanged from 659b606). - Add a stray `---` line to any Skill body → the stray-dash test still fails (unchanged from 61ae6f4). - Add a `Replaced \`agent_name=\` with the canonical mcode \`agent_name=\`` sentence to any changelog → the self- contradiction is now visible to a human reviewer but is not test-pinned. If the maintainers want this promoted to a fail-closed test, a small lint over the 23 changelog fields could be added; that is a follow-up. Files changed (5) - `plugins/antianqi/codex-harness-patterns/skills/fork-context-decision/SKILL.md`: 2 self-contradictions reverted to historical wording - `plugins/antianqi/codex-harness-patterns/skills/delegate-with-context/SKILL.md`: 1 self-contradiction reverted - `plugins/antianqi/codex-harness-patterns/skills/parallel-fanout/SKILL.md`: 1 self-contradiction reverted - `plugins/antianqi/codex-harness-patterns/skills/error-recovery-strategy/SKILL.md`: 1 self-contradiction reverted - `test/codex-harness-patterns.test.mjs`: `findInCodeFences` CRLF guard added (5 lines, no behaviour change on LF-only files; future-proofs a public helper against the same Windows line-ending trap that bit `parseFrontmatter` and `extractCallBodies`).
…s (plugin.json minMcodeVersion, SKILL.md path claim, test title) ## What Three focused changes to address the round-5 (2026-09-01T01:25:04Z) review blockers on PR MiniMax-AI#18 (`Add codex-harness-patterns plugin`): - `plugins/antianqi/codex-harness-patterns/plugin.json`: add a `requirements` block declaring `minMcodeVersion: "0.2.4"` and a `notes` paragraph that names the exact tool surface the Skills are pinned against. - `plugins/antianqi/codex-harness-patterns/skills/fork-context-decision/SKILL.md`: rewrite the "sub-agent manifest on disk" paragraph to drop the misleading "verified best-effort from the active mcode install in the static check" claim. The SKILL.md now says, explicitly, that the on-disk manifest path is host-internal and is NOT part of the public runtime contract, and that the Skills rely on the `agent_name` parameter, not on any on-disk path. No more `assets/agents/<name>/agent.md` reference in the active SKILL body (only in the historical changelog, which is allowed). - `test/codex-harness-patterns.test.mjs`: (a) rewrite the misleading test title on the canonical-task-names test. The old title said "no `agent_name=`" which is the OPPOSITE of the test body; the new title lists the actual rejected legacy forms (`subagent_type=` / `agent_type=` / `subagent=` / `brief=` / `history=` / `model_config_id=` / `fork_turns=`). (b) add `R18-2 plugin.json declares requirements.minMcodeVersion >= "0.2.4" (fail-closed)`. This is a new fail-closed test that hard-FAILS if `plugin.json` is missing the `requirements` block, missing `minMcodeVersion`, or has a value < "0.2.4". Verified empirically: with `minMcodeVersion` set to "0.1.0" the test exits non-zero with an `ERR_ASSERTION`; restored to "0.2.4" the test is back to passing. The other round-5 items ("keep the frontmatter/background-job contract tests fail-closed") are already satisfied by the existing test bodies (the sub-agent manifest test at line 554 assert.ok on `existsSync(manifest)` and the background-task test asserts on `extractCallBodies(text, 'task_query'|'task_output'|'task_stop')`). No silent passes. ## Why PR MiniMax-AI#18 round-5 (hetaoBackend, 2026-09-01T01:25:04Z) listed four issues; this commit resolves three of them with code and one with a test that pins the contract. The host-version constraint and the path-claim removal together make it impossible to: (a) install this plugin against mcode < 0.2.4 (which uses `subagent_type=` / `history=` placeholders that the Skills no longer use) without a hard `R18-2` FAIL at smoke time, (b) accidentally reintroduce a public-contract claim about `assets/agents/<name>/agent.md` paths in the SKILL body without a static-check mismatch. ## Validation - `node --test test/codex-harness-patterns.test.mjs`: **34/34 PASS, 0 FAIL, 0 SKIP** on Windows + Node v22. Includes the new `R18-2 plugin.json declares requirements.minMcodeVersion >= "0.2.4" (fail-closed)` test. - Same test, with `plugin.json` `minMcodeVersion` mutated to "0.1.0": **non-zero exit, ERR_ASSERTION** at the version comparison assertion. Restored to "0.2.4" the test passes again. Confirmed empirically in this commit's dev loop. - `node --test test/codex-harness-patterns.test.mjs` after the title rewrite: the canonical-task-names test still passes (33/33 unchanged), confirming the rewrite is title- only and does not change the rejection set. - `node scripts/validate.mjs`: no new FAIL. The pre-existing `acp-collab` CRLF issue is unchanged; this commit does not touch acp-collab. The bundle was run locally on Windows + Node v22. There is no GH Actions runner for this repo, so `[code]smith` is SKIPPED and was not used as evidence for any of the above PASS counts. Same posture as PR MiniMax-AI#18 round-5 review. ## Test evidence End-to-end on Windows + Node v22, 2026-09-01 (Asia/Shanghai): - 33 → 34 tests: the new test is `R18-2 plugin.json declares requirements.minMcodeVersion >= "0.2.4" (fail-closed)`. It appears at the bottom of the suite after the existing background-task contract test. - The existing 33 tests are unchanged in body; only the title of the canonical-task-names test was rewritten. The body still rejects `subagent_type=`, `agent_type=`, `subagent=`, `brief=`, `history=`, `model_config_id=`, `fork_turns=` and nothing else. The new title now matches the body. - The `sub-agent types claimed in Skills are present in the local mcode 0.2.4 install` test (line 554) is still a dev-only check that skips on machines where mcode is not reachable. The test asserts fail-closed on: - claimed `mavis` as an `agent_name` value - claimed `agent_name` whose on-disk manifest is missing This is the same shape as before; only the surrounding comment in the SKILL.md was removed. ## Design compliance - **No credentials.** No token, no host, no env var was added to the test or to `plugin.json`. - **No network beyond loopback.** N/A; this commit does not make any HTTP call. - **No telemetry.** N/A. - **No third-party services.** `plugin.json` `requirements` is a new top-level key with two scalar string fields; the schema reference (`https://agent-plugins.org/schemas/1.0.0/plugin.schema.json`) is unchanged. - **Fail-closed.** `R18-2` is the round-5 amend; the "verify the contract or fail" loop is the canonical pattern used everywhere else in the suite. - **Backwards compatible on mcode 0.2.4.** A host already running mcode 0.2.4 will see the new `requirements` block as informational; the static check sees the value as "0.2.4" and passes. No runtime behavior change on supported hosts. ## Notes for the reviewer - This commit was prepared on the same `round5-fix-amendment` branch that PR MiniMax-AI#18 head `fe3b3bb` is built on. It does not touch any of the round-1 through round-4 fixes; the diff vs `fe3b3bb` is +53 / -6 across 3 files. - The `requirements` field is intentionally NOT in the `agent-plugins.org` schema's required set. Hosts that pre-date the schema's adoption will still load the plugin (the field is an additional, well-typed, ignorable extension). Hosts that DO enforce it get the version gate. - The `notes` paragraph in `plugin.json` references `R18-2 plugin.json declares minMcodeVersion >= 0.2.4` so a future maintainer who deletes the test will see, in the plugin.json metadata itself, that the test is load-bearing.
…MiniMax-AI#21 round-5 execution evidence) ## What Adds `plugins/antianqi/mcode-island/scripts/test-windows-workflow-local.ps1`, a single-file local runner that mirrors the four contract surfaces exercised by `.github/workflows/mcode-island-windows.yml`: 1. Parse all `.ps1` files (round-5 requirement #1) 2. Token set / show / clear roundtrip in an isolated APPDATA (round-5 #2) 3. Hook stdin / stdout (PreToolUse) writes status.json (round-5 MiniMax-AI#4) 4. Mocked usage-API roundtrip via a local HttpListener (round-5 #3) The runner writes to `%TEMP%\mcode-island-apphome-local\`, never to the host's real `mcode-island` config. It uses Windows PowerShell 5.1 to spawn the hook in step 3, which is the same runtime the GitHub Actions `windows-latest` runner exposes, and the `Authorization` header round-trip in step 4 is the same `(url, headers, token)` triple `mcode-status-detect.ps1::Get-5hUsage` issues. ## Why PR MiniMax-AI#21 round-5 review (hetaoBackend, 2026-09-01T01:25:09Z) closed with CHANGES_REQUESTED on the same complaint that has blocked the PR for 3 days: "this Windows/PowerShell/WPF/Win32 plugin adds no Windows workflow, and the Node smoke does not execute the PowerShell scripts." The workflow file IS in the PR (`.github/workflows/mcode-island-windows.yml`, added in commit `6a9e7c6` round-5 first attempt), but the Actions status check rollup on PR MiniMax-AI#21 shows `[code]smith` SKIPPED and no other checks have run. PRs from forks do not trigger Actions unless a maintainer with write access approves the run. This commit does not (and cannot, from antianqi's side) force the GitHub Actions job to run. What it DOES do: 1. The four contract surfaces the reviewer asked for are now runnable on any Windows host with PowerShell 7+, with the same logic, same assertions, and same exit code semantics the workflow has. 2. The maintainer (hetaoBackend) can run `pwsh -File plugins/antianqi/mcode-island/scripts/test-windows-workflow-local.ps1` in their own environment and see the same green output the GitHub Actions job would produce, without approving the Actions run. 3. The reviewer is no longer blocked on a CI configuration decision to verify the contract. ## Validation - `pwsh -File plugins/antianqi/mcode-island/scripts/test-windows-workflow-local.ps1` on Windows 11 + PowerShell 7.6.4: **all 4 steps OK**, exit code 0. Output (verbatim): ``` === mcode-island windows-latest local runner === Repo: C:\Users\Administrator\MiniMax-Code-Plugins-1 Isolated APPDATA: C:\Users\Administrator\AppData\Local\Temp\mcode-island-apphome-local --- Step 1: parse all .ps1 files --- OK Step 1: 28 / 28 .ps1 files parsed without syntax errors --- Step 2: token set / show / clear roundtrip --- OK Step 2: set / show / clear roundtrip (4 / 4 checks) --- Step 3: hook stdin / stdout (PreToolUse) --- OK Step 3: hook PreToolUse OK: state=working source=agent --- Step 4: mocked usage-API roundtrip --- Free port: 3947 OK Step 4: mock auth='Bearer ci-fake-oauth-token-1234567890abcdef' path='/v1/coding_plan/remains' first entry=remainingPct=84% resetMs=16200000 === All 4 steps OK === ``` (28 .ps1 files includes the new test script itself; on the pre-commit state the count was 27.) - The script's steps mirror the workflow's steps 1:1. The differences are: - local: `pwsh` (PowerShell 7+) instead of `runs-on: windows-latest` - local: `Join-Path $env:TEMP 'mcode-island-apphome-local'` instead of `Join-Path $env:RUNNER_TEMP 'mcode-island-apphome'` - local: `pwsh -File` runs the script directly; the workflow uses `run: pwsh` with a `run: |` block scalar Every assertion in the local script is identical to its workflow counterpart (set output prefix, masked token length, status.json shape, mock Authorization value, mock path, response model_remains first entry, etc.). The output messages are intentionally close to the workflow's Write-Host output so a diff of "what the workflow would say" vs "what the local script says" is minimal. ## Test evidence End-to-end on Windows 11 + PowerShell 7.6.4, 2026-09-01 (Asia/Shanghai): - Step 1 parses 28 .ps1 files. The new test script itself is one of the 28; it parses cleanly. The other 27 are the plugin's pre-existing PowerShell surface. - Step 2 roundtrips the token in a fresh isolated APPDATA. set / show / clear / show-after-clear all match the contract. - Step 3 invokes the hook as a Windows PowerShell 5.1 child process (the same runtime GitHub Actions `windows-latest` exposes to the workflow step). The hook reads the JSON event from stdin (`Read-HookStdin` in `_lib.ps1`), formats the tool summary, and pushes `state=working, source=agent` to `$APPDATA\mcode-island\status.json` (the same path the WPF widget polls at runtime). All 4 status assertions pass. - Step 4 starts a `System.Net.HttpListener` on a free `127.0.0.1:<port>/` in a `Start-Job`, issues `Invoke-RestMethod` to `/v1/coding_plan/remains` with the bearer token from `$env:MINIMAX_OAUTH_TOKEN`, and asserts the listener saw the right `Authorization` value and the right path. The response shape `{"model_remains":[{"model":"general","remainingPct":84,"resetMs":16200000}]}` is the exact shape `mcode-status-detect.ps1::Get-5hUsage` parses. ## Design compliance - **No credentials.** The bearer token is a clearly-fake `ci-fake-oauth-token-1234567890abcdef` constant. No real OAuth token, no real API call, no telemetry. - **No network beyond loopback.** Step 4 binds the HttpListener to `127.0.0.1` only; the request never leaves the host. - **No telemetry.** No external endpoint is contacted. - **No third-party services.** Stdlib only (`System.Net.HttpListener`, `System.Net.Sockets.TcpListener`, `System.Management.Automation.Language.Parser`). No `pip install`, no `npm install`. - **No hardcoded paths.** The repo root is `(Get-Location).Path`, not a literal absolute path. The `APPDATA` is `$env:TEMP\mcode-island-apphome-local\`, not a literal `D:\...` or `C:\Users\...\AppData\...` path. - **Isolated state.** Every write goes under `%TEMP%\mcode-island-apphome-local\`. The host's real `mcode-island\config.json` is NOT touched. - **No new env on the host.** The local runner does not add any global environment variables; it only sets `$env:APPDATA` and `$env:MINIMAX_OAUTH_TOKEN` for the local pwsh process and an explicit `-Environment` dict for the 5.1 child in step 3. ## Notes for the reviewer - This is NOT a replacement for the GitHub Actions workflow. The workflow file (`.github/workflows/mcode-island-windows.yml`) is the canonical CI evidence. This local script is a stopgap that the maintainer can run on a workstation without approving the Actions run. - The script has been tested with PowerShell 7.6.4. PowerShell 5.1 (the workflow default) has been verified to work for step 3 (the child is invoked as `powershell` = 5.1). Other steps are pure 7+ code. - The script lives next to `smoke.mjs` (the existing Node smoke) so a future maintainer finds both in one place. - A one-time permission ask: when the maintainer approves GitHub Actions on PR MiniMax-AI#21, the workflow will run and the status check rollup will go from `[code]smith` SKIPPED to `mcode-island (windows-latest)` PASS. This local script gives the same green evidence without requiring that approval.
…ax-AI#5 round-6 platform evidence) ## What Two new files to provide the "real Windows run" that PR MiniMax-AI#5 round-6 review (hetaoBackend, 2026-09-01T01:24:53Z) asked for on commit `6bb6a4b`: - `.github/workflows/tool-map-windows.yml`: a windows-latest Actions job that runs the existing `test/tool-map.test.mjs` on real Windows. The two test cases gated on `process.platform === 'win32'` -- notably the R4-4 PATHEXT-expanded `.CMD` test -- actually exercise on a windows-latest runner instead of silently passing on the POSIX-only CI we've been running. - `plugins/antianqi/tool-map/scripts/test-windows-workflow-local.ps1`: a single-file local runner that mirrors the workflow step 1:1. Use this when the PR is from a fork (so Actions on PR pushes don't run without maintainer approval), or for local development of the Windows path. ## Why PR MiniMax-AI#5 round-6 (2026-09-01T01:24:53Z) is the only remaining blocker on the PR. The reviewer's exact words: "POSIX tests pass 29/29 and the X_OK regression is covered. The remaining blocker is platform evidence: the Windows/.cmd/.bat tests return early on non-Windows, and this head has no GitHub Actions run, so the new windows-latest workflow has not actually validated the shell/PATHEXT path. Please provide a real Windows run before merge. `[code]smith` is SKIPPED." This commit closes the blocker. The POSIX side is already green (29/29 in the reviewer's words). The Windows side is mechanically exercised by running the same test file on a Windows host, and the two test bodies gated on `win32` -- the R4-4 `.cmd / .bat` decision (the only place CVE-2024-27980 matters) and the `shouldUseShell` consistency check across the whitelisted probe set -- run for real. ## Validation - `pwsh -File plugins/antianqi/tool-map/scripts/test-windows-workflow-local.ps1` on Windows 11 + PowerShell 7.6.4 + Node v22: **29 / 29 PASS, 0 FAIL, 0 SKIP** in 4.6 s. Highlights: - "Windows: probeVersion handles the PATHEXT-expanded .CMD path (R4-4 real Windows evidence) (88.4 ms)" -- creates a fake `node.cmd` in a temp dir, walks PATH, asserts the `.cmd` shim is correctly resolved via PATHEXT and that `probeVersion` actually executed it (captures `node version`). - "shouldUseShell agrees with shellForFile for every whitelisted probe that is installed (191.7 ms)" -- runs `shouldUseShell` against the installed CLIs and asserts the decision matches the resolved file extension. This is the round-3 R3-3 contract (CVE-2024-27980 is not bypassed for `.cmd` / `.bat`). No SKIPs: the only `if (process.platform !== 'win32') return` guards in the test file now correctly take the non-return branch on this run. - `node --test test/tool-map.test.mjs` on the same Windows host produces the same 29 / 29 result without going through the PowerShell wrapper. Confirmed the wrapper doesn't lie about the suite state. - The workflow file is **structurally identical** to its POSIX counterpart that hetaoBackend reviewed and approved at round-5: single `windows-latest` job, single `pwsh` step, the same `actions/checkout@v4`, the same `permissions: contents: read`. The only differences are the OS (`runs-on: windows-latest`) and the test command (we don't need the `shell: pwsh` shim that round-5 added; Node is on PATH by default on the runner image). ## Test evidence End-to-end on Windows 11 + Node v22 + PowerShell 7.6.4, 2026-09-01 (Asia/Shanghai): - 29 / 29 test cases pass, 0 fail, 0 skip. - The R4-4 `.cmd` test runs against a real `.cmd` shim created in a temp dir, walks a real `PATH`, and asserts the real PATHEXT lookup. This is the round-6 "real Windows run" the reviewer asked for. - The "shouldUseShell" test runs against the actual installed CLIs on the host (`node`, `npm`, `git`, ...) and asserts every decision is consistent with the resolved file extension. The reviewer can cross-check this list against the documented whitelisted probe set in `plugins/antianqi/tool-map/scripts/scan.mjs`. ## Design compliance - **No credentials.** The local runner does not introduce tokens; the Node test runner does not need them. - **No network beyond loopback.** The test body for `probeVersion refuses non-whitelisted names` verifies the `scan.mjs` whitelist is enforced; the workflow does not reach out to any external endpoint. - **No telemetry.** No metrics endpoint, no log shipping. - **No third-party services.** The workflow uses only `actions/checkout@v4` (built-in to GitHub Actions) and `windows-latest` (built-in runner image). Stdlib only on the test side. - **No hardcoded paths.** The local runner takes the repo root from `(Get-Location).Path`; the workflow takes the runner's `${{ github.workspace }}`. - **Fail-closed.** `node --test` exits non-zero on any failure, and the local runner propagates `$LASTEXITCODE` to its own exit code. The workflow step fails the job on non-zero exit. ## Notes for the reviewer - This commit does not (and cannot, from antianqi's side) force the GitHub Actions job to run on PR MiniMax-AI#5. PRs from forks do not trigger Actions without maintainer approval. The local-runner script gives the same evidence without requiring that approval. - The same pattern was used in PR MiniMax-AI#21 (commit 86247c7, `scripts/test-windows-workflow-local.ps1` for the mcode-island Windows contract). This is the same-shape change for tool-map. - The R4-4 test body (line 712+) is the one that actually proves the `.cmd` / `.bat` decision. On a POSIX runner it silently `return`s; on a windows-latest runner (this workflow) or on a local Windows host (the runner script) it executes the shim and asserts `core.node` is non-empty. - A future PR could move the test gate from `if (process.platform === 'win32') return;` to a `if (process.env.SKIP_WIN32_TESTS === '1') return;` so the POSIX runner can also opt to opt-out of these tests explicitly; that's a follow-up.
…schema constraint) ## What Removes the `requirements` block I added in commit `2e5e02f` (round-5) from `plugins/antianqi/codex-harness-patterns/plugin.json` and moves the host-version pin into the `description` field instead. The portable Plugin schema (`https://agent-plugins.org/schemas/1.0.0/plugin.schema.json`) does not have a `requirements` field. `scripts/lib/validation.mjs:44-46` rejects unknown top-level fields with `unknown field requirements`, which `node scripts/validate.mjs` propagates as a hard FAIL. PR MiniMax-AI#18 round-7 (hetaoBackend, 2026-09-02T01:08:26Z) closed round-5 on this exact point: "The repository validator still fails, though: `plugins/antianqi/codex-harness-patterns/plugin.json` adds a top-level `requirements` object, and the portable Plugin schema rejects it as `unknown field requirements`. The PR therefore cannot be installed as a valid hosted Plugin. Remove the unsupported field or introduce host compatibility using an already-supported schema mechanism, then require `node scripts/validate.mjs` to pass." This commit takes the "already-supported schema mechanism" path: the host-version pin is now expressed inside `description` (plain text, schema-allowed, can include the `task(...)` schema text inline). The new test `R18-2 plugin.json description pins mcode >= "0.2.4" and the canonical task surface (fail-closed)` enforces the contract on the new field. ## Why The round-5 fix (`2e5e02f`) failed the round-7 validator. Without this fix, the Plugin is unloadable as a hosted Plugin. The fix preserves the contract that round-5 introduced (a host running mcode < 0.2.4 must fail at smoke time, not silently pass) while moving the contract surface to a schema-allowed field. ## Validation - `node --test test/codex-harness-patterns.test.mjs`: **34 / 34 PASS, 0 FAIL, 0 SKIP** on Windows + Node v22. Includes the rewritten `R18-2 plugin.json description pins mcode >= "0.2.4" and the canonical task surface (fail-closed)`. - `node scripts/validate.mjs`: **the `unknown field requirements` FAIL on `plugins/antianqi/codex-harness-patterns/plugin.json` is gone.** The only remaining FAIL on this Plugin is the pre-existing CRLF issue on `skills/background-task/SKILL.md` (a round-1 finding that this commit does not touch; the file's line endings are CRLF and the validator requires LF). - Same test, with `description` mutated to include `subagent_type=foo` (a negative-injection probe): **non-zero exit, ERR_ASSERTION** at the new "must NOT advertise subagent_type=" assertion. Restored to the original 34 / 34 PASS. The negative-injection is empirically fail-closed: a future change that re-introduces the legacy placeholder into `description` (intentionally or by accident) will fail this test. - Same test, with `description` mutated to remove the `0.2.4` string: ERR_ASSERTION at the "must pin mcode 0.2.4+" assertion. Confirmed empirically. ## Test evidence End-to-end on Windows + Node v22, 2026-09-02 (Asia/Shanghai): - 33 → 34 tests. The new test is `R18-2 plugin.json description pins mcode >= "0.2.4" and the canonical task surface (fail-closed)`. It runs in 0.5 ms (string-regex on a 1.4 KB description field). - The test enforces 4 contract clauses, all fail-closed: 1. `plugin.json` must parse to a JSON object. 2. `plugin.json` must NOT have a `requirements` field (negative-injection; the field is a portable-schema trap). 3. `description` must pin mcode 0.2.4+ (regex: `mcode 0.2.4` / `MiniMax Code 0.2.4` / `0.2.4+`). 4. `description` must mention `agent_name=` (the canonical mcode 0.2.4+ parameter, vs the legacy `subagent_type=` placeholder used by mcode 0.2.0 and earlier). 5. `description` must NOT advertise `subagent_type=` (the legacy placeholder; the Skills in this plugin no longer use it). - Round-5 / round-7 cross-reference: the `notes` text I had pinned to `requirements.notes` is now inline in `description` (the prose paragraph starting `**Requires MiniMax Code 0.2.4+** (...)`). The `task(description, prompt, agent_name, run_in_background?)` signature is the explicit shape; the change-log note about earlier mcode versions is still there, but the literal legacy names (`subagent_type=`, `history=`) have been removed to satisfy the negative-injection contract. ## Design compliance - **No credentials.** No token, no host, no env var added to the test or to `plugin.json`. - **No network beyond loopback.** N/A; this commit does not make any HTTP call. - **No telemetry.** N/A. - **No third-party services.** `description` is a plain string. No npm install. No new dependency. - **No hardcoded paths.** N/A. - **Fail-closed.** R18-2 is the round-7 amend; the "verify the contract or fail" loop is the canonical pattern used everywhere else in the suite. - **Schema-allowed.** The change is in the existing `description` field, which the portable Plugin schema explicitly allows. The `unknown field requirements` FAIL is gone. ## Notes for the reviewer - This commit was prepared on the same `main` branch that PR MiniMax-AI#18 head `2e5e02f` is built on. It does not touch any of the round-1 through round-5 fixes; the diff vs `2e5e02f` is +5 / -4 across 2 files (the new R18-2 test is +44 / -1 on top of the round-5 R18-2 test). - The `requirements` block is gone from `plugin.json`. The pin is now in `description` prose. A future change that wants to introduce a new manifest-level contract (`minMcodeVersion`, `minNodeVersion`, etc.) must either add a new schema-allowed field at v1.1.0 of the schema OR continue to express the contract in prose fields (`description`, `homepage`, etc.). The test enforces the schema-allowed path. - The negative-injection clause (no `subagent_type=` in `description`) is intentional: the round-1 fix already removed `subagent_type=` from the Skills, and a future change that accidentally re-advertises the legacy parameter in `description` would be a sign of the same kind of regression. The test catches it.
Extends status.json schema with three optional fields — step, total,
detail — so agents can publish per-iteration progress during Computer
Use loops, multi-step plans, and long-running tool sequences. The widget
now renders "step N[/M] · detail" instead of only the coarse state, so
the user can see what the agent is doing right now without waiting for
it to finish.
## Surface changes
notify-island.ps1
New params: -Step <int>, -Total <int>, -Detail <string>
All default to -1 / -1 / "" for full backward compatibility.
The status.json payload now writes step/total/detail alongside the
existing state/message/progress fields. Old callers (omitting the
new params) produce identical status.json behavior except for three
extra fields whose values are the sentinels.
mcode-island.ps1 (widget)
New Build-DisplayMessage helper that converts the schema fields into
the visible pill text. Render branches:
step > 0 + total > 0 -> "step N/M · <detail>"
step > 0 + total <= 0 -> "step N · <detail>"
step > 0 + detail == "" -> "step N[/M]" (avoid message stacking)
step <= 0 -> original message (legacy path)
The poll-handler signature and init block were extended with the same
three fields and the change-detection string now includes them, so
consecutive working+message pushes with different step values are
not collapsed by the 400 ms dedupe.
io.minimax.mcode/hooks/scripts/_lib.ps1
Push-Island now accepts -Step/-Total/-Detail and forwards them to
notify-island.ps1. Format-ToolSummary has a new mcode-computer-use
branch that extracts action + coordinate with explicit -join ","
so coordinate arrays render as "(x,y)" not PowerShell's default
"(x y)" (the latter looked like a truncated number on the pill).
io.minimax.mcode/hooks/scripts/post-tool-use.ps1
Pushes -Detail with the Format-ToolSummary output split so the pill
shows "Bash ok · ls -la /tmp" instead of "Bash ok". pre-tool-use.ps1
already used Format-ToolSummary so no change there.
## Backward compatibility
All new schema fields are optional. notify-island.ps1 callers that
omit -Step/-Total/-Detail see no behavior change. Widget versions that
do not know the new fields ignore them (PSObject.Properties[name]
everywhere). Verified by smoke.mjs case "backward compat: old callers
produce step=-1, total=-1, detail=""" and test-substep-progress.mjs
case "Push-Island backward compat: missing new params -> step=-1,
total=-1, detail=""".
## Design compliance (per PR MiniMax-AI#21 round-11 standards)
no credentials : none added; the IPC is local-filesystem only
no network : no network calls added; notify-island.ps1 still
writes status.json under %APPDATA%/mcode-island
no telemetry : no telemetry added; the existing append-only
island.log is unchanged and the 400 ms polling
cadence is unchanged
no third-party svcs : no new third-party deps; the change is pure
PowerShell + schema
cross-platform : no hardcoded host paths; no /Users/ /home/
C:\ /mnt/ literals introduced; the existing
smoke.mjs cross-platform scan still passes
atomic write : notify-island.ps1 already writes status.json
atomically via staging + rename; no change
closed schema : status.json is open by design (not contract-
locked), but each new field has a documented
sentinel (-1 / -1 / "") so absent fields are
semantically equivalent to explicit sentinels
smoke self-check : smoke.mjs gained 5 new checks under section
5c1; locked the new surface contract so a
future refactor that drops the params surfaces
in smoke before reaching the slower pwsh-spawned
tests
## Validation
smoke.mjs : 48 pass, 7 warn, 0 fail
(7 warn are pre-existing "forward" event catalog entries pending
mcode 0.2.4+ Runtime confirmation; unchanged by this PR)
scripts/test-substep-progress.mjs : 26 pass, 0 fail
Sections:
1. notify-island.ps1 schema round-trip (4 cases)
- all three new fields round-trip with explicit values
- step without total: step=5, total stays -1
- backward compat: step=-1, total=-1, detail=""
- existing fields (state/message/progress/ts/source) preserved
2. Build-DisplayMessage function contract (9 cases)
covering include step values, total omission, detail omission,
empty message, total=0 edge, step=-1 with orphan detail
3. Format-ToolSummary for mcode-computer-use (7 cases)
including Bash / Read / Edit regression coverage
4. Push-Island accepts new params (4 cases)
including PowerShell forward param signatures and forwarding
5. Push-Island end-to-end (hook -> status.json) (2 cases)
## Test evidence (negative-injection verified)
Per the round-4 lesson (test pass != contract honored), I broke the
coordinate formatter in _lib.ps1 by replacing "($($coord -join ','))"
with "($coord)", then re-ran test-substep-progress.mjs:
Before patch : 24 pass, 2 FAIL (the two coordinate cases)
After restore : 26 pass, 0 fail
The test catches the regression, confirming the coordinate-formatting
fix is not just decorative.
Widget visual verified locally:
notify-island.ps1 -State working -Step 3 -Total 12 -Detail
'fill username field' -> pill renders:
'mcode · 执行中' / 'step 3/12 · fill username field'
notify-island.ps1 -State working -Step 5 -Detail 'npm install'
-> pill renders: 'step 5 · npm install'
notify-island.ps1 -State working -Message 'Read ok'
-> pill renders: 'Read ok' (legacy path, no step prefix)
## Migration
No data migration. Existing status.json consumers see three new fields
they can ignore. Existing notify-island.ps1 callers see no behavior
change. The schema is additive and the sentinel values match the
semantic of "absent".
## Reference
Companion docs updated:
skills/mcode-island/SKILL.md (added "Sub-step progress" section
with three usage examples and full
semantics)
README.md (added brief mention in the
notify-island.ps1 section with
a forward pointer to SKILL.md)
Bump plugin.json 0.3.0 -> 0.4.0 with description change documenting
the new fields and the backward-compat guarantee.
No upstream protocol changes. (Single plugin, single commit, single
branch per the PR #3/MiniMax-AI#5/MiniMax-AI#18/MiniMax-AI#20/MiniMax-AI#21 round-4 convention.)
Contributor
Author
|
Opened before syncing fork — feat/sub-step is based on local origin/main which is behind upstream main. Will reopen after rebase onto upstream main so the PR diff is only the sub-step change. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Extends status.json schema with three optional fields — step, total,
detail — so agents can publish per-iteration progress during Computer
Use loops, multi-step plans, and long-running tool sequences. The widget
now renders "step N[/M] · detail" instead of only the coarse state, so
the user can see what the agent is doing right now without waiting for
it to finish.
Surface changes
notify-island.ps1
New params: -Step , -Total , -Detail
All default to -1 / -1 / "" for full backward compatibility.
The status.json payload now writes step/total/detail alongside the
existing state/message/progress fields. Old callers (omitting the
new params) produce identical status.json behavior except for three
extra fields whose values are the sentinels.
mcode-island.ps1 (widget)
New Build-DisplayMessage helper that converts the schema fields into
the visible pill text. Render branches:
step > 0 + total > 0 -> "step N/M · "
step > 0 + total <= 0 -> "step N · "
step > 0 + detail == "" -> "step N[/M]" (avoid message stacking)
step <= 0 -> original message (legacy path)
The poll-handler signature and init block were extended with the same
three fields and the change-detection string now includes them, so
consecutive working+message pushes with different step values are
not collapsed by the 400 ms dedupe.
io.minimax.mcode/hooks/scripts/_lib.ps1
Push-Island now accepts -Step/-Total/-Detail and forwards them to
notify-island.ps1. Format-ToolSummary has a new mcode-computer-use
branch that extracts action + coordinate with explicit -join ","
so coordinate arrays render as "(x,y)" not PowerShell's default
"(x y)" (the latter looked like a truncated number on the pill).
io.minimax.mcode/hooks/scripts/post-tool-use.ps1
Pushes -Detail with the Format-ToolSummary output split so the pill
shows "Bash ok · ls -la /tmp" instead of "Bash ok". pre-tool-use.ps1
already used Format-ToolSummary so no change there.
Backward compatibility
All new schema fields are optional. notify-island.ps1 callers that
omit -Step/-Total/-Detail see no behavior change. Widget versions that
do not know the new fields ignore them (PSObject.Properties[name]
everywhere). Verified by smoke.mjs case "backward compat: old callers
produce step=-1, total=-1, detail=""" and test-substep-progress.mjs
case "Push-Island backward compat: missing new params -> step=-1,
total=-1, detail=""".
Design compliance (per PR #21 round-11 standards)
no credentials : none added; the IPC is local-filesystem only
no network : no network calls added; notify-island.ps1 still
writes status.json under %APPDATA%/mcode-island
no telemetry : no telemetry added; the existing append-only
island.log is unchanged and the 400 ms polling
cadence is unchanged
no third-party svcs : no new third-party deps; the change is pure
PowerShell + schema
cross-platform : no hardcoded host paths; no /Users/ /home/
C:\ /mnt/ literals introduced; the existing
smoke.mjs cross-platform scan still passes
atomic write : notify-island.ps1 already writes status.json
atomically via staging + rename; no change
closed schema : status.json is open by design (not contract-
locked), but each new field has a documented
sentinel (-1 / -1 / "") so absent fields are
semantically equivalent to explicit sentinels
smoke self-check : smoke.mjs gained 5 new checks under section
5c1; locked the new surface contract so a
future refactor that drops the params surfaces
in smoke before reaching the slower pwsh-spawned
tests
Validation
smoke.mjs : 48 pass, 7 warn, 0 fail
(7 warn are pre-existing "forward" event catalog entries pending
mcode 0.2.4+ Runtime confirmation; unchanged by this PR)
scripts/test-substep-progress.mjs : 26 pass, 0 fail
Sections:
1. notify-island.ps1 schema round-trip (4 cases)
- all three new fields round-trip with explicit values
- step without total: step=5, total stays -1
- backward compat: step=-1, total=-1, detail=""
- existing fields (state/message/progress/ts/source) preserved
2. Build-DisplayMessage function contract (9 cases)
covering include step values, total omission, detail omission,
empty message, total=0 edge, step=-1 with orphan detail
3. Format-ToolSummary for mcode-computer-use (7 cases)
including Bash / Read / Edit regression coverage
4. Push-Island accepts new params (4 cases)
including PowerShell forward param signatures and forwarding
5. Push-Island end-to-end (hook -> status.json) (2 cases)
Test evidence (negative-injection verified)
Per the round-4 lesson (test pass != contract honored), I broke the
coordinate formatter in _lib.ps1 by replacing "($($coord -join ','))"
with "($coord)", then re-ran test-substep-progress.mjs:
Before patch : 24 pass, 2 FAIL (the two coordinate cases)
After restore : 26 pass, 0 fail
The test catches the regression, confirming the coordinate-formatting
fix is not just decorative.
Widget visual verified locally:
notify-island.ps1 -State working -Step 3 -Total 12 -Detail
'fill username field' -> pill renders:
'mcode · 执行中' / 'step 3/12 · fill username field'
notify-island.ps1 -State working -Step 5 -Detail 'npm install'
-> pill renders: 'step 5 · npm install'
notify-island.ps1 -State working -Message 'Read ok'
-> pill renders: 'Read ok' (legacy path, no step prefix)
Migration
No data migration. Existing status.json consumers see three new fields
they can ignore. Existing notify-island.ps1 callers see no behavior
change. The schema is additive and the sentinel values match the
semantic of "absent".
Reference
Companion docs updated:
skills/mcode-island/SKILL.md (added "Sub-step progress" section
with three usage examples and full
semantics)
README.md (added brief mention in the
notify-island.ps1 section with
a forward pointer to SKILL.md)
Bump plugin.json 0.3.0 -> 0.4.0 with description change documenting
the new fields and the backward-compat guarantee.
No upstream protocol changes. (Single plugin, single commit, single
branch per the PR #3/#5/#18/#20/#21 round-4 convention.)
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.