@rosenfan/matrix 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -69,7 +69,7 @@ Each phase:
69
69
 
70
70
  ## Prerequisites
71
71
 
72
- - [Claude Code](https://docs.anthropic.com/claude-code) and/or Codex installed as the Skill host
72
+ - [Claude Code](https://docs.anthropic.com/claude-code), [Codex](https://openai.com/codex), or [ZCode](https://zcode.ai) installed as the Skill host (agents that read the Agent Skills open-standard `.agents/skills` directory — opencode, Kimi Code, and other compatible tools — share the Codex installation)
73
73
  - Node.js 18+ and `npm` on `PATH` (for the Matrix CLI)
74
74
  - Python is optional during the compatibility window; new installations use the bundled Node workflow runtime.
75
75
  - Git is only needed when installing from GitHub or contributing to Matrix.
@@ -93,10 +93,10 @@ matrix init
93
93
 
94
94
  The interactive setup lets you:
95
95
 
96
- - Choose English or Chinese, project or global Matrix discovery scope, and Claude Code, Codex, or both
96
+ - Choose English or Chinese, project or global Matrix discovery scope, and any combination of Claude Code, Codex, and ZCode
97
97
  - Copy skills (recommended) or symlink them for local development
98
98
  - Safely reconfigure an existing installation with an automatic backup
99
- - Review all 25 official Matt Skills v1.2.3 roles and install the 23 compatible Skills into the target project
99
+ - Review all 27 official Matt Skills v1.3.1 roles and install the 25 compatible Skills into the target project
100
100
  - Choose **Prim** or **Arch** as the project default without disabling the other available mode
101
101
 
102
102
  For automation and CI:
@@ -133,7 +133,9 @@ matrix update --yes
133
133
 
134
134
  `matrix update` preserves the installation's scope, platforms, language, mode, and orchestration. It first validates a newer npm package in isolation, then re-runs the new CLI to refresh Matrix assets. When the local Matt Skills are incomplete or outdated, an interactive `matrix update` asks once and repairs them to the supported release in the same run — CLI upgrade, asset refresh, and Matt repair complete in a single command (`--with-mattpocock` gives non-interactive/CI the same behavior; `--force-matt` additionally replaces locally modified Matt Skills and requires that flag; Matt Skills are never removed by `matrix update`). `--all` extends the run to every indexed project (`~/.matrix/projects.json`); each project updates in isolation and missing directories are pruned from the index. npm and project assets are separate transactions: if asset refresh fails after a CLI upgrade, run `matrix update --skip-self-update` to retry. On Windows the self-update chain runs npm through Node (no shell), so one-command upgrades work without manual steps.
135
135
 
136
- Matrix 0.1.6 is bound to Matt Skills v1.2.3 at commit `6acc160e4e0cd062dbbbd7a1b26ae92855edf07e`. `matrix init --with-mattpocock` always uses that exact archive and its reviewed 23-Skill compatible manifest, even if upstream has a newer release. The two official but incompatible Skills, `implement` and raw `resolving-merge-conflicts`, are not Matrix-managed or installed. Matrix scope controls Matrix Skill discovery only: Matt Skills and `.matrix/matt-installation.json` are always project-local. Global Matt copies are preserved but never used to complete project readiness.
136
+ Matrix 0.1.7 is bound to Matt Skills v1.3.1 at commit `24fe0ef7737efae15c87225755e9f6f5965e4888`. `matrix init --with-mattpocock` always uses that exact archive and its reviewed 25-Skill compatible manifest, even if upstream has a newer release. The two official but incompatible Skills, `implement` and `implement-spec`, are not Matrix-managed or installed. Matrix scope controls Matrix Skill discovery only: Matt Skills and `.matrix/matt-installation.json` are always project-local. Global Matt copies are preserved but never used to complete project readiness. Upstream v1.3.1 also renamed the domain-doc convention: Matt Skills now read `GLOSSARY.md`/`GLOSSARY-MAP.md` instead of `CONTEXT.md`/`CONTEXT-MAP.md`; rename existing files with `git mv` after updating.
137
+
138
+ Matrix ships dedicated platform entries only for agents with their own skill-directory convention: Claude Code (`.claude/skills`), Codex (`.agents/skills`), and ZCode (`.zcode/skills`). Installing the Codex platform also serves every agent that reads the Agent Skills open-standard `.agents/skills` directory by default — opencode, Kimi Code, and other compatible tools pick up Matrix and Matt Skills from there without a separate Matrix platform.
137
139
 
138
140
  The language selected by `matrix init` controls the prose in new Matrix artifacts. Markdown headings required by Matrix guards remain stable English tokens; the artifact body is Chinese for `zh-CN` and English for `en`. A change freezes this language at creation time.
139
141
 
@@ -217,8 +219,8 @@ matrix workflow guard open
217
219
  # Advance (only if guard passes)
218
220
  matrix workflow transition design
219
221
 
220
- # Lightweight: approve the compact proposal and enter Build
221
- matrix workflow transition build --confirmed
222
+ # Lightweight: approve the compact proposal and enter Build (executor choice merged into the same pause)
223
+ matrix workflow transition build --confirmed --executor <current|model|handoff:<agent-id>>
222
224
 
223
225
  # Return from verification failure; stale evidence is retained in history
224
226
  matrix workflow return build --reason verification-failed
@@ -286,7 +288,7 @@ The sidecar does NOT change Matrix state. It's a pure export — like taking a s
286
288
 
287
289
  ## Integration with Matt Pocock's Skills
288
290
 
289
- Prim uses Matrix's own capability. Matrix reviews all 25 official Matt v1.2.3 roles, installs the 23 compatible Skills, and lets Arch automatically invoke only this reviewed ten-capability cohort:
291
+ Prim uses Matrix's own capability. Matrix reviews all 27 official Matt v1.3.1 roles, installs the 25 compatible Skills, and lets Arch automatically invoke only this reviewed ten-capability cohort:
290
292
 
291
293
  | Phase | Capability | Trigger |
292
294
  |---|---|---|
@@ -301,11 +303,11 @@ Prim uses Matrix's own capability. Matrix reviews all 25 official Matt v1.2.3 ro
301
303
  | build | `/wizard` | The Contract requires generated human-run shell steps; Matrix never runs them end-to-end |
302
304
  | verify | `/code-review` | Standards and Spec review evidence is required |
303
305
 
304
- Matrix may recommend but never nest these user-invoked handoffs: `setup-matt-pocock-skills`, `improve-codebase-architecture`, `wayfinder`, `handoff`, and `to-questionnaire`. The user explicitly runs them outside the active phase; selected output may enter a governed Change.
306
+ Matrix may recommend but never nest these user-invoked handoffs: `setup-matt-pocock-skills`, `improve-codebase-architecture`, `wayfinder`, `handoff`, `retro`, and `to-questionnaire`. The user explicitly runs them outside the active phase; selected output may enter a governed Change.
305
307
 
306
308
  Arch records a Runtime-managed review receipt after `code-review`, binding its fixed point and committed HEAD diff to the current Contract, review evidence, and workspace. A normal `code-review` receipt requires a clean candidate worktree outside Matrix-managed assets. When Git has no usable baseline, the committed diff is empty, staged/unstaged/untracked candidate files still exist, or the correctly installed capability fails, Matrix may perform one explicitly labelled same-capability fallback for that revision. The `uncommitted-worktree` fallback binds both the fixed point and a content hash of the complete worktree candidate. Installation-integrity failures never fall back. A heading or self-description without the matching receipt cannot advance Verify.
307
309
 
308
- Installed standalone Skills are `ask-matt`, `grill-with-docs`, `triage`, `to-spec`, `to-tickets`, `grill-me`, `teach`, and `wait-what`. `implement` and raw `resolving-merge-conflicts` are incompatible because they can take over TDD/review/staging/commit effects owned by Matrix, so 0.1.5 neither installs nor manages them. Pre-existing copies are preserved and reported as unmanaged extras. Installation never grants automatic invocation, and no companion may transition, commit, archive, recover, or write Matrix state.
310
+ Installed standalone Skills are `ask-matt`, `grill-with-docs`, `triage`, `to-spec`, `to-tickets`, `grill-me`, `teach`, `wait-what`, and `pr`. `implement` and `implement-spec` are incompatible because they can take over TDD/review/staging/commit effects owned by Matrix, so 0.1.7 neither installs nor manages them. Pre-existing copies (including any legacy `resolving-merge-conflicts`) are preserved and reported as unmanaged extras. Installation never grants automatic invocation, and no companion may transition, commit, archive, recover, or write Matrix state.
309
311
 
310
312
  ---
311
313
 
package/README.zh-CN.md CHANGED
@@ -69,7 +69,7 @@ verify --acceptance-or-design-gap------> design
69
69
 
70
70
  ## 前置条件
71
71
 
72
- - 已安装 [Claude Code](https://docs.anthropic.com/claude-code) 和/或 Codex,作为 Skill 宿主
72
+ - 已安装 [Claude Code](https://docs.anthropic.com/claude-code)、[Codex](https://openai.com/codex) 或 [ZCode](https://zcode.ai) 之一,作为 Skill 宿主(默认读取 Agent Skills 开放标准 `.agents/skills` 目录的 agent(如 opencode、Kimi Code 等兼容工具)直接共享 Codex 平台的安装产物)
73
73
  - Node.js 18+,且 `npm` 已加入 PATH(用于 Matrix CLI)
74
74
  - Python 仅在兼容窗口内的旧适配器路径使用;新安装使用内置 Node workflow runtime
75
75
  - Git 仅在从 GitHub 安装或参与 Matrix 开发时需要
@@ -96,7 +96,7 @@ matrix init
96
96
  - 选择 Matrix Skill 发现入口安装到当前项目或全局
97
97
  - 复制 skills(推荐)或为本地开发创建符号链接
98
98
  - 安全地重新配置已有安装,并自动备份被替换的 Matrix 文件
99
- - 审查 Matt Skills v1.2.3 官方 25 项角色,并在目标项目安装其中 23 项兼容 Skill
99
+ - 审查 Matt Skills v1.3.1 官方 27 项角色,并在目标项目安装其中 25 项兼容 Skill
100
100
  - 选择 Prim 或 Arch 作为项目默认值,且不禁用另一种可用模式
101
101
 
102
102
  自动化或 CI 环境可以使用:
@@ -130,7 +130,9 @@ matrix update --yes
130
130
 
131
131
  `matrix update` 会保留既有安装的 scope、平台、语言、mode 与 orchestration。它先隔离验证 npm 新包,再由新 CLI 同步 Matrix 资产。当本地 Matt Skills 不完整或过期时,交互式 `matrix update` 会询问一次,并在同一轮把 Matt 修复到受支持 release——CLI 升级、资产刷新、Matt 修复一条命令完成(`--with-mattpocock` 为自动化/CI 提供等价的非交互行为;`--force-matt` 可额外替换被本地修改的 Matt Skills,且必须与 `--with-mattpocock` 同传;`matrix update` 绝不删除 Matt Skills)。`--all` 可将更新扩展到全部已索引项目(`~/.matrix/projects.json`),各项目隔离更新,失效目录自动从索引清理。npm 与项目资产是两个事务:若 CLI 已更新但资产同步失败,可运行 `matrix update --skip-self-update` 重试。Windows 上自升级链路经 Node 调用 npm(不经 shell),一条命令升级无需手动步骤。
132
132
 
133
- Matrix 0.1.6 固定对应 Matt Skills v1.2.3,commit 为 `6acc160e4e0cd062dbbbd7a1b26ae92855edf07e`。即使上游已有新版,`matrix init --with-mattpocock` 仍使用该确切 archive 与经过审查的 23 项兼容 manifest。官方角色中的 `implement` 与 raw `resolving-merge-conflicts` 不兼容,因此 Matrix 不安装也不管理它们。Matrix scope 只控制 Matrix Skill 的发现位置;Matt Skills 与 `.matrix/matt-installation.json` 始终属于目标项目。全局 Matt 副本会保留,但绝不用于补齐项目 readiness。
133
+ Matrix 0.1.7 固定对应 Matt Skills v1.3.1,commit 为 `24fe0ef7737efae15c87225755e9f6f5965e4888`。即使上游已有新版,`matrix init --with-mattpocock` 仍使用该确切 archive 与经过审查的 25 项兼容 manifest。官方角色中的 `implement` 与 `implement-spec` 不兼容,因此 Matrix 不安装也不管理它们。Matrix scope 只控制 Matrix Skill 的发现位置;Matt Skills 与 `.matrix/matt-installation.json` 始终属于目标项目。全局 Matt 副本会保留,但绝不用于补齐项目 readiness。上游 v1.3.1 同时把域文档约定改名:Matt Skills 现在读取 `GLOSSARY.md`/`GLOSSARY-MAP.md`(原 `CONTEXT.md`/`CONTEXT-MAP.md`);升级后请用 `git mv` 跟随改名。
134
+
135
+ Matrix 只为拥有独立 skill 目录约定的 agent 内置平台条目:Claude Code(`.claude/skills`)、Codex(`.agents/skills`)与 ZCode(`.zcode/skills`)。安装 Codex 平台即同时覆盖所有默认读取 Agent Skills 开放标准 `.agents/skills` 目录的工具——opencode、Kimi Code 等兼容 agent 无需单独的 Matrix 平台条目即可使用 Matrix 与 Matt Skills。
134
136
 
135
137
  `matrix init` 选择的语言决定新 Matrix 产物的正文语言:`zh-CN` 使用中文、`en` 使用英文。Runtime 守卫依赖的 Markdown 英文标题 token 保持不变;每个 change 在创建时冻结该语言。
136
138
 
@@ -214,8 +216,8 @@ matrix workflow guard open
214
216
  # 推进(仅在守卫通过时)
215
217
  matrix workflow transition design
216
218
 
217
- # lightweight:确认紧凑 proposal 后进入 Build
218
- matrix workflow transition build --confirmed
219
+ # lightweight:确认紧凑 proposal 后进入 Build(执行主体选择并入同一次暂停)
220
+ matrix workflow transition build --confirmed --executor <current|model|handoff:<agent-id>>
219
221
 
220
222
  # 验证失败时返回 Build;旧证据保留在历史目录
221
223
  matrix workflow return build --reason verification-failed
@@ -283,7 +285,7 @@ $matrix → open → design → build → verify → archive
283
285
 
284
286
  ## 与 Matt Pocock Skills 的集成
285
287
 
286
- Prim 使用 Matrix 自身能力。Matrix 会审查 Matt v1.2.3 官方 25 项角色,安装其中 23 项兼容 Skill,但 Arch 只自动调用以下十项经过审查的能力:
288
+ Prim 使用 Matrix 自身能力。Matrix 会审查 Matt v1.3.1 官方 27 项角色,安装其中 25 项兼容 Skill,但 Arch 只自动调用以下十项经过审查的能力:
287
289
 
288
290
  | 阶段 | 能力 | 触发条件 |
289
291
  |---|---|---|
@@ -298,11 +300,11 @@ Prim 使用 Matrix 自身能力。Matrix 会审查 Matt v1.2.3 官方 25 项角
298
300
  | build | `/wizard` | Contract 需要生成人工执行的 shell 步骤;Matrix 不端到端运行 |
299
301
  | verify | `/code-review` | 需要 Standards 与 Spec 双轴证据 |
300
302
 
301
- Matrix 可以推荐但绝不嵌套以下 user-invoked handoff:`setup-matt-pocock-skills`、`improve-codebase-architecture`、`wayfinder`、`handoff`、`to-questionnaire`。它们由用户在活动阶段外显式运行,选中的结果可以再进入受治理 Change。
303
+ Matrix 可以推荐但绝不嵌套以下 user-invoked handoff:`setup-matt-pocock-skills`、`improve-codebase-architecture`、`wayfinder`、`handoff`、`retro`、`to-questionnaire`。它们由用户在活动阶段外显式运行,选中的结果可以再进入受治理 Change。
302
304
 
303
305
  Arch 在 `code-review` 后记录由 Runtime 管理的审查回执,把 fixed point 与已提交的 HEAD diff 绑定到当前 Contract、审查正文和工作区。正常的 `code-review` 回执要求 Matrix 管理资产之外的候选工作区是干净的。无可用 Git 基线、已提交 diff 为空、仍存在 staged/unstaged/untracked 候选,或正确安装的 capability 真实失败时,Matrix 可为该 revision 执行一次明确标记的同能力 fallback;其中 `uncommitted-worktree` 会同时绑定 fixed point 与完整工作区候选的内容 hash。安装完整性失败不得 fallback。只有标题或自述、但没有匹配回执时,Verify 不能推进。
304
306
 
305
- 安装但保持 standalone 的 Skill 是 `ask-matt`、`grill-with-docs`、`triage`、`to-spec`、`to-tickets`、`grill-me`、`teach`、`wait-what`。`implement` 与 raw `resolving-merge-conflicts` 可能接管 Matrix 所有的 TDD、review、stage 或 commit 副作用,因此 0.1.5 不安装也不管理它们;已有副本会被保留并报告为未管理额外项。安装不代表自动调用;任何 companion 都不得推进、提交、归档、恢复或写 Matrix 状态。
307
+ 安装但保持 standalone 的 Skill 是 `ask-matt`、`grill-with-docs`、`triage`、`to-spec`、`to-tickets`、`grill-me`、`teach`、`wait-what`、`pr`。`implement` 与 `implement-spec` 可能接管 Matrix 所有的 TDD、review、stage 或 commit 副作用,因此 0.1.7 不安装也不管理它们;已有副本(含遗留的 `resolving-merge-conflicts`)会被保留并报告为未管理额外项。安装不代表自动调用;任何 companion 都不得推进、提交、归档、恢复或写 Matrix 状态。
306
308
 
307
309
  ---
308
310
 
@@ -33,7 +33,7 @@ An explicit mode wins; otherwise Runtime uses the project `default_orchestration
33
33
  ## Orchestration
34
34
 
35
35
  - **Prim** uses Matrix's own capability in every phase and never requires Matt Skills.
36
- - **Arch** conditionally invokes the ten reviewed automatic capabilities frozen by the active Change. Matrix 0.1.4 reviews all 25 official Matt Skills v1.2.3 roles and installs only the 23 compatible Skills project-locally; `implement` and raw `resolving-merge-conflicts` are excluded.
36
+ - **Arch** conditionally invokes the ten reviewed automatic capabilities frozen by the active Change. Matrix 0.1.7 reviews all 27 official Matt Skills v1.3.1 roles and installs only the 25 compatible Skills project-locally; `implement` and `implement-spec` are excluded.
37
37
 
38
38
  Both orchestration modes use the same selected workflow profile. `full` uses `open → design → build → verify → archive`; `hotfix` and `tweak` share one internal `lightweight` profile, `open → build → verify → archive`, with different evidence policies. Companion Skills never write Matrix state, transition, commit, archive, or create competing workflow artifacts.
39
39
 
@@ -73,7 +73,7 @@ Use it only when the user explicitly chooses a different implementation agent; n
73
73
 
74
74
  ## Cross-Cutting Skills
75
75
 
76
- Matrix never nests user-invoked Matt Skills. When relevant, explain the reason and ask the user to invoke one explicitly: `setup-matt-pocock-skills` for one-time setup, `improve-codebase-architecture` for an out-of-band scan, `wayfinder` for generating a separate large-effort candidate, `handoff` at a context boundary, or `to-questionnaire` when another person owns blocking facts. `ask-matt`, `grill-with-docs`, `triage`, `to-spec`, `to-tickets`, `grill-me`, `teach`, and `wait-what` remain standalone. Matrix does not install or manage `implement` or raw `resolving-merge-conflicts`; preserve pre-existing copies as unmanaged extras and never suggest them inside an active Change.
76
+ Matrix never nests user-invoked Matt Skills. When relevant, explain the reason and ask the user to invoke one explicitly: `setup-matt-pocock-skills` for one-time setup, `improve-codebase-architecture` for an out-of-band scan, `wayfinder` for generating a separate large-effort candidate, `handoff` at a context boundary, `retro` for a post-session agent-environment retrospective, or `to-questionnaire` when another person owns blocking facts. `ask-matt`, `grill-with-docs`, `triage`, `to-spec`, `to-tickets`, `grill-me`, `teach`, `wait-what`, and `pr` remain standalone. Matrix does not install or manage `implement` or `implement-spec`; preserve pre-existing copies (including any legacy `resolving-merge-conflicts`) as unmanaged extras and never suggest them inside an active Change.
77
77
 
78
78
  ## Rules
79
79
 
@@ -84,5 +84,5 @@ Matrix never nests user-invoked Matt Skills. When relevant, explain the reason a
84
84
  - `node <matrix-skill-directory>/scripts/matrix-runtime.mjs doctor` is read-only. Every transaction or stale-lock repair must explicitly bind the identity reported by doctor; do not edit or delete `.matrix/transactions/` or `.matrix/workflow.lock`.
85
85
  - Do not replace required evidence with an assertion that work is complete.
86
86
  - Pause for user confirmation at material architecture/scope decisions and before archive/commit.
87
- - Before entering Build (full: after the design guard; lightweight: merged into the `transition build --confirmed` pause), present the implementation-actor choice — continue with the current agent/model, switch the model in the user's client, or switch the implementation agent via `$matrix-handoff` — and record the decision with the Contract.
87
+ - Before entering Build (full: after the design guard; lightweight: merged into the `transition build --confirmed` pause), present the implementation-actor choice — continue with the current agent/model, switch the model in the user's client, or switch the implementation agent via `$matrix-handoff` — and pass the recorded choice to the Runtime with `transition build --executor <current|model|handoff:<agent-id>>` (lightweight also carries `--confirmed`). The Runtime rejects Build entry without it and stores the choice as `implementation_actor`.
88
88
  - On shortcut scope expansion, run `node <matrix-skill-directory>/scripts/matrix-runtime.mjs return design --reason design-gap`; Runtime upgrades the change to `full`. Never silently continue the old contract.
@@ -34,3 +34,5 @@ node <matrix-skill-directory>/scripts/matrix-runtime.mjs return design --reason
34
34
  ```
35
35
 
36
36
  After successful commit, the change is under `.matrix/archive/` and the workflow is complete.
37
+
38
+ Optionally, when the session produced findings worth keeping (repeated friction, missing guardrails, unclear pointers), recommend that the user run `retro` (a user-invoked Matt handoff) to turn them into agent-environment improvements. Recommend only; never invoke it inside the workflow.
@@ -20,7 +20,7 @@ Use Matrix's own implementation and test discipline.
20
20
  - invoke `writing-for-agents` when the approved Contract changes agent-facing instructions;
21
21
  - invoke `wizard` only to generate and statically verify human-run shell steps required by the Contract; never execute the wizard end-to-end or collect secrets.
22
22
 
23
- Resolve an in-progress merge/rebase through Matrix-owned conflict handling. Do not invoke raw `resolving-merge-conflicts`, because it can stage, continue, and commit outside Matrix lifecycle ownership. Never invoke `implement` inside an active Change.
23
+ Resolve an in-progress merge/rebase through Matrix-owned conflict handling; never hand lifecycle control to a legacy `resolving-merge-conflicts` copy or any skill that stages, continues, or commits on its own. Never invoke `implement` or `implement-spec` inside an active Change.
24
24
 
25
25
  Announce each triggered capability and reason. No companion may commit, review, transition, archive, or broaden scope. A correctly installed capability gets at most one Matrix-owned fallback for that same capability; record the Skill, failure, fallback, and evidence. Installation-integrity failure stops the phase.
26
26
 
@@ -44,25 +44,25 @@ node <matrix-skill-directory>/scripts/matrix-runtime.mjs guard design
44
44
 
45
45
  ## Implementation Decision Point
46
46
 
47
- After the design guard passes and before `transition build`, pause and present the implementation-actor choice to the user. Do not transition without an explicit choice:
47
+ After the design guard passes and before `transition build`, pause and present the implementation-actor choice to the user. Do not transition without an explicit choice. The Runtime enforces this gate: `transition build` fails with `EXECUTOR_CONFIRMATION_REQUIRED` unless it carries the recorded choice, which is stored as `implementation_actor` in the change state.
48
48
 
49
49
  ### Option A: Continue with the current agent / model (default)
50
50
 
51
51
  Run transition and enter `$matrix-build`:
52
52
 
53
53
  ```powershell
54
- node <matrix-skill-directory>/scripts/matrix-runtime.mjs transition build
54
+ node <matrix-skill-directory>/scripts/matrix-runtime.mjs transition build --executor current
55
55
  ```
56
56
 
57
57
  Then enter `$matrix-build` to implement directly in this session.
58
58
 
59
59
  ### Option B: Switch the model, keep this agent
60
60
 
61
- The user switches the model in their own client (for example, a stronger model for design, an efficient one for implementation), returns to this session, and confirms. Then run transition exactly as in Option A. Matrix cannot see the client-side model; it provides the mandatory pause point only.
61
+ The user switches the model in their own client (for example, a stronger model for design, an efficient one for implementation), returns to this session, and confirms. Then run the transition with `--executor model`. Matrix cannot see the client-side model; it provides the mandatory pause point only.
62
62
 
63
63
  ### Option C: Switch the implementation agent
64
64
 
65
- First approve the frozen contract by transitioning to Build, then run `$matrix-handoff` to export a bounded task package for the chosen agent (for example Codex designs, Claude Code implements; or zcode designs, opencode implements). This:
65
+ First approve the frozen contract by transitioning to Build with the chosen agent recorded (`--executor handoff:<agent-id>`), then run `$matrix-handoff` to export a bounded task package for that agent (for example Codex designs, Claude Code implements; or zcode designs, opencode implements). This:
66
66
  - Does not change the Build phase or state beyond the preceding transition
67
67
  - Creates `artifacts/handoff-task-<task-id>.md` with all implementation details
68
68
  - Includes acceptance criteria, test seams, and required commands
@@ -1,4 +1,4 @@
1
1
  interface:
2
- display_name: "Matrix Claude Handoff"
3
- short_description: "Export frozen Matrix design as a Claude Code task"
4
- default_prompt: "Use $matrix-claude to prepare a Claude Code task package."
2
+ display_name: "Matrix Implementation Handoff"
3
+ short_description: "Export an approved Matrix Contract as a bounded task package"
4
+ default_prompt: "Use $matrix-handoff to export the approved Contract to an implementation agent."
@@ -48,6 +48,6 @@ Companion output is evidence for the Matrix proposal. It must not create another
48
48
  node <matrix-skill-directory>/scripts/matrix-runtime.mjs guard open
49
49
  # full
50
50
  node <matrix-skill-directory>/scripts/matrix-runtime.mjs transition design
51
- # hotfix/tweak, only after explicit confirmation
52
- node <matrix-skill-directory>/scripts/matrix-runtime.mjs transition build --confirmed
51
+ # hotfix/tweak, only after explicit confirmation (actor choice merged into the same pause)
52
+ node <matrix-skill-directory>/scripts/matrix-runtime.mjs transition build --confirmed --executor <choice>
53
53
  ```
@@ -13,8 +13,8 @@ Write verification prose in `artifact_language` from inspect while preserving th
13
13
 
14
14
  This phase can receive implementation from two sources:
15
15
 
16
- 1. **Direct Codex implementation** — from `$matrix-build` phase
17
- 2. **Claude Code implementation** — from `$matrix-claude` sidecar
16
+ 1. **Direct implementation** — from the `$matrix-build` phase in this session
17
+ 2. **Handed-off implementation** — from a `$matrix-handoff` task package executed by another implementation agent
18
18
 
19
19
  Regardless of source, the verification process is the same: check that implementation evidence meets the plan's requirements.
20
20
 
@@ -27,7 +27,7 @@ Before starting verification, ensure:
27
27
  - [ ] All commands from the plan's `## Validation` section have been run
28
28
  - [ ] Test results are recorded
29
29
 
30
- If these are missing (e.g., Claude Code didn't produce complete evidence), return to `$matrix-build` or ask Claude Code to补充.
30
+ If these are missing (for example, a handed-off agent did not produce complete evidence), return to `$matrix-build` or ask the implementation agent to fill in the missing evidence.
31
31
 
32
32
  ## Orchestration
33
33
 
@@ -55,7 +55,7 @@ Never label fallback output as a successful `code-review` run. `empty-head-diff`
55
55
  ## Failure Handling
56
56
 
57
57
  If verification or review fails:
58
- - If the issue is in the implementation, run `node <matrix-skill-directory>/scripts/matrix-runtime.mjs return build --reason verification-failed`, then enter `$matrix-build` or ask Claude Code to fix.
58
+ - If the issue is in the implementation, run `node <matrix-skill-directory>/scripts/matrix-runtime.mjs return build --reason verification-failed`, then enter `$matrix-build` or ask the implementation agent to fix it.
59
59
  - If the issue is in the acceptance criteria or design, run `node <matrix-skill-directory>/scripts/matrix-runtime.mjs return design --reason acceptance-or-design-gap`, then enter `$matrix-design`.
60
60
 
61
61
  Both Return paths rotate the current `verification.md` and any `review-receipt.json` into revisioned evidence history. Do not copy stale conclusions or receipts back into the new current evidence.
@@ -28,7 +28,7 @@ node <matrix-skill-directory>/scripts/matrix-runtime.mjs init <change-id> --work
28
28
  ## 编排模式
29
29
 
30
30
  - **Prim**:各阶段使用 Matrix 自身能力,不依赖 Matt Skills。
31
- - **Arch**:仅在任务事实满足触发条件时,调用活动 Change 冻结的十项自动能力。Matrix 0.1.4 审查 Matt Skills v1.2.3 官方 25 项角色,只把其中 23 项兼容 Skill 安装在项目本地;排除 `implement` 与 raw `resolving-merge-conflicts`。
31
+ - **Arch**:仅在任务事实满足触发条件时,调用活动 Change 冻结的十项自动能力。Matrix 0.1.7 审查 Matt Skills v1.3.1 官方 27 项角色,只把其中 25 项兼容 Skill 安装在项目本地;排除 `implement` 与 `implement-spec`。
32
32
 
33
33
  两种编排模式使用已选定的 workflow profile:`full` 走 `open → design → build → verify → archive`;`hotfix` 与 `tweak` 共用内部 `lightweight` profile,走 `open → build → verify → archive`,仅证据策略不同。Arch 能力不得写 Matrix 状态、推进阶段、提交、归档或创建竞争工作流。
34
34
 
@@ -71,7 +71,7 @@ full 的 `design -> build` 会批准 proposal/design/plan;lightweight 的 `ope
71
71
 
72
72
  ## 显式 Matt handoff
73
73
 
74
- Matrix 不嵌套 user-invoked Skill。适用时只解释原因,并让用户显式调用:一次性配置使用 `setup-matt-pocock-skills`,工作流外架构扫描使用 `improve-codebase-architecture`,独立大型候选生成使用 `wayfinder`,上下文边界使用 `handoff`,他人掌握阻塞事实时使用 `to-questionnaire`。`ask-matt`、`grill-with-docs`、`triage`、`to-spec`、`to-tickets`、`grill-me`、`teach`、`wait-what` 保持 standalone。Matrix 不安装也不管理 `implement` 或 raw `resolving-merge-conflicts`;已有副本保留为未管理额外项,活动 Change 内不得建议使用。
74
+ Matrix 不嵌套 user-invoked Skill。适用时只解释原因,并让用户显式调用:一次性配置使用 `setup-matt-pocock-skills`,工作流外架构扫描使用 `improve-codebase-architecture`,独立大型候选生成使用 `wayfinder`,上下文边界使用 `handoff`,会话复盘改进 agent 环境使用 `retro`,他人掌握阻塞事实时使用 `to-questionnaire`。`ask-matt`、`grill-with-docs`、`triage`、`to-spec`、`to-tickets`、`grill-me`、`teach`、`wait-what`、`pr` 保持 standalone。Matrix 不安装也不管理 `implement` 与 `implement-spec`;已有副本(含遗留的 `resolving-merge-conflicts`)保留为未管理额外项,活动 Change 内不得建议使用。
75
75
 
76
76
  ## 规则
77
77
 
@@ -80,5 +80,5 @@ Matrix 不嵌套 user-invoked Skill。适用时只解释原因,并让用户显
80
80
  - 成功 Archive 必须使用两步乐观提交:先运行 `node <matrix-skill-directory>/scripts/matrix-runtime.mjs archive --dry-run`,再将其精确 hash 传给 `node <matrix-skill-directory>/scripts/matrix-runtime.mjs archive --expect-preflight <sha256>`;不得绕过预演。
81
81
  - `node <matrix-skill-directory>/scripts/matrix-runtime.mjs doctor` 始终只读;transaction 与陈旧锁 repair 必须显式绑定 doctor 报告的 identity,不得手工编辑或删除 `.matrix/transactions/`、`.matrix/workflow.lock`。
82
82
  - 重大架构/范围决定和 archive/commit 前应暂停等待用户确认。
83
- - 进入 Build 前(full 在 design guard 之后;lightweight 并入 `transition build --confirmed` 的暂停)必须呈现执行主体选择——当前 agent/原模型继续、用户客户端切换模型、或经 `$matrix-handoff` 切换实现 agent——并将决策随 Contract 记录。
83
+ - 进入 Build 前(full 在 design guard 之后;lightweight 并入 `transition build --confirmed` 的暂停)必须呈现执行主体选择——当前 agent/原模型继续、用户客户端切换模型、或经 `$matrix-handoff` 切换实现 agent——并以 `transition build --executor <current|model|handoff:<agent-id>>` 把选择传给 Runtime(lightweight 同时携带 `--confirmed`)。缺省时 Runtime 拒绝进入 Build,并把选择存为 `implementation_actor`。
84
84
  - shortcut 范围扩大时执行 `node <matrix-skill-directory>/scripts/matrix-runtime.mjs return design --reason design-gap`;Runtime 将其升级为 full。
@@ -34,3 +34,5 @@ node <matrix-skill-directory>/scripts/matrix-runtime.mjs return design --reason
34
34
  ```
35
35
 
36
36
  提交成功后,变更位于 `.matrix/archive/`,工作流完成。
37
+
38
+ 可选:当本次会话产生了值得保留的发现(反复出现的摩擦、缺失的护栏、不清晰的指针等),可建议用户显式运行 `retro`(user-invoked Matt handoff)把它们转化为 agent 环境改进。仅建议,绝不在工作流内代为调用。
@@ -1,41 +1,41 @@
1
- ---
2
- name: matrix-build
3
- description: 用测试优先的纵向切片实现冻结的 Matrix 计划。仅在活动 Matrix 阶段为 build 时使用。
4
- ---
5
-
6
- # Matrix Build
7
-
8
- 读取 `node <matrix-skill-directory>/scripts/matrix-runtime.mjs inspect` 与已批准 Contract。full/legacy-full 读取 proposal/design/plan;lightweight 只读取紧凑 proposal,不补写 Design 产物。
9
-
10
- Build 证据与面向用户的说明使用 `artifact_language`;Runtime 所需英文标题 token 保持不变。
11
-
12
- ## 编排
13
-
14
- - **Prim**:使用 Matrix 自身实现与测试能力。
15
- - **Arch**:可测试行为变化时调用 `tdd`;真实失败且根因未知时调用 `diagnosing-bugs`;批准 Contract 修改 agent-facing 指令时调用 `writing-for-agents`;只有 Contract 需要人工执行 shell 步骤时,才用 `wizard` 生成并静态验证脚本,绝不端到端执行或收集秘密。
16
-
17
- 进行中的 merge/rebase 冲突由 Matrix 自身治理,不调用会 stage、continue 或 commit 的 raw `resolving-merge-conflicts`。活动 Change 内不得调用 `implement`。
18
-
19
- 调用前说明能力与原因。companion 不得 commit、review、transition、archive 或扩大范围。正确安装的能力失败时最多执行一次同能力 Matrix fallback,并记录调用、失败、fallback 与证据;安装完整性问题必须停止并修复。
20
-
21
- ## 范围纪律
22
-
23
- 若实现暴露范围或设计变化,停止并执行:
24
-
25
- ```powershell
26
- node <matrix-skill-directory>/scripts/matrix-runtime.mjs return design --reason design-gap
27
- ```
28
-
29
- 回到 Design 会清除既有 Contract 批准并轮换当前验证证据;重新进入 Build 必须生成新的批准与新证据。
30
-
31
- ## 流程
32
-
33
- 1. 对可测试的行为变化使用 `tdd` 纵向切片;纯文档或不可观察的机械变更使用相称的静态检查。
34
- 2. 在 `## Build evidence` 记录共同事实;hotfix 另写 `## Reproduction evidence`、`## Root cause`,tweak 另写 `## Scope evidence`。
35
- 3. 运行 Build guard;它同时确认批准的 Contract 仍精确匹配。
36
- 4. 通过后推进至 Verify,并进入 `$matrix-verify`。
37
-
38
- ```powershell
39
- node <matrix-skill-directory>/scripts/matrix-runtime.mjs guard build
40
- node <matrix-skill-directory>/scripts/matrix-runtime.mjs transition verify
41
- ```
1
+ ---
2
+ name: matrix-build
3
+ description: 用测试优先的纵向切片实现冻结的 Matrix 计划。仅在活动 Matrix 阶段为 build 时使用。
4
+ ---
5
+
6
+ # Matrix Build
7
+
8
+ 读取 `node <matrix-skill-directory>/scripts/matrix-runtime.mjs inspect` 与已批准 Contract。full/legacy-full 读取 proposal/design/plan;lightweight 只读取紧凑 proposal,不补写 Design 产物。
9
+
10
+ Build 证据与面向用户的说明使用 `artifact_language`;Runtime 所需英文标题 token 保持不变。
11
+
12
+ ## 编排
13
+
14
+ - **Prim**:使用 Matrix 自身实现与测试能力。
15
+ - **Arch**:可测试行为变化时调用 `tdd`;真实失败且根因未知时调用 `diagnosing-bugs`;批准 Contract 修改 agent-facing 指令时调用 `writing-for-agents`;只有 Contract 需要人工执行 shell 步骤时,才用 `wizard` 生成并静态验证脚本,绝不端到端执行或收集秘密。
16
+
17
+ 进行中的 merge/rebase 冲突由 Matrix 自身治理,不得把生命周期控制交给遗留的 `resolving-merge-conflicts` 副本或任何会自行 stage、continue、commit 的 skill。活动 Change 内不得调用 `implement` 与 `implement-spec`。
18
+
19
+ 调用前说明能力与原因。companion 不得 commit、review、transition、archive 或扩大范围。正确安装的能力失败时最多执行一次同能力 Matrix fallback,并记录调用、失败、fallback 与证据;安装完整性问题必须停止并修复。
20
+
21
+ ## 范围纪律
22
+
23
+ 若实现暴露范围或设计变化,停止并执行:
24
+
25
+ ```powershell
26
+ node <matrix-skill-directory>/scripts/matrix-runtime.mjs return design --reason design-gap
27
+ ```
28
+
29
+ 回到 Design 会清除既有 Contract 批准并轮换当前验证证据;重新进入 Build 必须生成新的批准与新证据。
30
+
31
+ ## 流程
32
+
33
+ 1. 对可测试的行为变化使用 `tdd` 纵向切片;纯文档或不可观察的机械变更使用相称的静态检查。
34
+ 2. 在 `## Build evidence` 记录共同事实;hotfix 另写 `## Reproduction evidence`、`## Root cause`,tweak 另写 `## Scope evidence`。
35
+ 3. 运行 Build guard;它同时确认批准的 Contract 仍精确匹配。
36
+ 4. 通过后推进至 Verify,并进入 `$matrix-verify`。
37
+
38
+ ```powershell
39
+ node <matrix-skill-directory>/scripts/matrix-runtime.mjs guard build
40
+ node <matrix-skill-directory>/scripts/matrix-runtime.mjs transition verify
41
+ ```
@@ -24,15 +24,15 @@ node <matrix-skill-directory>/scripts/matrix-runtime.mjs guard design
24
24
 
25
25
  ## 执行主体确认点(Build 前)
26
26
 
27
- `guard design` 通过后、`transition build` 之前,必须暂停并向用户确认执行主体,未确认不得推进:
27
+ `guard design` 通过后、`transition build` 之前,必须暂停并向用户确认执行主体,未确认不得推进。该闸门由 Runtime 强制执行:`transition build` 未携带所记录的选择时报 `EXECUTOR_CONFIRMATION_REQUIRED`,选择会以 `implementation_actor` 写入 change 状态:
28
28
 
29
29
  - **选项 A:当前 agent / 原模型继续(默认)**——直接推进:
30
30
  ```powershell
31
- node <matrix-skill-directory>/scripts/matrix-runtime.mjs transition build
31
+ node <matrix-skill-directory>/scripts/matrix-runtime.mjs transition build --executor current
32
32
  ```
33
33
  随后进入 `$matrix-build` 在本会话内实现。
34
- - **选项 B:切换模型、保留本 agent**——用户在自己的客户端完成模型切换(例如设计用强模型、实现用高性价比模型),回到会话确认后按选项 A 推进。Matrix 无法感知客户端模型,只提供强制暂停点。
35
- - **选项 C:切换执行 agent**——先推进进 Build 批准冻结 Contract,再用 `$matrix-handoff` 为目标 agent 导出有界任务包(例如 Codex 设计、Claude Code 实现,或 zcode 设计、opencode 实现)。实现 agent 完成并补齐 `## Build evidence` 后:
34
+ - **选项 B:切换模型、保留本 agent**——用户在自己的客户端完成模型切换(例如设计用强模型、实现用高性价比模型),回到会话确认后以 `--executor model` 推进。Matrix 无法感知客户端模型,只提供强制暂停点。
35
+ - **选项 C:切换执行 agent**——先以 `--executor handoff:<agent-id>` 记录所选 agent 并推进 Build 批准冻结 Contract,再用 `$matrix-handoff` 为目标 agent 导出有界任务包(例如 Codex 设计、Claude Code 实现,或 zcode 设计、opencode 实现)。实现 agent 完成并补齐 `## Build evidence` 后:
36
36
  ```powershell
37
37
  node <matrix-skill-directory>/scripts/matrix-runtime.mjs guard build
38
38
  node <matrix-skill-directory>/scripts/matrix-runtime.mjs transition verify
@@ -35,6 +35,6 @@ companion 输出只能作为当前 proposal 的证据,不得创建第二套工
35
35
  node <matrix-skill-directory>/scripts/matrix-runtime.mjs guard open
36
36
  # full
37
37
  node <matrix-skill-directory>/scripts/matrix-runtime.mjs transition design
38
- # hotfix/tweak,仅在用户明确确认后
39
- node <matrix-skill-directory>/scripts/matrix-runtime.mjs transition build --confirmed
38
+ # hotfix/tweak,仅在用户明确确认后(执行主体选择并入同一次暂停)
39
+ node <matrix-skill-directory>/scripts/matrix-runtime.mjs transition build --confirmed --executor <choice>
40
40
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rosenfan/matrix",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Evidence-driven development workflow with Prim and Arch agent-skill orchestration.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/catalog.js CHANGED
@@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url";
6
6
  import { MATT_INSTALLABLE_SKILLS, MATT_ROLES } from "./matt-catalog.mjs";
7
7
 
8
8
  export const LANGUAGE_IDS = ["en", "zh-CN"];
9
- export const PLATFORM_IDS = ["claude-code", "codex"];
9
+ export const PLATFORM_IDS = ["claude-code", "codex", "zcode"];
10
10
  export const MATRIX_SKILLS = [
11
11
  "matrix", "matrix-open", "matrix-design", "matrix-build", "matrix-verify",
12
12
  "matrix-archive", "matrix-hotfix", "matrix-tweak", "matrix-status", "matrix-handoff"
@@ -28,6 +28,14 @@ export const PLATFORMS = {
28
28
  id: "codex", name: "Codex", skillsCliAgent: "codex",
29
29
  skillRoot(base) { return path.join(base, ".agents", "skills"); },
30
30
  legacyRoots(base) { return [path.join(base, ".codex", "skills")]; }
31
+ },
32
+ // Agents that read the Agent Skills open-standard directory (.agents/skills)
33
+ // by default — opencode, Kimi Code, and other compatible tools — share the
34
+ // Codex installation and need no dedicated platform entry.
35
+ zcode: {
36
+ id: "zcode", name: "ZCode", skillsCliAgent: "zcode",
37
+ skillRoot(base) { return path.join(base, ".zcode", "skills"); },
38
+ legacyRoots() { return []; }
31
39
  }
32
40
  };
33
41
 
package/src/cli.js CHANGED
@@ -2,7 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { spawnSync } from "node:child_process";
4
4
  import { fileURLToPath } from "node:url";
5
- import { PLATFORMS } from "./catalog.js";
5
+ import { PLATFORMS, PLATFORM_IDS } from "./catalog.js";
6
6
  import { createDistribution, installedIntent } from "./distribution.js";
7
7
  import { createMattAdapter, inspectMattInstallation } from "./matt.js";
8
8
  import { text } from "./messages.js";
@@ -15,7 +15,7 @@ import { MATT_COMPATIBILITY } from "./matt-catalog.mjs";
15
15
 
16
16
  const packagePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
17
17
  const version = JSON.parse(fs.readFileSync(packagePath, "utf8")).version;
18
- const HELP = `Matrix ${version}\n\nUsage: matrix init [directory] [options]\n matrix update [directory] [options]\n matrix doctor [directory] [options]\n\nOptions:\n --language <en|zh-CN>\n --scope <project|global>\n --platform <claude-code|codex> (repeatable)\n --mode <copy|symlink>\n --with-mattpocock | --without-mattpocock\n --force-matt (with --with-mattpocock; replace modified Matt Skills)\n --default-orchestration <prim|arch>\n update only: --all | --project-only (update scope across indexed projects)\n --skip-self-update --force --yes, -y --dry-run --json --no-color`;
18
+ const HELP = `Matrix ${version}\n\nUsage: matrix init [directory] [options]\n matrix update [directory] [options]\n matrix doctor [directory] [options]\n\nOptions:\n --language <en|zh-CN>\n --scope <project|global>\n --platform <${PLATFORM_IDS.join("|")}> (repeatable)\n --mode <copy|symlink>\n --with-mattpocock | --without-mattpocock\n --force-matt (with --with-mattpocock; replace modified Matt Skills)\n --default-orchestration <prim|arch>\n update only: --all | --project-only (update scope across indexed projects)\n --skip-self-update --force --yes, -y --dry-run --json --no-color`;
19
19
 
20
20
  function parse(argv) {
21
21
  const result = { command: null, directory: null, scope: null, language: null, platforms: [], mode: "copy", matt: null, mattPolicy: "preserve", defaultOrchestration: null, matrixPolicy: "safe", yes: false, dryRun: false, json: false, color: true, skipSelfUpdate: false, reexec: false, all: false, projectOnly: false };
@@ -69,7 +69,7 @@ async function intentFrom(options, ui) {
69
69
  const language = options.language ?? (options.yes ? "en" : await ui.select("Language / 语言", [{ label: "English", value: "en" }, { label: "中文", value: "zh-CN" }]));
70
70
  const scope = options.scope ?? (options.yes ? "project" : await ui.select(text(language, "scopeQuestion"), [{ label: text(language, "project"), value: "project" }, { label: text(language, "global"), value: "global" }]));
71
71
  let platforms = options.platforms;
72
- if (!platforms.length && interactive && !options.yes) platforms = await ui.selectMany(text(language, "platformsQuestion"), [{ label: "Claude Code", value: "claude-code" }, { label: "Codex", value: "codex" }]);
72
+ if (!platforms.length && interactive && !options.yes) platforms = await ui.selectMany(text(language, "platformsQuestion"), PLATFORM_IDS.map((id) => ({ label: PLATFORMS[id].name, value: id })));
73
73
  const matt = options.matt ?? (interactive && !options.yes ? (await ui.confirm(text(language, "mattQuestion"), true, labelsFor(language)) ? "missing" : "none") : "missing");
74
74
  const defaultOrchestration = options.defaultOrchestration ?? (interactive && !options.yes && matt === "missing"
75
75
  ? await ui.select(text(language, "orchestrationQuestion"), [
@@ -115,12 +115,12 @@ async function refresh(intent, options, ui) {
115
115
  }
116
116
  if (options.json) {
117
117
  if (!evaluation.ok || options.dryRun) {
118
- const output = options.updateMode ? { ...evaluation, matt: inspectMattInstallation({ projectRoot: intent.projectRoot, home: intent.home, platforms: intent.platforms }) } : evaluation;
118
+ const output = options.updateMode ? { ...evaluation, matt: inspectMattInstallation({ projectRoot: intent.projectRoot, home: intent.home, platforms: intent.platforms, action: "matrix update --with-mattpocock" }) } : evaluation;
119
119
  console.log(JSON.stringify(output)); return evaluation.ok ? 0 : 2;
120
120
  }
121
121
  const result = distribution.commit(evaluation.plan);
122
122
  const output = { ...evaluation, ...result, intent: evaluation.intent, observations: evaluation.observations, actions: evaluation.actions,
123
- ...(options.updateMode ? { matt: inspectMattInstallation({ projectRoot: intent.projectRoot, home: intent.home, platforms: intent.platforms }) } : {}) };
123
+ ...(options.updateMode ? { matt: inspectMattInstallation({ projectRoot: intent.projectRoot, home: intent.home, platforms: intent.platforms, action: "matrix update --with-mattpocock" }) } : {}) };
124
124
  console.log(JSON.stringify(output));
125
125
  return !result.ok ? 2 : result.code === "PARTIAL" ? 1 : 0;
126
126
  }
@@ -40,12 +40,12 @@ const configContent = (existing, defaultOrchestration, scope, language) => {
40
40
  const values = {
41
41
  ...existing,
42
42
  schema: "matrix/config/v1",
43
- auto_transition: existing.auto_transition === "false" ? "false" : "true",
44
43
  default_orchestration: defaultOrchestration,
45
44
  installation_scope: scope,
46
45
  language
47
46
  };
48
- const preferred = ["schema", "auto_transition", "default_orchestration", "installation_scope", "language"];
47
+ delete values.auto_transition;
48
+ const preferred = ["schema", "default_orchestration", "installation_scope", "language"];
49
49
  const keys = [...preferred, ...Object.keys(values).filter((key) => !preferred.includes(key)).sort()];
50
50
  return `${keys.map((key) => `${key}: ${values[key]}`).join("\n")}\n`;
51
51
  };
package/src/installer.js CHANGED
@@ -1,45 +1,45 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { createDistribution } from "./distribution.js";
4
- import { MATRIX_SKILLS, MATT_SKILLS, PLATFORMS, sourceSkillsRoot } from "./catalog.js";
5
-
6
- export { MATRIX_SKILLS, MATT_SKILLS, sourceSkillsRoot };
7
-
8
- export function destinationFor({ scope, projectRoot, platform = "claude-code", home }) {
9
- const base = scope === "global" ? (home ?? process.env.USERPROFILE ?? process.env.HOME) : projectRoot;
10
- return PLATFORMS[platform].skillRoot(base);
11
- }
12
-
13
- export function inspectInstallation(destination) {
14
- const inspect = (names) => names.filter((skill) => {
15
- try { return Boolean(destination) && requireExists(destination, skill); } catch { return false; }
16
- });
17
- const installed = inspect(MATRIX_SKILLS);
18
- const mattInstalled = inspect(MATT_SKILLS);
19
- return { installed, missing: MATRIX_SKILLS.filter((skill) => !installed.includes(skill)), mattInstalled, mattMissing: MATT_SKILLS.filter((skill) => !mattInstalled.includes(skill)) };
20
- }
21
-
22
- function requireExists(destination, skill) {
23
- // Avoid exposing a second installation policy through this legacy helper.
24
- return fs.existsSync(path.join(destination, skill, "SKILL.md"));
25
- }
26
-
27
- export function installMatrix({ destination, mode = "copy", conflict = "error", dryRun = false }) {
28
- const platform = destination?.includes(".agents") ? "codex" : "claude-code";
29
- const projectRoot = path.resolve(destination, "..", "..");
30
- const distribution = createDistribution();
31
- const evaluation = distribution.evaluate({ projectRoot, scope: "project", platforms: [platform], mode, matrixPolicy: conflict === "replace" ? "replace" : "safe", dryRun });
32
- if (!evaluation.ok) throw new Error(evaluation.diagnostics?.[0]?.message ?? evaluation.code);
33
- const result = distribution.commit(evaluation.plan);
34
- if (!result.ok) throw new Error(result.error ?? result.code);
35
- return { installed: result.committed ?? MATRIX_SKILLS, skipped: [], existing: [], backupRoot: result.backups?.[0] ? result.backups[0].backup : null };
36
- }
37
-
38
- export function installMattSkills({ projectRoot, platforms = ["claude-code"], skills = MATT_SKILLS, global = false, timeout = 300_000 }) {
39
- void projectRoot; void platforms; void skills; void global; void timeout;
40
- const error = new Error("Direct Matt installation was retired; run matrix init --with-mattpocock so Distribution owns the complete Matrix/Matt transaction.");
41
- error.code = "MATT_DIRECT_INSTALL_UNSUPPORTED";
42
- throw error;
43
- }
44
-
45
- export function findPython() { return null; }
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { createDistribution } from "./distribution.js";
4
+ import { MATRIX_SKILLS, MATT_SKILLS, PLATFORMS, PLATFORM_IDS, sourceSkillsRoot } from "./catalog.js";
5
+
6
+ export { MATRIX_SKILLS, MATT_SKILLS, sourceSkillsRoot };
7
+
8
+ export function destinationFor({ scope, projectRoot, platform = "claude-code", home }) {
9
+ const base = scope === "global" ? (home ?? process.env.USERPROFILE ?? process.env.HOME) : projectRoot;
10
+ return PLATFORMS[platform].skillRoot(base);
11
+ }
12
+
13
+ export function inspectInstallation(destination) {
14
+ const inspect = (names) => names.filter((skill) => {
15
+ try { return Boolean(destination) && requireExists(destination, skill); } catch { return false; }
16
+ });
17
+ const installed = inspect(MATRIX_SKILLS);
18
+ const mattInstalled = inspect(MATT_SKILLS);
19
+ return { installed, missing: MATRIX_SKILLS.filter((skill) => !installed.includes(skill)), mattInstalled, mattMissing: MATT_SKILLS.filter((skill) => !mattInstalled.includes(skill)) };
20
+ }
21
+
22
+ function requireExists(destination, skill) {
23
+ // Avoid exposing a second installation policy through this legacy helper.
24
+ return fs.existsSync(path.join(destination, skill, "SKILL.md"));
25
+ }
26
+
27
+ export function installMatrix({ destination, mode = "copy", conflict = "error", dryRun = false }) {
28
+ const projectRoot = path.resolve(destination, "..", "..");
29
+ const platform = PLATFORM_IDS.find((id) => PLATFORMS[id].skillRoot(projectRoot) === path.resolve(destination)) ?? "claude-code";
30
+ const distribution = createDistribution();
31
+ const evaluation = distribution.evaluate({ projectRoot, scope: "project", platforms: [platform], mode, matrixPolicy: conflict === "replace" ? "replace" : "safe", dryRun });
32
+ if (!evaluation.ok) throw new Error(evaluation.diagnostics?.[0]?.message ?? evaluation.code);
33
+ const result = distribution.commit(evaluation.plan);
34
+ if (!result.ok) throw new Error(result.error ?? result.code);
35
+ return { installed: result.committed ?? MATRIX_SKILLS, skipped: [], existing: [], backupRoot: result.backups?.[0] ? result.backups[0].backup : null };
36
+ }
37
+
38
+ export function installMattSkills({ projectRoot, platforms = ["claude-code"], skills = MATT_SKILLS, global = false, timeout = 300_000 }) {
39
+ void projectRoot; void platforms; void skills; void global; void timeout;
40
+ const error = new Error("Direct Matt installation was retired; run matrix init --with-mattpocock so Distribution owns the complete Matrix/Matt transaction.");
41
+ error.code = "MATT_DIRECT_INSTALL_UNSUPPORTED";
42
+ throw error;
43
+ }
44
+
45
+ export function findPython() { return null; }
@@ -1,11 +1,11 @@
1
1
  import crypto from "node:crypto";
2
2
 
3
3
  export const MATT_COMPATIBILITY = Object.freeze({
4
- matrixVersion: "0.1.6",
5
- release: "v1.2.3",
6
- commit: "6acc160e4e0cd062dbbbd7a1b26ae92855edf07e",
7
- archive: "https://github.com/mattpocock/skills/archive/6acc160e4e0cd062dbbbd7a1b26ae92855edf07e.tar.gz",
8
- skillsCli: "skills@1.5.22"
4
+ matrixVersion: "0.1.8",
5
+ release: "v1.3.1",
6
+ commit: "24fe0ef7737efae15c87225755e9f6f5965e4888",
7
+ archive: "https://github.com/mattpocock/skills/archive/24fe0ef7737efae15c87225755e9f6f5965e4888.tar.gz",
8
+ skillsCli: "skills@1.7.4"
9
9
  });
10
10
 
11
11
  export const MATT_ROLES = Object.freeze({
@@ -14,12 +14,12 @@ export const MATT_ROLES = Object.freeze({
14
14
  "tdd", "diagnosing-bugs", "code-review", "wizard", "writing-for-agents"
15
15
  ]),
16
16
  handoff: Object.freeze([
17
- "setup-matt-pocock-skills", "improve-codebase-architecture", "wayfinder", "handoff", "to-questionnaire"
17
+ "setup-matt-pocock-skills", "improve-codebase-architecture", "wayfinder", "handoff", "to-questionnaire", "retro"
18
18
  ]),
19
19
  standalone: Object.freeze([
20
- "ask-matt", "grill-with-docs", "triage", "to-spec", "to-tickets", "grill-me", "teach", "wait-what"
20
+ "ask-matt", "grill-with-docs", "triage", "to-spec", "to-tickets", "grill-me", "teach", "wait-what", "pr"
21
21
  ]),
22
- incompatible: Object.freeze(["implement", "resolving-merge-conflicts"])
22
+ incompatible: Object.freeze(["implement", "implement-spec"])
23
23
  });
24
24
 
25
25
  export const MATT_AUTOMATIC_SKILLS = MATT_ROLES.automatic;
@@ -29,35 +29,37 @@ export const MATT_INSTALLABLE_SKILLS = Object.freeze([
29
29
  ...MATT_ROLES.standalone
30
30
  ]);
31
31
  export const MATT_OFFICIAL_SKILLS = Object.freeze(Object.values(MATT_ROLES).flat());
32
- // Content identities produced by skills@1.5.22 --copy from the exact v1.2.3 archive.
32
+ // Content identities produced by skills@1.7.4 --copy from the exact v1.3.1 archive.
33
33
  // They let init classify existing project files before any target mutation without
34
34
  // bundling or redistributing Matt's Skill contents.
35
35
  export const MATT_CONTENT_HASHES = Object.freeze({
36
- "grilling": "f6b0b6a07f6f51a7b39dfed4c737bd5990149e6ac227265dc457eb962e40fde6",
37
- "domain-modeling": "8d774870ffd93ad77b07f56bbe59fb76c8f9e246fe49e0181ed65a519fc28929",
38
- "research": "3bbc342f7484484d31118fd7cb148690aba2600b318d6b680edc6ce2c2399948",
39
- "prototype": "08f5e9d003dfcbc274f4a31864c9fcbc2962ad8574f40e7bba7d78193f5d349f",
40
- "codebase-design": "0050828acbec9af9711ac1376749acaf358fb1bbfd98c80fc20df7a26658ccd7",
41
- "tdd": "775d2b0eb45fdf7a102f0ec0cfca8448c5d441c49373e0a89bc7a22dbe892c3c",
42
- "diagnosing-bugs": "9fc5749a1671a7c894cc538fbbedda19890bd20f297eb6095ba0969a933ed2dc",
43
- "code-review": "e4c202dae670331dab5aef98a05594a45ec3b8210a8f9c2ab8a8bba848da4c8e",
44
- "wizard": "991cb02af3f4584b07f5b6ce30176d05bf2622152a8776db9832825f80601e3d",
45
- "writing-for-agents": "f58ac3a5e4034da89bd31ff86ffdc74df9e986ef818055d9a399f05fc3b37a1b",
46
- "setup-matt-pocock-skills": "cb2d75f72fb70ba283a5d76f2722d090a00cb4d3e227e32b05a547193e561800",
47
- "improve-codebase-architecture": "986e4245d52053692fc866c4c3065a3b944fc2f27a1ea99785a2b116d785e964",
48
- "wayfinder": "a99de1eab6ae2950b5d4146abfd58257a3e0cfdcbd5e9611f27b2a485fd52242",
49
- "handoff": "9f8a832aafee3eec6dae6902d758b2438fefb116a6afb9914777238538b14d2b",
50
- "to-questionnaire": "9a27c55ce8d45b8a7f1a7309bece202bebcd7cab359f551aeeeb3d5c320a3b55",
51
- "ask-matt": "55c6191c39ad1727d8285225db5a97e622026f6c861f1c924dbc92d8c433e344",
52
- "grill-with-docs": "05c1b216a482ea0fd5a5281e96b0f1b5fd5e1c1e9edd572709c71944ef6fd7d6",
53
- "triage": "7009d742e1cf0e40b0d8de8fc47ece25161795da595bd7017a170426dbb51d9a",
54
- "to-spec": "969b7955a044939809487b416d1e907021c6ad4b07bd5979f46e2f1c629ffd01",
55
- "to-tickets": "b7c6499e10c567abdbfca7f5c730f2de91584a517816ace5aaaff49f4790e947",
56
- "grill-me": "cec05582f13830a7bd63984474b999c806a7b93935ed638a27f32eac1b7f976b",
57
- "teach": "209d2ead53b2c6eba2425198f6f09eb51fd2bde490c34da2d93b187b4b749414",
58
- "wait-what": "345b8e9aba9f134944f37ae7807cadd19c1b6866d77e3414f2e8f3835061aac0",
36
+ "grilling": "229e9190ddcdf65fcfa68aeebe6edfcf3b039967e4d5321c48325ea197836b88",
37
+ "domain-modeling": "332c4e23360e692a488fa25fe878596f91e4eaf125f48ea280dca151f3798eaa",
38
+ "research": "8766e592808cb8e6951dd8e4d414f4fef3ee9dde1c487b2e5af7461027f794e2",
39
+ "prototype": "5fc3162feb286bc773e21c208c440de81f66ef36e6ae2fd2189a54756af4468c",
40
+ "codebase-design": "17a6a1ab67eff070a38564834d6e3ff82bc2d46cdc888a57f6bc18c3efe6442b",
41
+ "tdd": "cf0a63fd20988baef0a31d2d8467e70319fdc75d5bb3eaa60620abcedb97574c",
42
+ "diagnosing-bugs": "bc1ade4a8b7e988aed02c251d3d0d8cdbedc74ce3361a244e2cf12cbb6744d74",
43
+ "code-review": "9a883c4392f27b6d45c6a51d12d168897ba7707a5e70f8d45a042b41db58d5a5",
44
+ "wizard": "b2793f1654b9c518eb85386cc51f9218030c6dc83772bea3c128cb156c972300",
45
+ "writing-for-agents": "46d0c6f716d2c4cbb27ea157fd3e18675389fecf3d722343f05c82b7c1e61bf8",
46
+ "setup-matt-pocock-skills": "7fd2c1e02b10bdd59f2c15a7cbc364e247286884c1c5cdd21704f7cbc769b42a",
47
+ "improve-codebase-architecture": "4502270f003e0702f19da6c2436daffe0f9d4dafcea3de2759347b99c5a5c404",
48
+ "wayfinder": "a774efe838aede8db313dbf3cd16660075803b3ed3657d3297ec913ea67fad49",
49
+ "handoff": "5aff146205749c5daab83c1f0299b910a1caaa403bcd37f59f35513a6c13dc12",
50
+ "to-questionnaire": "51d0425a65248e2d5ca1b4d06e4f854cb131e49a43326f54a81690ce28e27535",
51
+ "retro": "e7b2f28bf8e88e539791db6f9ec566252b56afc179d5b881486340a477ec59bd",
52
+ "ask-matt": "b1cf1467569d4199e6cec74a3fa9036d42bb208e76084e5d5030e23cd7f1f1dc",
53
+ "grill-with-docs": "bbf60554b6f102ed734211b26c5d3cc237be19e0dd5d93c1a8dea1367d6ebc23",
54
+ "triage": "f44745c02d710fee1bd12e036f8a4d53a4615018c255dd90dba7ec09a23ebb6d",
55
+ "to-spec": "23e7133ad3781b22ddc23335c88a975b71ebc809a7b5f734b158bb05abaaa5e8",
56
+ "to-tickets": "b7296c8466d53eb832047afad28932db82b1f31da4b647ec0c63a5af824a8bbc",
57
+ "grill-me": "b37db6d78417792b8dd9a478bc8fcdf7a008dcb94cb172247b01fda6205ba6e3",
58
+ "teach": "78966fd2b3ab60f3c08c25660d7a237325c1a243a9ba1103e7dc77edf9713825",
59
+ "wait-what": "ea4bde2069b457b52a0b2ba39bfc686a54fff73b79c91ef974224d0215298a9f",
60
+ "pr": "4f2bdb1aaf75c6cd0f4696889ff1022cff86ab81c3c1e3a1ba4ce00964d98146",
59
61
  "implement": "3180dbe245245f99dceb669e597cf633935cc87be0ca3c39a86dde54e62a4db9",
60
- "resolving-merge-conflicts": "cf66bf26fb8a943d426271120f033825f7ed591349812ff4f636d3db56c976dc"
62
+ "implement-spec": "9f2d9a27a67a0edee8bcc1c12c5dae69ea77ffaf2a4898a939c6afd0c7db7f47"
61
63
  });
62
64
  export const MATT_CATALOG_DIGEST = crypto.createHash("sha256").update(JSON.stringify({
63
65
  compatibility: MATT_COMPATIBILITY,
package/src/messages.js CHANGED
@@ -5,7 +5,7 @@ export const messages = {
5
5
  project: "Project (current directory)", global: "Global Matrix Skills",
6
6
  copy: "Copy (recommended)", symlink: "Symlink (advanced)",
7
7
  matrix: "Matrix", matt: "Matt Pocock skills", installed: "installed", ready: "Matrix is ready.",
8
- noPlatform: "No verified platform was detected. Select Claude Code, Codex, or both.",
8
+ noPlatform: "No verified platform was detected. Select Claude Code, Codex, or ZCode.",
9
9
  preview: "Planned changes", confirm: "Apply these changes?", doctor: "Installation diagnosis",
10
10
  languageQuestion: "Language", scopeQuestion: "Installation scope", platformsQuestion: "Platforms", orchestrationQuestion: "Default orchestration",
11
11
  orchestrationArchDetail: "Orchestrates the installed Matt Skills cohort (requires Matt Skills).", orchestrationPrimDetail: "Uses Matrix's own capabilities only (no Matt dependency).",
@@ -37,7 +37,7 @@ export const messages = {
37
37
  project: "项目(当前目录)", global: "全局 Matrix Skills",
38
38
  copy: "复制(推荐)", symlink: "符号链接(高级)",
39
39
  matrix: "Matrix", matt: "Matt Pocock skills", installed: "已安装", ready: "Matrix 已就绪。",
40
- noPlatform: "未检测到已验证的平台。请选择 Claude Code、Codex,或两者。",
40
+ noPlatform: "未检测到已验证的平台。请选择 Claude Code、Codex 或 ZCode。",
41
41
  preview: "计划变更", confirm: "确认应用这些变更?", doctor: "安装诊断",
42
42
  matrixModifiedDiagnostic: "{platform}:现有 Matrix 资产无法确认由 Matrix 管理。", mattModifiedDiagnostic: "{platform}:Matt Skills 存在本地修改或不可读内容,需要单独授权替换。", archIncompleteDiagnostic: "Arch 需要精确匹配已审查集合和可信项目 receipt。请使用 --with-mattpocock 重试。", matrixStateDiagnostic: "{platform}:Matrix 状态为 {state}。", mattStateDiagnostic: "{platform}:Matt Pocock Skills 状态为 {state}。", codexLegacyDiagnostic: "检测到 Codex 旧版 .codex/skills;Matrix 不会写入该目录。", unmanagedExtraDiagnostic: "{platform}:将保留但不会调用这些非托管附加 Skills:{skills}。"
43
43
  }
@@ -1,76 +1,76 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { MATRIX_VERSION, PLATFORMS, hashDirectory, releaseCatalog } from "./catalog.js";
4
-
5
- function readJson(file) {
6
- try { return JSON.parse(fs.readFileSync(file, "utf8")); } catch { return null; }
7
- }
8
-
9
- function installationScope(projectRoot) {
10
- try {
11
- const line = fs.readFileSync(path.join(projectRoot, ".matrix", "config.yaml"), "utf8").split(/\r?\n/).find((value) => value.startsWith("installation_scope:"));
12
- return line?.split(":").slice(1).join(":").trim() === "global" ? "global" : "project";
13
- } catch { return "project"; }
14
- }
15
-
16
- function runtimeCommand(runtime) {
17
- const quoted = process.platform === "win32" ? `"${runtime}"` : `'${runtime.replaceAll("'", "'\\''")}'`;
18
- return `node ${quoted}`;
19
- }
20
-
21
- export function resolveProjectRuntime({ projectRoot = process.cwd() } = {}) {
22
- const root = path.resolve(projectRoot);
23
- const manifest = readJson(path.join(root, ".matrix", "installation.json"));
24
- if (!manifest) return installationScope(root) === "global"
25
- ? { ok: true, code: "GLOBAL_INSTALLATION", runtime: null }
26
- : { ok: true, code: "PROJECT_INSTALLATION_ABSENT", runtime: null };
27
- const candidates = [];
28
- let currentCatalog = null;
29
- let catalogUnavailable = false;
30
- try { currentCatalog = releaseCatalog(manifest.language ?? "en"); } catch { catalogUnavailable = true; }
31
- for (const platform of ["codex", "claude-code"]) {
32
- const expected = manifest.platforms?.[platform]?.skills?.matrix?.hash;
33
- if (!expected) continue;
34
- const matrixRoot = path.join(PLATFORMS[platform].skillRoot(root), "matrix");
35
- const runtime = path.join(matrixRoot, "scripts", "matrix-runtime.mjs");
36
- if (!fs.existsSync(runtime)) { candidates.push({ platform, matrixRoot, runtime, reason: "missing" }); continue; }
37
- try {
38
- const actual = hashDirectory(matrixRoot);
39
- if (actual === expected) {
40
- if (manifest.catalogVersion !== MATRIX_VERSION) {
41
- const catalogVersion = manifest.catalogVersion ?? null;
42
- return {
43
- ok: false,
44
- code: catalogVersion ? "PROJECT_RUNTIME_VERSION_MISMATCH" : "PROJECT_RUNTIME_VERSION_UNKNOWN",
45
- message: catalogVersion
46
- ? `The project Matrix Runtime is ${catalogVersion}, while this launcher is ${MATRIX_VERSION}. The project remains authoritative, but a different-release Runtime requires explicit invocation.`
47
- : `The project Matrix Runtime has no release identity. The project remains authoritative, but an unidentified Runtime requires explicit invocation.`,
48
- platform,
49
- runtime,
50
- catalogVersion,
51
- launcherVersion: MATRIX_VERSION,
52
- recovery_command: runtimeCommand(runtime)
53
- };
54
- }
55
- if (catalogUnavailable
56
- || manifest.catalogDigest !== currentCatalog.digest || expected !== currentCatalog.skills.matrix) {
57
- return {
58
- ok: false,
59
- code: "PROJECT_RUNTIME_RELEASE_MISMATCH",
60
- message: `The project Matrix Runtime claims ${MATRIX_VERSION} but does not match that release catalog. Run matrix update.`,
61
- platform,
62
- runtime,
63
- catalogVersion: manifest.catalogVersion,
64
- expected: currentCatalog?.skills.matrix ?? null,
65
- actual
66
- };
67
- }
68
- return { ok: true, code: "PROJECT_RUNTIME", platform, runtime, catalogVersion: manifest.catalogVersion };
69
- }
70
- candidates.push({ platform, matrixRoot, runtime, reason: "modified" });
71
- } catch { candidates.push({ platform, matrixRoot, runtime, reason: "unreadable" }); }
72
- }
73
- return candidates.length
74
- ? { ok: false, code: "PROJECT_RUNTIME_UNTRUSTED", message: "The project Matrix Runtime does not match its installation manifest.", findings: candidates }
75
- : { ok: true, code: "PROJECT_RUNTIME_ABSENT", runtime: null };
76
- }
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { MATRIX_VERSION, PLATFORMS, PLATFORM_IDS, hashDirectory, releaseCatalog } from "./catalog.js";
4
+
5
+ function readJson(file) {
6
+ try { return JSON.parse(fs.readFileSync(file, "utf8")); } catch { return null; }
7
+ }
8
+
9
+ function installationScope(projectRoot) {
10
+ try {
11
+ const line = fs.readFileSync(path.join(projectRoot, ".matrix", "config.yaml"), "utf8").split(/\r?\n/).find((value) => value.startsWith("installation_scope:"));
12
+ return line?.split(":").slice(1).join(":").trim() === "global" ? "global" : "project";
13
+ } catch { return "project"; }
14
+ }
15
+
16
+ function runtimeCommand(runtime) {
17
+ const quoted = process.platform === "win32" ? `"${runtime}"` : `'${runtime.replaceAll("'", "'\\''")}'`;
18
+ return `node ${quoted}`;
19
+ }
20
+
21
+ export function resolveProjectRuntime({ projectRoot = process.cwd() } = {}) {
22
+ const root = path.resolve(projectRoot);
23
+ const manifest = readJson(path.join(root, ".matrix", "installation.json"));
24
+ if (!manifest) return installationScope(root) === "global"
25
+ ? { ok: true, code: "GLOBAL_INSTALLATION", runtime: null }
26
+ : { ok: true, code: "PROJECT_INSTALLATION_ABSENT", runtime: null };
27
+ const candidates = [];
28
+ let currentCatalog = null;
29
+ let catalogUnavailable = false;
30
+ try { currentCatalog = releaseCatalog(manifest.language ?? "en"); } catch { catalogUnavailable = true; }
31
+ for (const platform of PLATFORM_IDS) {
32
+ const expected = manifest.platforms?.[platform]?.skills?.matrix?.hash;
33
+ if (!expected) continue;
34
+ const matrixRoot = path.join(PLATFORMS[platform].skillRoot(root), "matrix");
35
+ const runtime = path.join(matrixRoot, "scripts", "matrix-runtime.mjs");
36
+ if (!fs.existsSync(runtime)) { candidates.push({ platform, matrixRoot, runtime, reason: "missing" }); continue; }
37
+ try {
38
+ const actual = hashDirectory(matrixRoot);
39
+ if (actual === expected) {
40
+ if (manifest.catalogVersion !== MATRIX_VERSION) {
41
+ const catalogVersion = manifest.catalogVersion ?? null;
42
+ return {
43
+ ok: false,
44
+ code: catalogVersion ? "PROJECT_RUNTIME_VERSION_MISMATCH" : "PROJECT_RUNTIME_VERSION_UNKNOWN",
45
+ message: catalogVersion
46
+ ? `The project Matrix Runtime is ${catalogVersion}, while this launcher is ${MATRIX_VERSION}. The project remains authoritative, but a different-release Runtime requires explicit invocation.`
47
+ : `The project Matrix Runtime has no release identity. The project remains authoritative, but an unidentified Runtime requires explicit invocation.`,
48
+ platform,
49
+ runtime,
50
+ catalogVersion,
51
+ launcherVersion: MATRIX_VERSION,
52
+ recovery_command: runtimeCommand(runtime)
53
+ };
54
+ }
55
+ if (catalogUnavailable
56
+ || manifest.catalogDigest !== currentCatalog.digest || expected !== currentCatalog.skills.matrix) {
57
+ return {
58
+ ok: false,
59
+ code: "PROJECT_RUNTIME_RELEASE_MISMATCH",
60
+ message: `The project Matrix Runtime claims ${MATRIX_VERSION} but does not match that release catalog. Run matrix update.`,
61
+ platform,
62
+ runtime,
63
+ catalogVersion: manifest.catalogVersion,
64
+ expected: currentCatalog?.skills.matrix ?? null,
65
+ actual
66
+ };
67
+ }
68
+ return { ok: true, code: "PROJECT_RUNTIME", platform, runtime, catalogVersion: manifest.catalogVersion };
69
+ }
70
+ candidates.push({ platform, matrixRoot, runtime, reason: "modified" });
71
+ } catch { candidates.push({ platform, matrixRoot, runtime, reason: "unreadable" }); }
72
+ }
73
+ return candidates.length
74
+ ? { ok: false, code: "PROJECT_RUNTIME_UNTRUSTED", message: "The project Matrix Runtime does not match its installation manifest.", findings: candidates }
75
+ : { ok: true, code: "PROJECT_RUNTIME_ABSENT", runtime: null };
76
+ }
package/src/workflow.js CHANGED
@@ -49,6 +49,13 @@ const LEGACY_ARCH_SKILLS = [
49
49
  "grilling", "domain-modeling", "research", "wayfinder", "prototype",
50
50
  "codebase-design", "tdd", "diagnosing-bugs", "resolving-merge-conflicts", "code-review"
51
51
  ];
52
+ // The bundled runtime is injected without catalog.js, so this platform→skill-root
53
+ // mapping mirrors catalog.js PLATFORMS and is pinned by a parity test.
54
+ export const PLATFORM_SKILL_ROOTS = Object.freeze({
55
+ "claude-code": ".claude/skills",
56
+ codex: ".agents/skills",
57
+ zcode: ".zcode/skills"
58
+ });
52
59
  const ARTIFACT_POLICY = {
53
60
  proposal: ["## Goal", "## Scope", "## Non-goals", "## Acceptance", "## Risks"],
54
61
  design: ["## Decisions", "## Boundaries", "## Test seams", "## Risks"],
@@ -249,9 +256,11 @@ function archIntegrity(cwd, flow = null, contentHashes = MATT_CONTENT_HASHES) {
249
256
  }
250
257
  const findings = [];
251
258
  for (const [platform, record] of platforms) {
259
+ const relativeRoot = PLATFORM_SKILL_ROOTS[platform];
252
260
  for (const skill of legacy ? LEGACY_ARCH_SKILLS : MATT_AUTOMATIC_SKILLS) {
261
+ if (!relativeRoot) { findings.push({ platform, skill, reason: "unsupported" }); continue; }
253
262
  const installed = legacy ? record?.matt?.skills?.[skill] : mattReceipt?.platforms?.[platform]?.skills?.[skill];
254
- const expectedRoot = path.join(cwd, platform === "codex" ? ".agents" : ".claude", "skills", skill);
263
+ const expectedRoot = path.join(cwd, relativeRoot, skill);
255
264
  const target = legacy ? installed?.root : expectedRoot;
256
265
  if (!legacy && installed?.root && path.resolve(installed.root) !== path.resolve(expectedRoot)) {
257
266
  findings.push({ platform, skill, reason: "non-project" });
@@ -328,8 +337,8 @@ function readFlow(p) {
328
337
  return normalizeFlow(parsed);
329
338
  }
330
339
  function flowContent(flow) {
331
- const order = ["schema", "id", "workflow", "workflow_profile", "orchestration", "artifact_language", "arch_catalog_digest", "matt_release", "status", "phase", "revision", "approved_contract_hash", "contract_approved_revision", "title", "created_at", "updated_at", "acceptance", "scope"];
332
- return `${order.filter((key) => (!["workflow_profile", "artifact_language", "arch_catalog_digest", "matt_release"].includes(key) || flow[key])).map((key) => `${key}: ${flow[key] ?? ""}`).join("\n")}\n`;
340
+ const order = ["schema", "id", "workflow", "workflow_profile", "orchestration", "artifact_language", "arch_catalog_digest", "matt_release", "status", "phase", "revision", "approved_contract_hash", "contract_approved_revision", "implementation_actor", "title", "created_at", "updated_at", "acceptance", "scope"];
341
+ return `${order.filter((key) => (!["workflow_profile", "artifact_language", "arch_catalog_digest", "matt_release", "implementation_actor"].includes(key) || flow[key])).map((key) => `${key}: ${flow[key] ?? ""}`).join("\n")}\n`;
333
342
  }
334
343
  function active(cwd) { const activeFile = path.join(rootFor(cwd), "active.json"); if (!fs.existsSync(activeFile)) return null; return read(activeFile).change_id; }
335
344
  function meaningful(file, headings) {
@@ -385,7 +394,7 @@ function managedReviewPrefixes(cwd) {
385
394
  const { manifest, mattReceipt } = orchestrationConfig(cwd);
386
395
  const prefixes = [];
387
396
  for (const [platform, record] of Object.entries(manifest?.platforms ?? {})) {
388
- const root = platform === "codex" ? ".agents/skills" : platform === "claude-code" ? ".claude/skills" : null;
397
+ const root = PLATFORM_SKILL_ROOTS[platform];
389
398
  if (!root) continue;
390
399
  for (const skill of Object.keys(record?.skills ?? {})) prefixes.push(`${root}/${skill}`);
391
400
  for (const skill of Object.keys(mattReceipt?.platforms?.[platform]?.skills ?? {})) prefixes.push(`${root}/${skill}`);
@@ -696,7 +705,7 @@ export function invoke(argv, { cwd = process.cwd(), failAfterOperation = null, m
696
705
  const operations = [
697
706
  ...(!fs.existsSync(path.join(p.root, "archive")) ? [workflowMkdirOperation(cwd, path.join(p.root, "archive"))] : []),
698
707
  workflowMkdirOperation(cwd, p.artifacts),
699
- ...(!fs.existsSync(path.join(p.root, "config.yaml")) ? [workflowWriteOperation(cwd, path.join(p.root, "config.yaml"), `schema: matrix/config/v1\nauto_transition: true\ndefault_orchestration: ${configured.defaultOrchestration}\ninstallation_scope: project\nlanguage: ${configured.artifactLanguage}\n`)] : []),
708
+ ...(!fs.existsSync(path.join(p.root, "config.yaml")) ? [workflowWriteOperation(cwd, path.join(p.root, "config.yaml"), `schema: matrix/config/v1\ndefault_orchestration: ${configured.defaultOrchestration}\ninstallation_scope: project\nlanguage: ${configured.artifactLanguage}\n`)] : []),
700
709
  ...(!fs.existsSync(path.join(p.root, ".gitignore")) ? [workflowWriteOperation(cwd, path.join(p.root, ".gitignore"), "active.json\ninstallation.json\nmatt-installation.json\nchanges/*/events.jsonl\nchanges/*/artifacts/handoff.md\n")] : []),
701
710
  workflowWriteOperation(cwd, p.flow, flowContent(flow)),
702
711
  ...(workspaceBaseline ? [workflowWriteOperation(cwd, p.workspaceBaseline, `${JSON.stringify(workspaceBaseline, null, 2)}\n`)] : []),
@@ -858,7 +867,13 @@ export function invoke(argv, { cwd = process.cwd(), failAfterOperation = null, m
858
867
  if (command === "transition") {
859
868
  const to = args[0];
860
869
  const confirmed = args.includes("--confirmed");
861
- if (args.slice(1).some((arg) => arg !== "--confirmed")) return fail("INVALID_INTENT", "Transition accepts only a target phase and optional --confirmed.");
870
+ const executorFlag = args.indexOf("--executor");
871
+ const executor = executorFlag >= 0 ? args[executorFlag + 1] : null;
872
+ if (args.slice(1).some((arg, offset) => {
873
+ const index = offset + 1;
874
+ if (index === executorFlag || index === executorFlag + 1) return false;
875
+ return arg !== "--confirmed";
876
+ })) return fail("INVALID_INTENT", "Transition accepts only a target phase, optional --confirmed, and optional --executor <choice>.");
862
877
  if (flow.status !== "active") return fail("CHANGE_NOT_ACTIVE", `Matrix change is ${flow.status}.`);
863
878
  const profile = workflowProfile(p, flow);
864
879
  if (profile.next[flow.phase] !== to) return fail("ILLEGAL_TRANSITION", `Illegal ${profile.id} transition ${flow.phase} -> ${to}.`);
@@ -868,14 +883,24 @@ export function invoke(argv, { cwd = process.cwd(), failAfterOperation = null, m
868
883
  const order = workspaceOrderGuard(cwd, p);
869
884
  if (!order.ok) return order;
870
885
  }
886
+ // The implementation-actor gate is runtime-enforced, not prompt-level: Build
887
+ // may not be entered until the actor choice is recorded explicitly.
888
+ if (to === "build") {
889
+ if (executor != null && !/^(current|model|handoff:[a-z0-9]+(?:-[a-z0-9]+)*)$/.test(executor)) {
890
+ return fail("INVALID_INTENT", "Executor choice must be current, model, or handoff:<agent-id>.");
891
+ }
892
+ if (!executor) {
893
+ return fail("EXECUTOR_CONFIRMATION_REQUIRED", "Present the implementation-actor choice to the user before Build: continue with the current agent/model, switch the model in the user's client, or hand off to another agent. Then rerun with --executor <current|model|handoff:<agent-id>>.");
894
+ }
895
+ }
871
896
  const before = flow.phase;
872
897
  const approval = to === "build" ? contractState(p, flow) : null;
873
898
  if (approval && approval.status !== "approved-and-matching" && approval.status !== "unapproved") return { ...contractFailure(flow, approval), change_id: id };
874
899
  if (to === "build" && approval.snapshot.missing) return { ...fail("GUARD_FAILED", "Workflow contract artifacts are incomplete."), change_id: id };
875
- const next = { ...flow, schema: "matrix/change/v2", phase: to, revision: flow.revision + 1, updated_at: stamp(), ...(to === "build" ? { approved_contract_hash: approval.snapshot.hash, contract_approved_revision: flow.revision + 1 } : {}) };
900
+ const next = { ...flow, schema: "matrix/change/v2", phase: to, revision: flow.revision + 1, updated_at: stamp(), ...(to === "build" ? { approved_contract_hash: approval.snapshot.hash, contract_approved_revision: flow.revision + 1, implementation_actor: executor } : {}) };
876
901
  const nextProfile = workflowProfile(p, next);
877
902
  const details = to === "build"
878
- ? { approved_contract_hash: next.approved_contract_hash, contract_approved_revision: next.contract_approved_revision, ...(shortcut(flow) ? { confirmed, profile: nextProfile.id } : {}) }
903
+ ? { approved_contract_hash: next.approved_contract_hash, contract_approved_revision: next.contract_approved_revision, implementation_actor: next.implementation_actor, ...(shortcut(flow) ? { confirmed, profile: nextProfile.id } : {}) }
879
904
  : shortcut(flow) ? { profile: nextProfile.id } : {};
880
905
  const resultFields = { ok: true, code: "OK", change_id: id, workflow: next.workflow, profile: nextProfile.id, evidence_policy: evidencePolicy(next, nextProfile), orchestration: next.orchestration, artifact_language: next.artifact_language ?? artifactLanguage, status: next.status, phase: to, revision: next.revision, next_skill: skill(to), ...contractFields(next, contractState(p, next)) };
881
906
  return runWorkflowTransaction({