@rosenfan/matrix 0.1.7 → 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
@@ -219,8 +219,8 @@ matrix workflow guard open
219
219
  # Advance (only if guard passes)
220
220
  matrix workflow transition design
221
221
 
222
- # Lightweight: approve the compact proposal and enter Build
223
- 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>>
224
224
 
225
225
  # Return from verification failure; stale evidence is retained in history
226
226
  matrix workflow return build --reason verification-failed
package/README.zh-CN.md CHANGED
@@ -216,8 +216,8 @@ matrix workflow guard open
216
216
  # 推进(仅在守卫通过时)
217
217
  matrix workflow transition design
218
218
 
219
- # lightweight:确认紧凑 proposal 后进入 Build
220
- matrix workflow transition build --confirmed
219
+ # lightweight:确认紧凑 proposal 后进入 Build(执行主体选择并入同一次暂停)
220
+ matrix workflow transition build --confirmed --executor <current|model|handoff:<agent-id>>
221
221
 
222
222
  # 验证失败时返回 Build;旧证据保留在历史目录
223
223
  matrix workflow return build --reason verification-failed
@@ -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.
@@ -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
@@ -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
  ```
@@ -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。
@@ -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.7",
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",
@@ -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
  };
@@ -1,7 +1,7 @@
1
1
  import crypto from "node:crypto";
2
2
 
3
3
  export const MATT_COMPATIBILITY = Object.freeze({
4
- matrixVersion: "0.1.7",
4
+ matrixVersion: "0.1.8",
5
5
  release: "v1.3.1",
6
6
  commit: "24fe0ef7737efae15c87225755e9f6f5965e4888",
7
7
  archive: "https://github.com/mattpocock/skills/archive/24fe0ef7737efae15c87225755e9f6f5965e4888.tar.gz",
package/src/workflow.js CHANGED
@@ -337,8 +337,8 @@ function readFlow(p) {
337
337
  return normalizeFlow(parsed);
338
338
  }
339
339
  function flowContent(flow) {
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", "title", "created_at", "updated_at", "acceptance", "scope"];
341
- 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`;
342
342
  }
343
343
  function active(cwd) { const activeFile = path.join(rootFor(cwd), "active.json"); if (!fs.existsSync(activeFile)) return null; return read(activeFile).change_id; }
344
344
  function meaningful(file, headings) {
@@ -705,7 +705,7 @@ export function invoke(argv, { cwd = process.cwd(), failAfterOperation = null, m
705
705
  const operations = [
706
706
  ...(!fs.existsSync(path.join(p.root, "archive")) ? [workflowMkdirOperation(cwd, path.join(p.root, "archive"))] : []),
707
707
  workflowMkdirOperation(cwd, p.artifacts),
708
- ...(!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`)] : []),
709
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")] : []),
710
710
  workflowWriteOperation(cwd, p.flow, flowContent(flow)),
711
711
  ...(workspaceBaseline ? [workflowWriteOperation(cwd, p.workspaceBaseline, `${JSON.stringify(workspaceBaseline, null, 2)}\n`)] : []),
@@ -867,7 +867,13 @@ export function invoke(argv, { cwd = process.cwd(), failAfterOperation = null, m
867
867
  if (command === "transition") {
868
868
  const to = args[0];
869
869
  const confirmed = args.includes("--confirmed");
870
- 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>.");
871
877
  if (flow.status !== "active") return fail("CHANGE_NOT_ACTIVE", `Matrix change is ${flow.status}.`);
872
878
  const profile = workflowProfile(p, flow);
873
879
  if (profile.next[flow.phase] !== to) return fail("ILLEGAL_TRANSITION", `Illegal ${profile.id} transition ${flow.phase} -> ${to}.`);
@@ -877,14 +883,24 @@ export function invoke(argv, { cwd = process.cwd(), failAfterOperation = null, m
877
883
  const order = workspaceOrderGuard(cwd, p);
878
884
  if (!order.ok) return order;
879
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
+ }
880
896
  const before = flow.phase;
881
897
  const approval = to === "build" ? contractState(p, flow) : null;
882
898
  if (approval && approval.status !== "approved-and-matching" && approval.status !== "unapproved") return { ...contractFailure(flow, approval), change_id: id };
883
899
  if (to === "build" && approval.snapshot.missing) return { ...fail("GUARD_FAILED", "Workflow contract artifacts are incomplete."), change_id: id };
884
- 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 } : {}) };
885
901
  const nextProfile = workflowProfile(p, next);
886
902
  const details = to === "build"
887
- ? { 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 } : {}) }
888
904
  : shortcut(flow) ? { profile: nextProfile.id } : {};
889
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)) };
890
906
  return runWorkflowTransaction({