@rosenfan/matrix 0.1.0 → 0.1.2

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.
Files changed (37) hide show
  1. package/.claude/skills/matrix/SKILL.md +62 -21
  2. package/.claude/skills/matrix/agents/openai.yaml +4 -4
  3. package/.claude/skills/matrix/scripts/matrix_claude.py +11 -166
  4. package/.claude/skills/matrix/scripts/matrix_state.py +60 -114
  5. package/.claude/skills/matrix-archive/SKILL.md +33 -26
  6. package/.claude/skills/matrix-build/SKILL.md +26 -39
  7. package/.claude/skills/matrix-claude/SKILL.md +22 -22
  8. package/.claude/skills/matrix-design/SKILL.md +28 -29
  9. package/.claude/skills/matrix-hotfix/SKILL.md +8 -8
  10. package/.claude/skills/matrix-open/SKILL.md +17 -20
  11. package/.claude/skills/matrix-status/SKILL.md +4 -3
  12. package/.claude/skills/matrix-tweak/SKILL.md +8 -8
  13. package/.claude/skills/matrix-verify/SKILL.md +18 -29
  14. package/LICENSE +21 -21
  15. package/README.md +255 -242
  16. package/README.zh-CN.md +254 -242
  17. package/assets/skills-zh-CN/matrix/SKILL.md +57 -5
  18. package/assets/skills-zh-CN/matrix-archive/SKILL.md +23 -2
  19. package/assets/skills-zh-CN/matrix-build/SKILL.md +26 -2
  20. package/assets/skills-zh-CN/matrix-claude/SKILL.md +12 -11
  21. package/assets/skills-zh-CN/matrix-design/SKILL.md +16 -11
  22. package/assets/skills-zh-CN/matrix-hotfix/SKILL.md +8 -8
  23. package/assets/skills-zh-CN/matrix-open/SKILL.md +16 -11
  24. package/assets/skills-zh-CN/matrix-status/SKILL.md +12 -11
  25. package/assets/skills-zh-CN/matrix-tweak/SKILL.md +8 -8
  26. package/assets/skills-zh-CN/matrix-verify/SKILL.md +22 -8
  27. package/bin/matrix.js +15 -15
  28. package/package.json +56 -56
  29. package/src/catalog.js +85 -80
  30. package/src/cli.js +116 -111
  31. package/src/distribution.js +216 -152
  32. package/src/installer.js +45 -45
  33. package/src/matt.js +45 -34
  34. package/src/messages.js +33 -33
  35. package/src/ui.js +89 -89
  36. package/src/workflow-transaction.js +622 -0
  37. package/src/workflow.js +547 -74
@@ -5,36 +5,73 @@ description: Route, initialize, resume, and govern a persistent Matrix developme
5
5
 
6
6
  # Matrix
7
7
 
8
- Use Matrix as the development-workflow entrypoint. Its source of truth is the current repository's `.matrix/`; never infer a phase from chat history.
8
+ Use Matrix as the development-workflow entrypoint. Its source of truth is the current repository's `.matrix/`; never infer a phase from chat history.
9
9
 
10
10
  ## Route
11
11
 
12
- First run:
12
+ First run:
13
13
 
14
14
  ```powershell
15
- matrix workflow inspect
16
- ```
17
-
18
- If and only if the `matrix` executable is reported as absent, run the bundled fallback from this Skill directory instead:
19
-
20
- ```powershell
21
- node <matrix-skill-directory>/scripts/matrix-runtime.mjs inspect
22
- ```
15
+ matrix workflow inspect
16
+ ```
23
17
 
24
- - If an active change exists, show its phase and enter the returned `next_skill`.
25
- - If no active change exists, classify the request: reproducible small bug → `$matrix-hotfix` with workflow `hotfix`; bounded small change with no public API/schema/architecture impact → `$matrix-tweak` with workflow `tweak`; otherwise full Matrix with workflow `full`.
26
- - Do not create Matrix state for questions, read-only investigation, or requests outside software development.
27
- - Before `init`, derive a short kebab-case change id and an accurate title. Start with `full` unless a shortcut clearly applies.
18
+ If and only if the `matrix` executable is reported as absent, run the bundled fallback from this Skill directory instead:
28
19
 
29
20
  ```powershell
