@rosenfan/matrix 0.1.3 → 0.1.5
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/LICENSE +21 -21
- package/README.md +72 -63
- package/README.zh-CN.md +70 -61
- package/{.claude → assets}/skills/matrix/SKILL.md +81 -86
- package/{.claude → assets}/skills/matrix/agents/openai.yaml +4 -4
- package/{.claude → assets}/skills/matrix/scripts/matrix_claude.py +11 -11
- package/{.claude → assets}/skills/matrix/scripts/matrix_state.py +60 -60
- package/assets/skills/matrix/scripts/test_matrix_claude.py +69 -0
- package/assets/skills/matrix/scripts/test_matrix_state.py +69 -0
- package/{.claude → assets}/skills/matrix-archive/SKILL.md +27 -27
- package/{.claude/skills/matrix-hotfix → assets/skills/matrix-archive}/agents/openai.yaml +4 -4
- package/{.claude → assets}/skills/matrix-build/SKILL.md +41 -38
- package/{.claude/skills/matrix-archive → assets/skills/matrix-build}/agents/openai.yaml +4 -4
- package/{.claude → assets}/skills/matrix-claude/SKILL.md +93 -93
- package/{.claude → assets}/skills/matrix-claude/agents/openai.yaml +4 -4
- package/{.claude → assets}/skills/matrix-design/SKILL.md +69 -67
- package/{.claude → assets}/skills/matrix-design/agents/openai.yaml +4 -4
- package/{.claude → assets}/skills/matrix-hotfix/SKILL.md +7 -7
- package/{.claude/skills/matrix-build → assets/skills/matrix-hotfix}/agents/openai.yaml +4 -4
- package/{.claude → assets}/skills/matrix-open/SKILL.md +26 -24
- package/assets/skills/matrix-open/agents/openai.yaml +4 -0
- package/{.claude → assets}/skills/matrix-status/SKILL.md +14 -14
- package/{.claude → assets}/skills/matrix-status/agents/openai.yaml +4 -4
- package/{.claude → assets}/skills/matrix-tweak/SKILL.md +7 -7
- package/assets/skills/matrix-tweak/agents/openai.yaml +4 -0
- package/assets/skills/matrix-verify/SKILL.md +89 -0
- package/assets/skills/matrix-verify/agents/openai.yaml +4 -0
- package/assets/skills-zh-CN/matrix/SKILL.md +27 -27
- package/assets/skills-zh-CN/matrix-archive/SKILL.md +10 -10
- package/assets/skills-zh-CN/matrix-build/SKILL.md +13 -11
- package/assets/skills-zh-CN/matrix-claude/SKILL.md +10 -10
- package/assets/skills-zh-CN/matrix-design/SKILL.md +12 -10
- package/assets/skills-zh-CN/matrix-hotfix/SKILL.md +1 -1
- package/assets/skills-zh-CN/matrix-open/SKILL.md +18 -16
- package/assets/skills-zh-CN/matrix-status/SKILL.md +3 -3
- package/assets/skills-zh-CN/matrix-tweak/SKILL.md +1 -1
- package/assets/skills-zh-CN/matrix-verify/SKILL.md +25 -9
- package/bin/matrix.js +15 -15
- package/package.json +4 -13
- package/src/catalog.js +110 -92
- package/src/cli.js +138 -80
- package/src/distribution.js +379 -253
- package/src/installer.js +4 -4
- package/src/matt-catalog.mjs +67 -0
- package/src/matt.js +91 -9
- package/src/messages.js +14 -10
- package/src/runtime-resolver.js +76 -0
- package/src/self-update.js +2 -2
- package/src/workflow-transaction.js +9 -5
- package/src/workflow.js +688 -429
- package/.claude/skills/matrix-open/agents/openai.yaml +0 -4
- package/.claude/skills/matrix-tweak/agents/openai.yaml +0 -4
- package/.claude/skills/matrix-verify/SKILL.md +0 -73
- package/.claude/skills/matrix-verify/agents/openai.yaml +0 -4
|
@@ -1,92 +1,87 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: matrix
|
|
3
|
-
description: Route, initialize, resume, and govern a persistent Matrix development workflow. Use when the user starts a feature, change, bug fix, or asks to resume managed development.
|
|
4
|
-
---
|
|
5
|
-
|
|
1
|
+
---
|
|
2
|
+
name: matrix
|
|
3
|
+
description: Route, initialize, resume, and govern a persistent Matrix development workflow. Use when the user starts a feature, change, bug fix, or asks to resume managed development.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
6
|
# Matrix
|
|
7
7
|
|
|
8
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
|
-
Read `artifact_language` from `matrix
|
|
11
|
-
|
|
12
|
-
## Route
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
```powershell
|
|
17
|
-
matrix
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
- **Prim** uses Matrix's own capability in every phase and never requires Matt Skills.
|
|
40
|
-
- **Arch** conditionally invokes the initialization-verified ten-Skill cohort when documented task-fact triggers are present.
|
|
41
|
-
|
|
10
|
+
Read `artifact_language` from `node <matrix-skill-directory>/scripts/matrix-runtime.mjs inspect`. Keep required Markdown heading tokens unchanged, but write every user-facing artifact body, explanation, and handoff in that language: English for `en`, Chinese for `zh-CN`.
|
|
11
|
+
|
|
12
|
+
## Route
|
|
13
|
+
|
|
14
|
+
For every workflow command, run the Runtime bundled with this project Skill so the project compatibility authority wins over a different global launcher:
|
|
15
|
+
|
|
16
|
+
```powershell
|
|
17
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs inspect
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Use the global `matrix` command for `init`, `update`, and bootstrap only. A trusted 0.1.4 launcher auto-delegates only to an exact, hash-verified 0.1.4 project Runtime. A different or unidentified project release remains authoritative but must be invoked through the exact direct command returned by the launcher. Older launchers cannot provide this cross-release check and must not be used as the project workflow entrypoint.
|
|
21
|
+
|
|
22
|
+
- 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.
|
|
23
|
+
- 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`.
|
|
24
|
+
- Do not create Matrix state for questions, read-only investigation, or requests outside software development.
|
|
25
|
+
- Before `init`, derive a short kebab-case change id and an accurate title. Start with `full` unless a shortcut clearly applies.
|
|
26
|
+
|
|
27
|
+
```powershell
|
|
28
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs init <change-id> --workflow <full|hotfix|tweak> --orchestration <prim|arch> --title "<title>"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
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`.
|
|
32
|
+
|
|
33
|
+
## Orchestration
|
|
34
|
+
|
|
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.
|
|
37
|
+
|
|
42
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.
|
|
43
|
-
|
|
44
|
-
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.
|
|
45
|
-
|
|
46
|
-
## Recovery
|
|
47
|
-
|
|
48
|
-
Use Runtime commands instead of editing Matrix state:
|
|
49
|
-
|
|
50
|
-
```powershell
|
|
51
|
-
matrix
|
|
52
|
-
matrix
|
|
53
|
-
matrix
|
|
54
|
-
matrix
|
|
55
|
-
matrix
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
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.
|
|
59
|
-
|
|
60
|
-
If any mutation reports `WORKFLOW_RECOVERY_REQUIRED`, stop normal phase work and run the read-only diagnosis:
|
|
61
|
-
|
|
62
|
-
```powershell
|
|
63
|
-
matrix
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Never infer a repair target or strategy. Use only the exact command returned by doctor:
|
|
67
|
-
|
|
68
|
-
```powershell
|
|
69
|
-
matrix
|
|
70
|
-
matrix
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Optional Claude handoff
|
|
74
|
-
|
|
75
|
-
`$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.
|
|
76
|
-
Use it only when the user explicitly chooses Codex design with Claude Code implementation; never inject it into the standard path.
|
|
77
|
-
|
|
78
|
-
## Cross-Cutting Skills
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
- A phase can
|
|
86
|
-
- A phase can move backward only through one of the documented `matrix workflow return` intents.
|
|
39
|
+
|
|
40
|
+
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.
|
|
41
|
+
|
|
42
|
+
## Recovery
|
|
43
|
+
|
|
44
|
+
Use Runtime commands instead of editing Matrix state:
|
|
45
|
+
|
|
46
|
+
```powershell
|
|
47
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs return design --reason design-gap
|
|
48
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs return build --reason verification-failed
|
|
49
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs return design --reason acceptance-or-design-gap
|
|
50
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs return design --reason acceptance-or-design-gap # from Archive, before terminal archive
|
|
51
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs abort --reason <requirement-cancelled|superseded|no-longer-valuable|blocked|other>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
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.
|
|
55
|
+
|
|
56
|
+
If any mutation reports `WORKFLOW_RECOVERY_REQUIRED`, stop normal phase work and run the read-only diagnosis:
|
|
57
|
+
|
|
58
|
+
```powershell
|
|
59
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs doctor
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Never infer a repair target or strategy. Use only the exact command returned by doctor:
|
|
63
|
+
|
|
64
|
+
```powershell
|
|
65
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs doctor --repair --transaction <id> --strategy <continue|rollback>
|
|
66
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs doctor --repair --lock <id>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Optional Claude handoff
|
|
70
|
+
|
|
71
|
+
`$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.
|
|
72
|
+
Use it only when the user explicitly chooses Codex design with Claude Code implementation; never inject it into the standard path.
|
|
73
|
+
|
|
74
|
+
## Cross-Cutting Skills
|
|
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.
|
|
77
|
+
|
|
78
|
+
## Rules
|
|
79
|
+
|
|
80
|
+
- A phase can advance only through `node <matrix-skill-directory>/scripts/matrix-runtime.mjs transition`; run its guard first.
|
|
81
|
+
- A phase can move backward only through one of the documented `node <matrix-skill-directory>/scripts/matrix-runtime.mjs return` intents.
|
|
87
82
|
- Full Build approval binds proposal, design, and plan. Lightweight Build approval binds its compact proposal, requires `transition build --confirmed`, and rejects project implementation changes made after initialization but before approval.
|
|
88
|
-
- A successful Archive is always a two-step optimistic commit: run `matrix
|
|
89
|
-
- `matrix
|
|
90
|
-
- Do not replace required evidence with an assertion that work is complete.
|
|
91
|
-
- Pause for user confirmation at material architecture/scope decisions and before archive/commit.
|
|
92
|
-
- On shortcut scope expansion, run `matrix
|
|
83
|
+
- A successful Archive is always a two-step optimistic commit: run `node <matrix-skill-directory>/scripts/matrix-runtime.mjs archive --dry-run`, then pass its exact hash to `node <matrix-skill-directory>/scripts/matrix-runtime.mjs archive --expect-preflight <sha256>`. Never bypass the preflight.
|
|
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
|
+
- Do not replace required evidence with an assertion that work is complete.
|
|
86
|
+
- Pause for user confirmation at material architecture/scope decisions and before archive/commit.
|
|
87
|
+
- 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.
|
|
@@ -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,11 +1,11 @@
|
|
|
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
|
+
#!/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,60 +1,60 @@
|
|
|
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
|
-
|
|
26
|
-
if
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if
|
|
41
|
-
return [
|
|
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
|
+
#!/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
|
+
development = development_runtime()
|
|
26
|
+
if development is not None:
|
|
27
|
+
if not development.is_file():
|
|
28
|
+
raise RuntimeError(f"Configured Matrix development runtime was not found: {development}")
|
|
29
|
+
node = shutil.which("node")
|
|
30
|
+
if node is None:
|
|
31
|
+
raise RuntimeError("Node.js is required to run the Matrix workflow.")
|
|
32
|
+
return [node, str(development), *args]
|
|
33
|
+
|
|
34
|
+
bundled = Path(__file__).with_name("matrix-runtime.mjs")
|
|
35
|
+
node = shutil.which("node")
|
|
36
|
+
if node is not None and bundled.exists():
|
|
37
|
+
return [node, str(bundled), *args]
|
|
38
|
+
|
|
39
|
+
primary = primary_runtime()
|
|
40
|
+
if primary is not None:
|
|
41
|
+
return [primary, "workflow", *args, "--json"]
|
|
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())
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
import tempfile
|
|
7
|
+
import unittest
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
STATE = Path(__file__).with_name("matrix_state.py")
|
|
11
|
+
CLAUDE = Path(__file__).with_name("matrix_claude.py")
|
|
12
|
+
RUNTIME = Path(__file__).resolve().parents[4] / "src" / "workflow.js"
|
|
13
|
+
NODE_DIRECTORY = str(Path(shutil.which("node") or "").parent)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MatrixClaudeTests(unittest.TestCase):
|
|
17
|
+
def invoke(self, script, *args, cwd):
|
|
18
|
+
env = {**os.environ, "PATH": NODE_DIRECTORY, "MATRIX_DEVELOPMENT_RUNTIME": str(RUNTIME)}
|
|
19
|
+
return subprocess.run([sys.executable, str(script), *args], cwd=cwd, env=env, text=True, capture_output=True)
|
|
20
|
+
|
|
21
|
+
def inspect(self, cwd):
|
|
22
|
+
return json.loads(self.invoke(STATE, "inspect", cwd=cwd).stdout)
|
|
23
|
+
|
|
24
|
+
def make_design_change(self, cwd):
|
|
25
|
+
self.assertEqual(self.invoke(STATE, "init", "bridge", "--title", "Bridge", cwd=cwd).returncode, 0)
|
|
26
|
+
artifacts = cwd / ".matrix/changes/bridge/artifacts"
|
|
27
|
+
(artifacts / "proposal.md").write_text("## Goal\nBuild a bounded feature.\n\n## Scope\nOne module only.\n\n## Non-goals\nNo extra work.\n\n## Acceptance\nA focused test passes.\n\n## Risks\nKnown risk.\n")
|
|
28
|
+
self.assertEqual(self.invoke(STATE, "transition", "design", cwd=cwd).returncode, 0)
|
|
29
|
+
(artifacts / "design.md").write_text("## Decisions\nKeep one public seam.\n\n## Boundaries\nOne public seam only.\n\n## Test seams\nUse the public interface.\n\n## Risks\nKnown risk.\n")
|
|
30
|
+
(artifacts / "plan.md").write_text("## Steps\n1. Add a failing test.\n\n## Validation\nRun the focused test.\n\n## Stop conditions\nStop on scope change.\n")
|
|
31
|
+
self.assertEqual(self.invoke(STATE, "transition", "build", cwd=cwd).returncode, 0)
|
|
32
|
+
|
|
33
|
+
def test_generic_export_does_not_change_matrix_state(self):
|
|
34
|
+
with tempfile.TemporaryDirectory() as temp:
|
|
35
|
+
cwd = Path(temp)
|
|
36
|
+
self.make_design_change(cwd)
|
|
37
|
+
result = self.invoke(CLAUDE, "export", "--task-id", "GEN-001", cwd=cwd)
|
|
38
|
+
self.assertEqual(result.returncode, 0, result.stderr)
|
|
39
|
+
task = cwd / ".matrix/changes/bridge/artifacts/claude-task.md"
|
|
40
|
+
self.assertIn("## Prohibited work", task.read_text(encoding="utf-8"))
|
|
41
|
+
self.assertEqual(self.inspect(cwd)["phase"], "build")
|
|
42
|
+
|
|
43
|
+
def test_fnsec_board_refuses_existing_objective(self):
|
|
44
|
+
with tempfile.TemporaryDirectory() as temp:
|
|
45
|
+
cwd = Path(temp)
|
|
46
|
+
self.make_design_change(cwd)
|
|
47
|
+
tasks = cwd / "docs/tasks"
|
|
48
|
+
tasks.mkdir(parents=True)
|
|
49
|
+
(tasks / "active.md").write_text("# Active task\n", encoding="utf-8")
|
|
50
|
+
result = self.invoke(CLAUDE, "export", "--task-id", "FNSEC-X", "--target", "fnsec", "--apply-fnsec-board", cwd=cwd)
|
|
51
|
+
self.assertNotEqual(result.returncode, 0)
|
|
52
|
+
self.assertFalse((tasks / "claude-task-FNSEC-X.md").exists(), result.stdout + result.stderr)
|
|
53
|
+
|
|
54
|
+
def test_fnsec_export_updates_empty_board_only(self):
|
|
55
|
+
with tempfile.TemporaryDirectory() as temp:
|
|
56
|
+
cwd = Path(temp)
|
|
57
|
+
self.make_design_change(cwd)
|
|
58
|
+
tasks = cwd / "docs/tasks"
|
|
59
|
+
tasks.mkdir(parents=True)
|
|
60
|
+
(tasks / "active.md").write_text("# NO_ACTIVE_OBJECTIVE\n", encoding="utf-8")
|
|
61
|
+
result = self.invoke(CLAUDE, "export", "--task-id", "FNSEC-Y", "--target", "fnsec", "--apply-fnsec-board", cwd=cwd)
|
|
62
|
+
self.assertEqual(result.returncode, 0, result.stderr)
|
|
63
|
+
self.assertIn("## Prohibited work", (tasks / "claude-task-FNSEC-Y.md").read_text(encoding="utf-8"))
|
|
64
|
+
self.assertIn("CLAUDE_QUEUED", (tasks / "active.md").read_text(encoding="utf-8"))
|
|
65
|
+
self.assertEqual(self.inspect(cwd)["phase"], "build")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == "__main__":
|
|
69
|
+
unittest.main()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import importlib.util
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
import tempfile
|
|
8
|
+
import unittest
|
|
9
|
+
from unittest import mock
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
SCRIPT = Path(__file__).with_name("matrix_state.py")
|
|
13
|
+
RUNTIME = Path(__file__).resolve().parents[4] / "src" / "workflow.js"
|
|
14
|
+
NODE_DIRECTORY = str(Path(shutil.which("node") or "").parent)
|
|
15
|
+
class MatrixStateTests(unittest.TestCase):
|
|
16
|
+
def invoke(self, *args, cwd):
|
|
17
|
+
env = {**os.environ, "PATH": NODE_DIRECTORY, "MATRIX_DEVELOPMENT_RUNTIME": str(RUNTIME)}
|
|
18
|
+
return subprocess.run([sys.executable, str(SCRIPT), *args], cwd=cwd, env=env, text=True, capture_output=True)
|
|
19
|
+
def load_adapter(self, directory):
|
|
20
|
+
script = directory / "matrix_state.py"
|
|
21
|
+
shutil.copyfile(SCRIPT, script)
|
|
22
|
+
spec = importlib.util.spec_from_file_location(f"matrix_state_{directory.name}", script)
|
|
23
|
+
module = importlib.util.module_from_spec(spec)
|
|
24
|
+
spec.loader.exec_module(module)
|
|
25
|
+
return module
|
|
26
|
+
def test_runtime_precedence_is_bundled_then_global(self):
|
|
27
|
+
with tempfile.TemporaryDirectory() as temp:
|
|
28
|
+
directory = Path(temp)
|
|
29
|
+
adapter = self.load_adapter(directory)
|
|
30
|
+
bundled = directory / "matrix-runtime.mjs"
|
|
31
|
+
bundled.write_text("// project runtime\n", encoding="utf-8")
|
|
32
|
+
with mock.patch.object(adapter, "development_runtime", return_value=None), mock.patch.object(adapter, "primary_runtime", return_value="global-matrix"):
|
|
33
|
+
command = adapter.runtime_command(["inspect"])
|
|
34
|
+
self.assertEqual(Path(command[1]), bundled)
|
|
35
|
+
bundled.unlink()
|
|
36
|
+
with mock.patch.object(adapter, "development_runtime", return_value=None), mock.patch.object(adapter, "primary_runtime", return_value="global-matrix"):
|
|
37
|
+
self.assertEqual(adapter.runtime_command(["inspect"]), ["global-matrix", "workflow", "inspect", "--json"])
|
|
38
|
+
def test_phase_guard_and_recovery(self):
|
|
39
|
+
with tempfile.TemporaryDirectory() as temp:
|
|
40
|
+
cwd = Path(temp); result = self.invoke("init", "sample", "--title", "Sample", cwd=cwd); self.assertEqual(result.returncode, 0)
|
|
41
|
+
self.assertTrue((cwd / ".matrix" / "active.json").exists())
|
|
42
|
+
self.assertFalse((cwd / ".codex").exists())
|
|
43
|
+
self.assertNotEqual(self.invoke("transition", "design", cwd=cwd).returncode, 0)
|
|
44
|
+
p = cwd / ".matrix/changes/sample/artifacts"
|
|
45
|
+
(p / "proposal.md").write_text("## Goal\nBuild a bounded feature.\n\n## Scope\nOne module only.\n\n## Non-goals\nNo extra work.\n\n## Acceptance\nA focused test passes.\n\n## Risks\nKnown risk.\n")
|
|
46
|
+
self.assertEqual(self.invoke("transition", "design", cwd=cwd).returncode, 0)
|
|
47
|
+
self.assertEqual(json.loads(self.invoke("inspect", cwd=cwd).stdout)["phase"], "design")
|
|
48
|
+
|
|
49
|
+
def test_full_lifecycle_requires_evidence(self):
|
|
50
|
+
with tempfile.TemporaryDirectory() as temp:
|
|
51
|
+
cwd = Path(temp); self.assertEqual(self.invoke("init", "full", "--title", "Full", cwd=cwd).returncode, 0)
|
|
52
|
+
p = cwd / ".matrix/changes/full/artifacts"
|
|
53
|
+
(p / "proposal.md").write_text("## Goal\nBuild a bounded feature.\n\n## Scope\nOne module only.\n\n## Non-goals\nNo extra work.\n\n## Acceptance\nA focused test passes.\n\n## Risks\nKnown risk.\n")
|
|
54
|
+
self.assertEqual(self.invoke("transition", "design", cwd=cwd).returncode, 0)
|
|
55
|
+
self.assertNotEqual(self.invoke("transition", "build", cwd=cwd).returncode, 0)
|
|
56
|
+
(p / "design.md").write_text("## Decisions\nKeep one public seam.\n\n## Boundaries\nOne public seam only.\n\n## Test seams\nUse the public interface.\n\n## Risks\nKnown risk.\n")
|
|
57
|
+
(p / "plan.md").write_text("## Steps\n1. Add a failing test.\n\n## Validation\nRun the focused test.\n\n## Stop conditions\nStop on scope change.\n")
|
|
58
|
+
self.assertEqual(self.invoke("transition", "build", cwd=cwd).returncode, 0)
|
|
59
|
+
(p / "verification.md").write_text("## Build evidence\nThe focused implementation test command completed successfully with an expected result.\n")
|
|
60
|
+
self.assertEqual(self.invoke("transition", "verify", cwd=cwd).returncode, 0)
|
|
61
|
+
self.assertNotEqual(self.invoke("transition", "archive", cwd=cwd).returncode, 0)
|
|
62
|
+
(p / "verification.md").write_text("## Build evidence\nThe focused implementation test command completed successfully with an expected result.\n\n## Test evidence\nThe acceptance command passed and its output was reviewed for the requested behavior.\n\n## Review evidence\nThe fixed-baseline standards and specification review found no blocking issue.\n")
|
|
63
|
+
self.assertEqual(self.invoke("transition", "archive", cwd=cwd).returncode, 0)
|
|
64
|
+
preflight = self.invoke("archive", "--dry-run", cwd=cwd)
|
|
65
|
+
self.assertEqual(preflight.returncode, 0)
|
|
66
|
+
token = json.loads(preflight.stdout)["preflight_hash"]
|
|
67
|
+
self.assertEqual(self.invoke("archive", "--expect-preflight", token, cwd=cwd).returncode, 0)
|
|
68
|
+
self.assertTrue((cwd / ".matrix/archive/full/matrix.yaml").exists())
|
|
69
|
+
if __name__ == "__main__": unittest.main()
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: matrix-archive
|
|
3
|
-
description: Close and archive a verified Matrix change. Use when the active Matrix phase is archive.
|
|
4
|
-
---
|
|
5
|
-
|
|
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
6
|
# Matrix Archive
|
|
7
7
|
|
|
8
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
9
|
|
|
10
10
|
Write archive summaries and handoff prose in `artifact_language`; keep Runtime-required English heading tokens unchanged.
|
|
11
|
-
|
|
12
|
-
Archive is Matrix-owned in both Prim and Arch. Do not invoke companion implementation, review, or architecture capabilities here.
|
|
13
|
-
|
|
14
|
-
## Mandatory two-step Archive
|
|
15
|
-
|
|
16
|
-
Run the read-only preflight and review its bounded effect summary. Then submit exactly the hash returned by that preflight:
|
|
17
|
-
|
|
18
|
-
```powershell
|
|
19
|
-
matrix
|
|
20
|
-
matrix
|
|
11
|
+
|
|
12
|
+
Archive is Matrix-owned in both Prim and Arch. Do not invoke companion implementation, review, or architecture capabilities here.
|
|
13
|
+
|
|
14
|
+
## Mandatory two-step Archive
|
|
15
|
+
|
|
16
|
+
Run the read-only preflight and review its bounded effect summary. Then submit exactly the hash returned by that preflight:
|
|
17
|
+
|
|
18
|
+
```powershell
|
|
19
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs archive --dry-run
|
|
20
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs archive --expect-preflight <sha256-returned-by-dry-run>
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Bare Archive is rejected. Runtime reacquires the project Archive boundary and recomputes the entire protected change-directory manifest before committing.
|
|
24
24
|
For hotfix/tweak, ask for explicit confirmation after reviewing the preflight, then use the returned command with `--confirmed`. Runtime rejects a shortcut Archive without it.
|
|
25
|
-
|
|
26
|
-
- `ARCHIVE_PREFLIGHT_CHANGED`: run dry-run again and review the new summary.
|
|
27
|
-
- `ARCHIVE_BUSY`: retry later. Do not remove or take over the lock; stale-lock recovery belongs to the later doctor/repair change.
|
|
28
|
-
- Guard or Contract failure: use the returned recovery command and do not submit.
|
|
29
|
-
|
|
30
|
-
If acceptance criteria or design must change while awaiting confirmation, use the sole Archive recovery path:
|
|
31
|
-
|
|
32
|
-
```powershell
|
|
33
|
-
matrix
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
After successful commit, the change is under `.matrix/archive/` and the workflow is complete.
|
|
25
|
+
|
|
26
|
+
- `ARCHIVE_PREFLIGHT_CHANGED`: run dry-run again and review the new summary.
|
|
27
|
+
- `ARCHIVE_BUSY`: retry later. Do not remove or take over the lock; stale-lock recovery belongs to the later doctor/repair change.
|
|
28
|
+
- Guard or Contract failure: use the returned recovery command and do not submit.
|
|
29
|
+
|
|
30
|
+
If acceptance criteria or design must change while awaiting confirmation, use the sole Archive recovery path:
|
|
31
|
+
|
|
32
|
+
```powershell
|
|
33
|
+
node <matrix-skill-directory>/scripts/matrix-runtime.mjs return design --reason acceptance-or-design-gap
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
After successful commit, the change is under `.matrix/archive/` and the workflow is complete.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
interface:
|
|
2
|
-
display_name: "Matrix Workflow"
|
|
3
|
-
short_description: "Persistent development workflow stage"
|
|
4
|
-
default_prompt: "Use $name to continue a Matrix development workflow."
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Matrix Workflow"
|
|
3
|
+
short_description: "Persistent development workflow stage"
|
|
4
|
+
default_prompt: "Use $name to continue a Matrix development workflow."
|