30
- matrix workflow init <change-id> --workflow <full|hotfix|tweak> --title "<title>"
21
+ node <matrix-skill-directory>/scripts/matrix-runtime.mjs inspect
31
22
  ```
32
23
 
33
- Then enter `$matrix-open`. State initialization creates tracked design artifacts and ignores machine-only state without changing the repository's existing `.gitignore`.
24
+ - If an active change exists, show its phase and frozen `orchestration`, report any Arch installation-integrity failure, and enter the returned `next_skill` only when the Runtime permits continuation.
25
+ - If no active change exists, classify the request: reproducible small bug → `$matrix-hotfix` with workflow `hotfix`; bounded small change with no public API/schema/architecture impact → `$matrix-tweak` with workflow `tweak`; otherwise full Matrix with workflow `full`.
26
+ - Do not create Matrix state for questions, read-only investigation, or requests outside software development.
27
+ - Before `init`, derive a short kebab-case change id and an accurate title. Start with `full` unless a shortcut clearly applies.
34
28
 
35
- ## Optional Claude handoff
29
+ ```powershell
30
+ matrix workflow init <change-id> --workflow <full|hotfix|tweak> --orchestration <prim|arch> --title "<title>"
31
+ ```
32
+
33
+ An explicit mode wins; otherwise Runtime uses the project `default_orchestration`. The resolved value is immutable for the change and is shown in init, inspect, and the initialized Event. Then enter `$matrix-open`.
34
+
35
+ ## Orchestration
36
+
37
+ - **Prim** uses Matrix's own capability in every phase and never requires Matt Skills.
38
+ - **Arch** conditionally invokes the initialization-verified ten-Skill cohort when documented task-fact triggers are present.
39
+
40
+ Both modes share the same Matrix phase graph, artifacts, guards, Return/Abort, Contract, transactions, and Archive protocol. Companion Skills never write Matrix state, transition, commit, archive, or create competing workflow artifacts. Announce each Arch capability and trigger before invoking it; do not ask for redundant per-Skill permission.
41
+
42
+ A correctly installed Arch capability that fails gets at most one Matrix-owned fallback for the same bounded capability, with failure and evidence recorded. A missing, modified, or incomplete Arch installation must be repaired with `matrix init --with-mattpocock`; never switch the active change to Prim.
43
+
44
+ ## Recovery
45
+
46
+ Use Runtime commands instead of editing Matrix state:
47
+
48
+ ```powershell
49
+ matrix workflow return design --reason design-gap
50
+ matrix workflow return build --reason verification-failed
51
+ matrix workflow return design --reason acceptance-or-design-gap
52
+ matrix workflow return design --reason acceptance-or-design-gap # from Archive, before terminal archive
53
+ matrix workflow abort --reason <requirement-cancelled|superseded|no-longer-valuable|blocked|other>
54
+ ```
55
+
56
+ Return preserves the change but moves the current `verification.md` into revisioned evidence history, so the returned phase must produce fresh current evidence. Abort preserves the last phase and moves the terminal change into `.matrix/archive/`; it never undoes worktree changes.
57
+
58
+ If any mutation reports `WORKFLOW_RECOVERY_REQUIRED`, stop normal phase work and run the read-only diagnosis:
59
+
60
+ ```powershell
61
+ matrix workflow doctor
62
+ ```
63
+
64
+ Never infer a repair target or strategy. Use only the exact command returned by doctor:
65
+
66
+ ```powershell
67
+ matrix workflow doctor --repair --transaction <id> --strategy <continue|rollback>
68
+ matrix workflow doctor --repair --lock <id>
69
+ ```
70
+
71
+ ## Optional Claude handoff
36
72
 
37
- `$matrix-claude` is an optional sidecar command that exports a frozen Matrix design to a Claude Code task package. It never changes the Matrix phase, workflow, guard result, or runtime state.
73
+ `$matrix-claude` is an optional sidecar command used after the approved Design -> Build transition. It exports the matching approved contract to a Claude Code task package. It never changes the Matrix phase, workflow, guard result, or runtime state.
74
+ Use it only when the user explicitly chooses Codex design with Claude Code implementation; never inject it into the standard path.
38
75
 
39
76
  ## Cross-Cutting Skills
40
77
 
@@ -44,6 +81,10 @@ When context is running low and work needs to continue in a fresh session, use `
44
81
  ## Rules
45
82
 
46
83
  - A phase can advance only through `matrix workflow transition`; run its guard first.
47
- - Do not replace required evidence with an assertion that work is complete.
48
- - Pause for user confirmation at material architecture/scope decisions and before archive/commit.
49
- - On scope expansion, return to `$matrix-design`; never silently continue the old plan.
84
+ - A phase can move backward only through one of the documented `matrix workflow return` intents.
85
+ - The Build transition approves the exact bytes of proposal, design, and plan. Any later byte change blocks Build, Verify, Archive, and Claude export until a controlled Return to Design and a new Build approval.
86
+ - A successful Archive is always a two-step optimistic commit: run `matrix workflow archive --dry-run`, then pass its exact hash to `matrix workflow archive --expect-preflight <sha256>`. Never bypass the preflight.
87
+ - `matrix workflow 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`.
88
+ - Do not replace required evidence with an assertion that work is complete.
89
+ - Pause for user confirmation at material architecture/scope decisions and before archive/commit.
90
+ - On scope expansion, run `matrix workflow return design --reason design-gap` and enter `$matrix-design`; never silently continue the old plan.
@@ -1,4 +1,4 @@
1
- interface:
2
- display_name: "Matrix"
3
- short_description: "Route, resume, and govern development changes"
4
- default_prompt: "Use $matrix to start or resume a development change."
1
+ interface:
2
+ display_name: "Matrix"
3
+ short_description: "Route, resume, and govern development changes"
4
+ default_prompt: "Use $matrix to start or resume a development change."
@@ -1,166 +1,11 @@
1
- #!/usr/bin/env python3
2
- """Export a frozen Matrix design to a Claude Code task package without changing Matrix state."""
3
- from __future__ import annotations
4
-
5
- import argparse
6
- import re
7
- import sys
8
- from pathlib import Path
9
-
10
- from matrix_state import active_path, artifact, get_active, guard, parse_flow, root
11
-
12
-
13
- def fail(message: str) -> None:
14
- print(f"MATRIX CLAUDE ERROR: {message}", file=sys.stderr)
15
- raise SystemExit(2)
16
-
17
-
18
- def read_artifact(change: str, name: str) -> str:
19
- path = artifact(change, name)
20
- if not path.exists():
21
- fail(f"Required Matrix artifact is missing: {path}")
22
- return path.read_text(encoding="utf-8").strip()
23
-
24
-
25
- def task_content(change: str, task_id: str, proposal: str, design: str, plan: str, result_path: str) -> str:
26
- return f"""# Claude Code 执行任务包:{task_id}
27
-
28
- ## 元数据
29
-
30
- | 字段 | 内容 |
31
- | --- | --- |
32
- | 状态 | CLAUDE_QUEUED |
33
- | Codex 决策人 | Codex |
34
- | Claude Code 执行人 | Claude Code |
35
- | Matrix Change | `{change}` |
36
- | 回执 | `{result_path}` |
37
-
38
- ## 目标
39
-
40
- 以下目标来自已冻结的 Matrix proposal,不得扩展:
41
-
42
- {proposal}
43
-
44
- ## 范围
45
-
46
- ### 允许修改
47
-
48
- - 仅修改为完成下列 Matrix 设计与计划所必需的文件。
49
- - 先阅读仓库级指令与现有测试,再开始实施。
50
-
51
- ### 禁止修改
52
-
53
- - 不得修改 Matrix 状态文件或推进 Matrix phase。
54
- - 不得自行改变架构、公开接口、数据模型、依赖或任务范围。
55
- - 不得执行未获授权的网络、部署、破坏性或外部副作用动作。
56
-
57
- ## 禁止事项
58
-
59
- - 不得把设计外的发现直接实现为额外功能。
60
- - 遇到范围扩大、设计冲突或验收不清时必须停止并写 `NEEDS_DECISION`。
61
-
62
- ## 已冻结的设计决定
63
-
64
- {design}
65
-
66
- ## 执行步骤
67
-
68
- {plan}
69
-
70
- ## 验收
71
-
72
- - 执行 `plan.md` 中的全部验证命令。
73
- - 为每项行为变化提供测试或可复现验证证据。
74
- - 完成后保留可供 Codex 固定基线审查的 diff。
75
-
76
- ## 停止并上报的条件
77
-
78
- - 需要修改范围外文件或改变冻结设计。
79
- - 验收失败且不能在既定范围内修复。
80
- - 发现安全、授权或项目指令冲突。
81
-
82
- ## 回执路径
83
-
84
- 在 `{result_path}` 创建结构化回执,至少包含:
85
-
86
- - `## 元数据`:执行状态与任务 ID。
87
- - `## 改动清单`:实际改动文件和理由。
88
- - `## 测试结果`:命令与输出摘要。
89
- - `## 边界确认`:未越界声明,或 `NEEDS_DECISION`。
90
- - `## 未解决项`:如有。
91
-
92
- Codex 才能验收;Claude Code 不得自行宣告任务已接受或完成。
93
- """
94
-
95
-
96
- def active_board(task_id: str, task_path: str, result_path: str, change: str) -> str:
97
- return f"""# 活跃任务看板
98
-
99
- > 这里是唯一实时任务面板:只放当前任务的状态、执行主体、范围、完成条件和下一步。
100
-
101
- ## 当前状态
102
-
103
- | 字段 | 内容 |
104
- | --- | --- |
105
- | ID | {task_id} |
106
- | 状态 | CLAUDE_QUEUED |
107
- | 执行主体 | Claude Code(由用户通过 `$matrix-claude` 明确选择) |
108
- | 目标 | 执行 Matrix Change `{change}` 的冻结任务包。 |
109
- | 冻结设计 | `.matrix/changes/{change}/artifacts/{{proposal,design,plan}}.md` |
110
- | 任务包 | `{task_path}` |
111
- | 回执 | `{result_path}` |
112
- | 完成条件 | 任务包验收命令通过、Claude 回执齐全,并经 Codex 审查实际 diff 和证据。 |
113
- | 下一步 | Claude Code 按冻结任务包实施;范围冲突时写 `NEEDS_DECISION`。 |
114
- """
115
-
116
-
117
- def export(args: argparse.Namespace) -> None:
118
- change = get_active()
119
- flow = parse_flow(root() / "changes" / change / "matrix.yaml")
120
- if flow.get("phase") != "design":
121
- fail(f"Matrix change '{change}' is in phase '{flow.get('phase')}', not design.")
122
- if not guard(change, "design", output=True):
123
- fail("Design guard failed; no Claude task package was written.")
124
- if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9-]*", args.task_id):
125
- fail("Task id must use letters, digits, and hyphens.")
126
- proposal, design, plan = (read_artifact(change, n) for n in ("proposal.md", "design.md", "plan.md"))
127
-
128
- if args.target == "generic":
129
- output = artifact(change, "claude-task.md")
130
- result = artifact(change, "claude-result.md")
131
- output.write_text(task_content(change, args.task_id, proposal, design, plan, str(result.relative_to(Path.cwd())).replace("\\", "/")), encoding="utf-8")
132
- print(output)
133
- return
134
-
135
- tasks = Path.cwd() / "docs" / "tasks"
136
- if not tasks.is_dir() or not (tasks / "active.md").is_file():
137
- fail("FnSec target requires docs/tasks/active.md in the current project.")
138
- output = tasks / f"claude-task-{args.task_id}.md"
139
- result = tasks / f"claude-result-{args.task_id}.md"
140
- if output.exists() or result.exists():
141
- fail(f"FnSec task or result path already exists for {args.task_id}.")
142
- output.write_text(task_content(change, args.task_id, proposal, design, plan, str(result.relative_to(Path.cwd())).replace("\\", "/")), encoding="utf-8")
143
- if args.apply_fnsec_board:
144
- board = tasks / "active.md"
145
- current = board.read_text(encoding="utf-8")
146
- if "NO_ACTIVE_OBJECTIVE" not in current:
147
- output.unlink()
148
- fail("FnSec active.md already has an objective; refused to overwrite it.")
149
- board.write_text(active_board(args.task_id, str(output.relative_to(Path.cwd())).replace("\\", "/"), str(result.relative_to(Path.cwd())).replace("\\", "/"), change), encoding="utf-8")
150
- print(output)
151
-
152
-
153
- def main() -> None:
154
- parser = argparse.ArgumentParser()
155
- subs = parser.add_subparsers(dest="command", required=True)
156
- command = subs.add_parser("export")
157
- command.add_argument("--task-id", required=True)
158
- command.add_argument("--target", choices=("generic", "fnsec"), default="generic")
159
- command.add_argument("--apply-fnsec-board", action="store_true")
160
- command.set_defaults(func=export)
161
- args = parser.parse_args()
162
- args.func(args)
163
-
164
-
165
- if __name__ == "__main__":
166
- main()
1
+ #!/usr/bin/env python3
2
+ """Compatibility adapter for the authoritative Node workflow exporter."""
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+ from matrix_state import run_runtime
8
+
9
+
10
+ if __name__ == "__main__":
11
+ raise SystemExit(run_runtime(sys.argv[1:]))
@@ -1,114 +1,60 @@
1
- #!/usr/bin/env python3
2
- """Deterministic state and guard kernel shared by every Matrix Skill."""
3
- from __future__ import annotations
4
-
5
- import argparse
6
- import json
7
- import re
8
- import shutil
9
- import sys
10
- from datetime import datetime, timezone
11
- from pathlib import Path
12
-
13
- PHASES = ("open", "design", "build", "verify", "archive")
14
- NEXT = {"open": "design", "design": "build", "build": "verify", "verify": "archive"}
15
- SKILLS = {phase: f"$matrix-{phase}" for phase in PHASES}
16
-
17
- def now(): return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
18
- def root(): return Path.cwd() / ".matrix"
19
- def active_path(): return root() / "active.json"
20
- def fail(message): print(f"MATRIX ERROR: {message}", file=sys.stderr); raise SystemExit(2)
21
- def write_json(path, value): path.write_text(json.dumps(value, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
22
- def load_json(path): return json.loads(path.read_text(encoding="utf-8"))
23
- def flow_path(change): return root() / "changes" / change / "matrix.yaml"
24
- def parse_flow(path):
25
- values = {}
26
- for line in path.read_text(encoding="utf-8").splitlines():
27
- if ":" in line and not line.startswith(" "):
28
- key, value = line.split(":", 1); values[key.strip()] = value.strip().strip('"')
29
- return values
30
- def write_flow(path, values):
31
- order = ("schema", "id", "workflow", "status", "phase", "title", "created_at", "updated_at", "acceptance", "scope")
32
- path.write_text("\n".join(f"{key}: {values.get(key, '')}" for key in order) + "\n", encoding="utf-8")
33
- def get_active():
34
- if not active_path().exists(): fail("No active Matrix change. Start with $matrix <request>.")
35
- change = load_json(active_path()).get("change_id")
36
- if not isinstance(change, str) or not flow_path(change).exists(): fail("active.json does not reference a valid Matrix change.")
37
- return change
38
- def artifact(change, name): return root() / "changes" / change / "artifacts" / name
39
- def meaningful(path, headings=()):
40
- if not path.exists(): return False
41
- text = path.read_text(encoding="utf-8").strip()
42
- if len(text) < 40: return False
43
- return all(h in text for h in headings)
44
- def checks(change, phase):
45
- base = root() / "changes" / change / "artifacts"
46
- if phase == "open":
47
- return [("proposal.md has goal, scope, and acceptance", meaningful(base / "proposal.md", ("## Goal", "## Scope", "## Acceptance")))]
48
- if phase == "design":
49
- return [("design.md has decisions and test seams", meaningful(base / "design.md", ("## Decisions", "## Test seams"))), ("plan.md has ordered implementation steps", meaningful(base / "plan.md", ("## Steps", "## Validation")))]
50
- if phase == "build":
51
- return [("plan.md exists", meaningful(base / "plan.md", ("## Steps",))), ("verification.md has build evidence", meaningful(base / "verification.md", ("## Build evidence",)))]
52
- if phase == "verify":
53
- return [("verification.md has test evidence", meaningful(base / "verification.md", ("## Test evidence",))), ("verification.md has review evidence", meaningful(base / "verification.md", ("## Review evidence",)))]
54
- return [("verify guard passed before archive", meaningful(base / "verification.md", ("## Test evidence", "## Review evidence")))]
55
- def guard(change, phase, output=True):
56
- results = checks(change, phase)
57
- ok = all(result for _, result in results)
58
- if output:
59
- print(f"Matrix guard: {change} / {phase}")
60
- for label, result in results: print(f"[{'PASS' if result else 'FAIL'}] {label}")
61
- return ok
62
- def event(change, event_name, before, after):
63
- p = root() / "changes" / change / "events.jsonl"
64
- with p.open("a", encoding="utf-8") as handle: handle.write(json.dumps({"at": now(), "event": event_name, "from": before, "to": after}, ensure_ascii=False) + "\n")
65
- def init(args):
66
- change = re.sub(r"[^a-z0-9]+", "-", args.change.lower()).strip("-")
67
- if not change: fail("Change id must contain letters or digits.")
68
- if active_path().exists(): fail(f"Active Matrix change already exists: {get_active()}.")
69
- directory = root() / "changes" / change
70
- if directory.exists() or (root() / "archive" / change).exists(): fail(f"Matrix change already exists: {change}.")
71
- (directory / "artifacts").mkdir(parents=True)
72
- root().mkdir(exist_ok=True); (root() / "archive").mkdir(exist_ok=True)
73
- (root() / "config.yaml").write_text("schema: matrix/config/v1\nauto_transition: true\n", encoding="utf-8") if not (root() / "config.yaml").exists() else None
74
- gitignore = root() / ".gitignore"
75
- ignored = "active.json\ninstallation.json\nchanges/*/run-state.json\nchanges/*/events.jsonl\nchanges/*/artifacts/handoff.md\n"
76
- gitignore.write_text(ignored, encoding="utf-8") if not gitignore.exists() else None
77
- values = {"schema":"matrix/change/v1", "id":change, "workflow":args.workflow, "status":"active", "phase":"open", "title":args.title, "created_at":now(), "updated_at":now(), "acceptance":"pending", "scope":"pending"}
78
- write_flow(directory / "matrix.yaml", values)
79
- write_json(directory / "run-state.json", {"schema":"matrix/run/v1", "current_step":"open", "iteration":0, "pending_gate":None, "updated_at":now()})
80
- write_json(active_path(), {"change_id":change})
81
- event(change, "initialized", None, "open")
82
- print(f"Initialized Matrix change '{change}'. Next: {SKILLS['open']}")
83
- def inspect(_):
84
- change = get_active(); flow = parse_flow(flow_path(change)); phase = flow.get("phase", "invalid")
85
- if phase not in PHASES: fail(f"Invalid phase '{phase}'.")
86
- print(json.dumps({"change_id":change, "workflow":flow.get("workflow"), "phase":phase, "next_skill":SKILLS[phase], "guard_pass":guard(change, phase, False)}, ensure_ascii=False, indent=2))
87
- def transition(args):
88
- change = get_active(); flow = parse_flow(flow_path(change)); before = flow.get("phase")
89
- expected = NEXT.get(before)
90
- if args.to != expected: fail(f"Illegal transition {before} -> {args.to}; expected {expected or 'archive command' }.")
91
- if not guard(change, before): fail("Current phase guard failed; state was not changed.")
92
- flow["phase"] = args.to; flow["updated_at"] = now(); write_flow(flow_path(change), flow); event(change, "transition", before, args.to)
93
- print(f"Transitioned {change}: {before} -> {args.to}. Next: {SKILLS[args.to]}")
94
- def archive(_):
95
- change = get_active(); flow = parse_flow(flow_path(change))
96
- if flow.get("phase") != "archive": fail("Only an archive-phase change can be archived.")
97
- if not guard(change, "archive"): fail("Archive guard failed; state was not changed.")
98
- flow["status"] = "archived"; flow["updated_at"] = now(); write_flow(flow_path(change), flow); event(change, "archived", "archive", "archived")
99
- source = root() / "changes" / change; target = root() / "archive" / change; shutil.move(str(source), str(target)); active_path().unlink()
100
- print(f"Archived Matrix change '{change}'.")
101
- def handoff(_):
102
- change = get_active(); flow = parse_flow(flow_path(change)); phase = flow["phase"]
103
- p = artifact(change, "handoff.md")
104
- p.write_text(f"# Matrix handoff: {change}\n\n- Workflow: {flow['workflow']}\n- Current phase: {phase}\n- Resume with: `{SKILLS[phase]}`\n- Guard passes: {guard(change, phase, False)}\n", encoding="utf-8")
105
- print(p)
106
- def main():
107
- parser = argparse.ArgumentParser(); subs = parser.add_subparsers(dest="command", required=True)
108
- p = subs.add_parser("init"); p.add_argument("change"); p.add_argument("--workflow", choices=("full","hotfix","tweak"), default="full"); p.add_argument("--title", required=True); p.set_defaults(func=init)
109
- subs.add_parser("inspect").set_defaults(func=inspect)
110
- p = subs.add_parser("guard"); p.add_argument("phase", choices=PHASES); p.set_defaults(func=lambda a: sys.exit(0 if guard(get_active(), a.phase) else 1))
111
- p = subs.add_parser("transition"); p.add_argument("to", choices=PHASES[1:]); p.set_defaults(func=transition)
112
- subs.add_parser("archive").set_defaults(func=archive); subs.add_parser("handoff").set_defaults(func=handoff)
113
- args = parser.parse_args(); args.func(args)
114
- if __name__ == "__main__": main()
1
+ #!/usr/bin/env python3
2
+ """Compatibility adapter for the authoritative Node Workflow Runtime."""
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import shutil
7
+ import subprocess
8
+ import sys
9
+ from pathlib import Path
10
+
11
+
12
+ def development_runtime() -> Path | None:
13
+ """Return an explicitly selected source runtime for repository tests only."""
14
+ configured = os.environ.get("MATRIX_DEVELOPMENT_RUNTIME")
15
+ return Path(configured).resolve() if configured else None
16
+
17
+
18
+ def primary_runtime() -> str | None:
19
+ if os.name == "nt":
20
+ return shutil.which("matrix.cmd") or shutil.which("matrix.exe")
21
+ return shutil.which("matrix")
22
+
23
+
24
+ def runtime_command(args: list[str]) -> list[str]:
25
+ primary = primary_runtime()
26
+ if primary is not None:
27
+ return [primary, "workflow", *args, "--json"]
28
+
29
+ development = development_runtime()
30
+ if development is not None:
31
+ if not development.is_file():
32
+ raise RuntimeError(f"Configured Matrix development runtime was not found: {development}")
33
+ node = shutil.which("node")
34
+ if node is None:
35
+ raise RuntimeError("Node.js is required to run the Matrix workflow.")
36
+ return [node, str(development), *args]
37
+
38
+ bundled = Path(__file__).with_name("matrix-runtime.mjs")
39
+ node = shutil.which("node")
40
+ if node is not None and bundled.exists():
41
+ return [node, str(bundled), *args]
42
+
43
+ raise RuntimeError("Matrix CLI is absent and the bundled workflow runtime was not found.")
44
+
45
+
46
+ def run_runtime(args: list[str]) -> int:
47
+ try:
48
+ completed = subprocess.run(runtime_command(args), cwd=Path.cwd(), check=False)
49
+ except (OSError, RuntimeError) as error:
50
+ print(f"MATRIX ERROR [RUNTIME_UNAVAILABLE]: {error}", file=sys.stderr)
51
+ return 2
52
+ return completed.returncode
53
+
54
+
55
+ def main() -> int:
56
+ return run_runtime(sys.argv[1:])
57
+
58
+
59
+ if __name__ == "__main__":
60
+ raise SystemExit(main())
@@ -1,26 +1,33 @@
1
- ---
2
- name: matrix-archive
3
- description: Close and archive a verified Matrix change. Use when the active Matrix phase is archive.
4
- ---
5
-
6
- # Matrix Archive
7
-
8
- This is the **final phase** of the Matrix workflow. After archiving, the workflow is complete.
9
-
10
- ## Process
11
-
12
- 1. Read the proposal, plan, and verification evidence
13
- 2. Summarize changed behavior, verification, residual risks, and follow-up work in `artifacts/verification.md` or the project's normal delivery document
14
- 3. Ask for explicit confirmation before committing or archiving
15
- 4. Run the archive command (this also runs the verify guard)
16
- 5. **Workflow complete** — no further phase transitions needed
17
-
18
- ```powershell
19
- matrix workflow archive
20
- ```
21
-
22
- Do not archive if the verify guard fails.
23
-
24
- ## After Archive
25
-
26
- The change is moved to `.matrix/archive/`. To start a new change, run `$matrix` again.
1
+ ---
2
+ name: matrix-archive
3
+ description: Close and archive a verified Matrix change. Use when the active Matrix phase is archive.
4
+ ---
5
+
6
+ # Matrix Archive
7
+
8
+ This is the final Matrix phase. Read the proposal, plan, and verification evidence; summarize changed behavior, verification, residual risks, and follow-up work. Ask for explicit confirmation before committing or archiving.
9
+
10
+ Archive is Matrix-owned in both Prim and Arch. Do not invoke companion implementation, review, or architecture capabilities here.
11
+
12
+ ## Mandatory two-step Archive
13
+
14
+ Run the read-only preflight and review its bounded effect summary. Then submit exactly the hash returned by that preflight:
15
+
16
+ ```powershell
17
+ matrix workflow archive --dry-run
18
+ matrix workflow archive --expect-preflight <sha256-returned-by-dry-run>
19
+ ```
20
+
21
+ Bare Archive is rejected. Runtime reacquires the project Archive boundary and recomputes the entire protected change-directory manifest before committing.
22
+
23
+ - `ARCHIVE_PREFLIGHT_CHANGED`: run dry-run again and review the new summary.
24
+ - `ARCHIVE_BUSY`: retry later. Do not remove or take over the lock; stale-lock recovery belongs to the later doctor/repair change.
25
+ - Guard or Contract failure: use the returned recovery command and do not submit.
26
+
27
+ If acceptance criteria or design must change while awaiting confirmation, use the sole Archive recovery path:
28
+
29
+ ```powershell
30
+ matrix workflow return design --reason acceptance-or-design-gap
31
+ ```
32
+
33
+ After successful commit, the change is under `.matrix/archive/` and the workflow is complete.
@@ -5,51 +5,38 @@ description: Implement the frozen active Matrix plan with test-first vertical sl
5
5
 
6
6
  # Matrix Build
7
7
 
8
- Read `plan.md`, `design.md`, and repository instructions. Implement only the frozen scope.
9
-
10
- ## Skills to Use
11
-
12
- ### Primary: `implement`
13
-
14
- Use the `implement` skill to execute the plan. It automatically:
15
-
16
- 1. Uses `/tdd` at pre-agreed seams for red-green-refactor cycles
17
- 2. Runs typechecking regularly
18
- 3. Runs single test files regularly during development
19
- 4. Runs the full test suite once at the end
20
- 5. Uses `/code-review` to review the work when done
21
- 6. Commits work to the current branch
22
-
23
- ### On Failure: `diagnosing-bugs`
24
-
25
- When encountering crashes, test failures, or build failures:
26
-
27
- 1. **Stop** — do not propose source code fixes before root cause is located
28
- 2. Load the `diagnosing-bugs` skill
29
- 3. Follow its feedback-loop discipline: build a tight red/pass signal first
30
- 4. Only after root cause is identified, implement the fix through `implement` + `tdd`
31
- 5. Record the diagnosis and fix in `artifacts/verification.md` under `## Build evidence`
32
-
33
- ### On Merge Conflicts: `resolving-merge-conflicts`
34
-
35
- If merge conflicts occur during implementation, use the `resolving-merge-conflicts` skill to resolve them systematically.
8
+ Run `matrix workflow inspect`. Read `plan.md`, `design.md`, the approved Contract, and repository instructions. Implement only the frozen scope.
9
+
10
+ ## Prim
11
+
12
+ Use Matrix's own implementation and test discipline.
13
+
14
+ ## Arch
15
+
16
+ - invoke `tdd` when the change modifies observable behavior at a testable seam;
17
+ - invoke `diagnosing-bugs` only when an actual failure exists and root cause is not established;
18
+ - invoke `resolving-merge-conflicts` only for an in-progress merge or rebase conflict.
19
+
20
+ 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.
36
21
 
37
22
  ## Scope Discipline
38
23
 
39
- If implementation exposes a scope or design change, **stop and return to `$matrix-design`**; do not amend the plan silently.
24
+ If implementation exposes a scope or design change, **stop and run**:
25
+
26
+ ```powershell
27
+ matrix workflow return design --reason design-gap
28
+ ```
29
+
30
+ Then enter `$matrix-design`; do not amend the plan silently. The Runtime preserves old verification evidence in revision history and requires fresh evidence after the redesigned plan returns to Build.
40
31
 
41
32
  ## Process
42
33
 
43
- 1. Load `implement` skill
44
- 2. Implement through vertical slices with TDD
45
- 3. On failure: load `diagnosing-bugs` skill
46
- 4. On merge conflicts: load `resolving-merge-conflicts` skill
47
- 5. Record concrete commands and outcomes under `## Build evidence` in `artifacts/verification.md`
48
- 6. Run the guard
49
- 7. If guard passes, run transition to advance to verify phase
50
- 8. **Then enter `$matrix-verify` to continue the workflow**
34
+ 1. Implement the approved plan in bounded vertical slices.
35
+ 2. Use only triggered Arch capabilities.
36
+ 3. Record commands, outcomes, capability invocations and fallbacks under `## Build evidence`.
37
+ 4. Run the guard and transition, then enter `$matrix-verify`.
51
38
 
52
39
  ```powershell
53
- matrix workflow guard build
54
- matrix workflow transition verify
40
+ matrix workflow guard build
41
+ matrix workflow transition verify
55
42
  ```