agentopology 0.9.1 → 0.10.0

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 (178) hide show
  1. package/README.md +17 -19
  2. package/dist/__tests__/e2e/action-guard.test.d.ts +13 -0
  3. package/dist/__tests__/e2e/action-guard.test.d.ts.map +1 -0
  4. package/dist/__tests__/e2e/action-guard.test.js +299 -0
  5. package/dist/__tests__/e2e/action-guard.test.js.map +1 -0
  6. package/dist/__tests__/e2e/compile.test.js +1 -1
  7. package/dist/__tests__/e2e/compile.test.js.map +1 -1
  8. package/dist/__tests__/e2e/conformance.test.d.ts +13 -0
  9. package/dist/__tests__/e2e/conformance.test.d.ts.map +1 -0
  10. package/dist/__tests__/e2e/conformance.test.js +109 -0
  11. package/dist/__tests__/e2e/conformance.test.js.map +1 -0
  12. package/dist/__tests__/e2e/contract-drift.test.d.ts +11 -0
  13. package/dist/__tests__/e2e/contract-drift.test.d.ts.map +1 -0
  14. package/dist/__tests__/e2e/contract-drift.test.js +142 -0
  15. package/dist/__tests__/e2e/contract-drift.test.js.map +1 -0
  16. package/dist/__tests__/e2e/fail-closed.test.d.ts +11 -0
  17. package/dist/__tests__/e2e/fail-closed.test.d.ts.map +1 -0
  18. package/dist/__tests__/e2e/fail-closed.test.js +111 -0
  19. package/dist/__tests__/e2e/fail-closed.test.js.map +1 -0
  20. package/dist/__tests__/e2e/guard-check.test.d.ts +9 -0
  21. package/dist/__tests__/e2e/guard-check.test.d.ts.map +1 -0
  22. package/dist/__tests__/e2e/guard-check.test.js +316 -0
  23. package/dist/__tests__/e2e/guard-check.test.js.map +1 -0
  24. package/dist/__tests__/e2e/guard-target.test.d.ts +11 -0
  25. package/dist/__tests__/e2e/guard-target.test.d.ts.map +1 -0
  26. package/dist/__tests__/e2e/guard-target.test.js +198 -0
  27. package/dist/__tests__/e2e/guard-target.test.js.map +1 -0
  28. package/dist/__tests__/e2e/inventory.test.d.ts +18 -0
  29. package/dist/__tests__/e2e/inventory.test.d.ts.map +1 -0
  30. package/dist/__tests__/e2e/inventory.test.js +187 -0
  31. package/dist/__tests__/e2e/inventory.test.js.map +1 -0
  32. package/dist/__tests__/e2e/matcher.test.d.ts +14 -0
  33. package/dist/__tests__/e2e/matcher.test.d.ts.map +1 -0
  34. package/dist/__tests__/e2e/matcher.test.js +145 -0
  35. package/dist/__tests__/e2e/matcher.test.js.map +1 -0
  36. package/dist/__tests__/e2e/step-check.test.d.ts +9 -0
  37. package/dist/__tests__/e2e/step-check.test.d.ts.map +1 -0
  38. package/dist/__tests__/e2e/step-check.test.js +158 -0
  39. package/dist/__tests__/e2e/step-check.test.js.map +1 -0
  40. package/dist/__tests__/e2e/strength.test.d.ts +17 -0
  41. package/dist/__tests__/e2e/strength.test.d.ts.map +1 -0
  42. package/dist/__tests__/e2e/strength.test.js +130 -0
  43. package/dist/__tests__/e2e/strength.test.js.map +1 -0
  44. package/dist/__tests__/e2e/verified-on.test.d.ts +2 -0
  45. package/dist/__tests__/e2e/verified-on.test.d.ts.map +1 -0
  46. package/dist/__tests__/e2e/verified-on.test.js +57 -0
  47. package/dist/__tests__/e2e/verified-on.test.js.map +1 -0
  48. package/dist/__tests__/open-findings.test.js +1 -0
  49. package/dist/__tests__/open-findings.test.js.map +1 -1
  50. package/dist/bindings/__tests__/bindings.test.js +6 -686
  51. package/dist/bindings/__tests__/bindings.test.js.map +1 -1
  52. package/dist/bindings/__tests__/defaults-agreement.test.js +0 -12
  53. package/dist/bindings/__tests__/defaults-agreement.test.js.map +1 -1
  54. package/dist/bindings/__tests__/hook-command.test.js +2 -2
  55. package/dist/bindings/__tests__/hook-command.test.js.map +1 -1
  56. package/dist/bindings/__tests__/mod-plugin.test.js +2 -2
  57. package/dist/bindings/__tests__/mod-plugin.test.js.map +1 -1
  58. package/dist/bindings/claude-code.d.ts +45 -1
  59. package/dist/bindings/claude-code.d.ts.map +1 -1
  60. package/dist/bindings/claude-code.js +191 -19
  61. package/dist/bindings/claude-code.js.map +1 -1
  62. package/dist/bindings/index.d.ts +5 -5
  63. package/dist/bindings/index.d.ts.map +1 -1
  64. package/dist/bindings/index.js +9 -16
  65. package/dist/bindings/index.js.map +1 -1
  66. package/dist/bindings/lib/stub.d.ts +15 -1
  67. package/dist/bindings/lib/stub.d.ts.map +1 -1
  68. package/dist/bindings/lib/stub.js +27 -2
  69. package/dist/bindings/lib/stub.js.map +1 -1
  70. package/dist/bindings/types.d.ts +6 -1
  71. package/dist/bindings/types.d.ts.map +1 -1
  72. package/dist/cli/__tests__/cli.test.js +25 -0
  73. package/dist/cli/__tests__/cli.test.js.map +1 -1
  74. package/dist/cli/index.js +222 -8
  75. package/dist/cli/index.js.map +1 -1
  76. package/dist/conformance/claude-code.d.ts +99 -0
  77. package/dist/conformance/claude-code.d.ts.map +1 -0
  78. package/dist/conformance/claude-code.js +242 -0
  79. package/dist/conformance/claude-code.js.map +1 -0
  80. package/dist/conformance/extract.d.ts +70 -0
  81. package/dist/conformance/extract.d.ts.map +1 -0
  82. package/dist/conformance/extract.js +185 -0
  83. package/dist/conformance/extract.js.map +1 -0
  84. package/dist/docs/content.d.ts.map +1 -1
  85. package/dist/docs/content.js +57 -32
  86. package/dist/docs/content.js.map +1 -1
  87. package/dist/guard/check.d.ts +53 -0
  88. package/dist/guard/check.d.ts.map +1 -0
  89. package/dist/guard/check.js +366 -0
  90. package/dist/guard/check.js.map +1 -0
  91. package/dist/guard/target.d.ts +45 -0
  92. package/dist/guard/target.d.ts.map +1 -0
  93. package/dist/guard/target.js +168 -0
  94. package/dist/guard/target.js.map +1 -0
  95. package/dist/import/__tests__/import.test.js +0 -554
  96. package/dist/import/__tests__/import.test.js.map +1 -1
  97. package/dist/import/index.d.ts +0 -1
  98. package/dist/import/index.d.ts.map +1 -1
  99. package/dist/import/index.js +1 -7
  100. package/dist/import/index.js.map +1 -1
  101. package/dist/import/serializer.d.ts.map +1 -1
  102. package/dist/import/serializer.js +41 -0
  103. package/dist/import/serializer.js.map +1 -1
  104. package/dist/index.d.ts +0 -4
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js +0 -4
  107. package/dist/index.js.map +1 -1
  108. package/dist/inventory/index.d.ts +65 -0
  109. package/dist/inventory/index.d.ts.map +1 -0
  110. package/dist/inventory/index.js +272 -0
  111. package/dist/inventory/index.js.map +1 -0
  112. package/dist/parser/ast.d.ts +46 -0
  113. package/dist/parser/ast.d.ts.map +1 -1
  114. package/dist/parser/ast.js.map +1 -1
  115. package/dist/parser/index.d.ts +6 -1
  116. package/dist/parser/index.d.ts.map +1 -1
  117. package/dist/parser/index.js +66 -4
  118. package/dist/parser/index.js.map +1 -1
  119. package/dist/parser/known-fields.d.ts +3 -0
  120. package/dist/parser/known-fields.d.ts.map +1 -1
  121. package/dist/parser/known-fields.js +5 -3
  122. package/dist/parser/known-fields.js.map +1 -1
  123. package/dist/parser/ledger.d.ts +36 -0
  124. package/dist/parser/ledger.d.ts.map +1 -0
  125. package/dist/parser/ledger.js +72 -0
  126. package/dist/parser/ledger.js.map +1 -0
  127. package/dist/parser/run.d.ts +6 -0
  128. package/dist/parser/run.d.ts.map +1 -1
  129. package/dist/parser/run.js +10 -0
  130. package/dist/parser/run.js.map +1 -1
  131. package/dist/parser/validator.d.ts.map +1 -1
  132. package/dist/parser/validator.js +255 -1
  133. package/dist/parser/validator.js.map +1 -1
  134. package/dist/scaffold/manifest.d.ts.map +1 -1
  135. package/dist/scaffold/manifest.js +0 -8
  136. package/dist/scaffold/manifest.js.map +1 -1
  137. package/dist/scaffold/merge.d.ts.map +1 -1
  138. package/dist/scaffold/merge.js +1 -3
  139. package/dist/scaffold/merge.js.map +1 -1
  140. package/dist/step/check.d.ts +56 -0
  141. package/dist/step/check.d.ts.map +1 -0
  142. package/dist/step/check.js +125 -0
  143. package/dist/step/check.js.map +1 -0
  144. package/dist/sync/index.d.ts +0 -2
  145. package/dist/sync/index.d.ts.map +1 -1
  146. package/dist/sync/index.js +0 -109
  147. package/dist/sync/index.js.map +1 -1
  148. package/package.json +3 -1
  149. package/spec/claude-code/README.md +19 -0
  150. package/spec/claude-code/hooks-contract.2.1.295.json +143 -0
  151. package/spec/claude-code/hooks-contract.json +142 -0
  152. package/spec/grammar.md +45 -7
  153. package/spec/testing.md +16 -0
  154. package/spec/validation.md +11 -2
  155. package/dist/__tests__/live/zz-debug.test.d.ts +0 -2
  156. package/dist/__tests__/live/zz-debug.test.d.ts.map +0 -1
  157. package/dist/__tests__/live/zz-debug.test.js +0 -4
  158. package/dist/__tests__/live/zz-debug.test.js.map +0 -1
  159. package/dist/bindings/copilot-cli.d.ts +0 -20
  160. package/dist/bindings/copilot-cli.d.ts.map +0 -1
  161. package/dist/bindings/copilot-cli.js +0 -1516
  162. package/dist/bindings/copilot-cli.js.map +0 -1
  163. package/dist/bindings/gemini-cli.d.ts +0 -18
  164. package/dist/bindings/gemini-cli.d.ts.map +0 -1
  165. package/dist/bindings/gemini-cli.js +0 -1318
  166. package/dist/bindings/gemini-cli.js.map +0 -1
  167. package/dist/bindings/kiro.d.ts +0 -17
  168. package/dist/bindings/kiro.d.ts.map +0 -1
  169. package/dist/bindings/kiro.js +0 -1615
  170. package/dist/bindings/kiro.js.map +0 -1
  171. package/dist/bindings/openclaw.d.ts +0 -18
  172. package/dist/bindings/openclaw.d.ts.map +0 -1
  173. package/dist/bindings/openclaw.js +0 -1947
  174. package/dist/bindings/openclaw.js.map +0 -1
  175. package/dist/import/openclaw.d.ts +0 -70
  176. package/dist/import/openclaw.d.ts.map +0 -1
  177. package/dist/import/openclaw.js +0 -1060
  178. package/dist/import/openclaw.js.map +0 -1
@@ -0,0 +1,187 @@
1
+ /**
2
+ * `agentopology inventory <dir>` and `validate <file> --inventory` (N34):
3
+ * which live things in a project the .at accounts for, and which things the
4
+ * .at points at that are not there.
5
+ *
6
+ * inventory lists what is live under a project (the .claude tree, each
7
+ * settings.json hook handler, .mcp.json servers, package.json scripts), with a
8
+ * sha256 fingerprint per file. It reads and prints; it never writes.
9
+ *
10
+ * Coverage matches each live thing to the .at in two ways: the file or hook is
11
+ * what scaffold would write from this .at, or the .at points at it by hand
12
+ * (file:, a hook's run, a gate's run, a tool's script, at:, an mcp server).
13
+ * A live thing nothing accounts for is unmapped (a warning). A path the .at
14
+ * points at that is not on disk is missing (an error, exit 1). Things tagged
15
+ * "e" (the platform, the machine) are counted apart and never required.
16
+ */
17
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
18
+ import { spawnSync } from "node:child_process";
19
+ import { createHash } from "node:crypto";
20
+ import * as fs from "node:fs";
21
+ import * as os from "node:os";
22
+ import { resolve, dirname, join } from "node:path";
23
+ import { fileURLToPath } from "node:url";
24
+ import { parse } from "../../parser/index.js";
25
+ import { claudeCodeBinding } from "../../bindings/claude-code.js";
26
+ import { collectInventory, coverage } from "../../inventory/index.js";
27
+ const here = dirname(fileURLToPath(import.meta.url));
28
+ const FIXTURE = resolve(here, "fixtures/compile.at");
29
+ const CLI = resolve(here, "../../../dist/cli/index.js");
30
+ const source = fs.readFileSync(FIXTURE, "utf-8");
31
+ const ast = parse(source);
32
+ let root;
33
+ const at = (p) => join(root, p);
34
+ const write = (p, text) => { fs.mkdirSync(dirname(at(p)), { recursive: true }); fs.writeFileSync(at(p), text); };
35
+ const settings = () => JSON.parse(fs.readFileSync(at(".claude/settings.json"), "utf8"));
36
+ const report = () => coverage(ast, collectInventory(root), root);
37
+ const ids = (items) => items.map((i) => i.id);
38
+ const cli = (...a) => spawnSync("node", [CLI, ...a], { encoding: "utf-8", cwd: root });
39
+ beforeEach(() => {
40
+ root = fs.mkdtempSync(join(os.tmpdir(), "at-inventory-"));
41
+ for (const f of claudeCodeBinding.scaffold(ast))
42
+ write(f.path, f.content);
43
+ });
44
+ afterEach(() => fs.rmSync(root, { recursive: true, force: true }));
45
+ describe("inventory lists what is live", () => {
46
+ it("[N34] [IV1] agents, skills, skill scripts, settings hooks and mcp servers, each file with its sha256", () => {
47
+ const items = collectInventory(root);
48
+ const byId = new Map(items.map((i) => [i.id, i]));
49
+ expect(byId.get("agent:drafter")).toMatchObject({ kind: "agent", path: ".claude/agents/drafter/AGENT.md" });
50
+ expect(byId.get("skill:tone")).toMatchObject({ kind: "skill", path: ".claude/skills/tone/SKILL.md" });
51
+ expect(byId.get("skill-file:compile/scripts/before-agent.sh")).toMatchObject({ kind: "skill-file" });
52
+ expect(byId.get("mcp:search")).toMatchObject({ kind: "mcp-server" });
53
+ expect(items.filter((i) => i.kind === "hook").length).toBeGreaterThan(3);
54
+ const tone = byId.get("skill:tone");
55
+ expect(tone.fingerprint).toBe(createHash("sha256").update(fs.readFileSync(at(tone.path))).digest("hex"));
56
+ });
57
+ it("[N34] [IV1] the listing is sorted and stable, and settings.local.json is suggested as e (machine-local)", () => {
58
+ write(".claude/settings.local.json", "{}\n");
59
+ const a = collectInventory(root);
60
+ expect(ids(a)).toEqual([...ids(a)].sort());
61
+ expect(collectInventory(root)).toEqual(a);
62
+ expect(a.find((i) => i.path === ".claude/settings.local.json")).toMatchObject({ tag: "e" });
63
+ });
64
+ it("[N34] [IV1] worktrees and node_modules are skipped, and a skill's working folder is one item, not every file in it", () => {
65
+ write(".claude/worktrees/agent-1/.claude/agents/copy.md", "x\n");
66
+ write(".claude/node_modules/pkg/index.js", "x\n");
67
+ write(".claude/skills/tone/scripts/node_modules/dep/index.js", "x\n");
68
+ write(".claude/skills/tone/references/style.md", "x\n");
69
+ write(".claude/skills/tone/workspace/p1/render.mp4", "x\n");
70
+ write(".claude/skills/tone/workspace/p2/render.mp4", "x\n");
71
+ write(".claude/skills/tone/scripts/__pycache__/lock.cpython-312.pyc", "x\n");
72
+ const got = ids(collectInventory(root));
73
+ expect(got.filter((id) => /worktrees|node_modules|__pycache__/.test(id))).toEqual([]);
74
+ expect(got).toContain("skill-file:tone/references/style.md");
75
+ expect(got.filter((id) => id.includes("tone/workspace"))).toEqual(["skill-dir:tone/workspace"]);
76
+ });
77
+ it("[N34] [IV1] package.json scripts are live things too", () => {
78
+ write("package.json", JSON.stringify({ scripts: { post: "node media.js post", test: "vitest" } }));
79
+ expect(ids(collectInventory(root))).toEqual(expect.arrayContaining(["script:post", "script:test"]));
80
+ });
81
+ });
82
+ describe("coverage: the .at against what is live", () => {
83
+ it("[N34] [IV2] a freshly scaffolded project is fully covered, nothing unmapped, nothing missing", () => {
84
+ const r = report();
85
+ expect(r.unmapped).toEqual([]);
86
+ expect(r.missing).toEqual([]);
87
+ expect(r.mapped).toBe(r.total);
88
+ expect(r.total).toBeGreaterThan(20);
89
+ });
90
+ it("[N34] [IV3] hand-built things nothing in the .at accounts for are unmapped, by kind", () => {
91
+ write(".claude/agents/stray.md", "---\nname: stray\n---\nA hand-made agent.\n");
92
+ write(".claude/commands/ship.md", "Ship it.\n");
93
+ const s = settings();
94
+ s.hooks.PreToolUse = [...(s.hooks.PreToolUse ?? []), { matcher: "mcp__chrome__upload_image", hooks: [{ type: "command", command: "python3 \"$CLAUDE_PROJECT_DIR/tools/lock.py\"" }] }];
95
+ fs.writeFileSync(at(".claude/settings.json"), JSON.stringify(s, null, 2));
96
+ const mcp = JSON.parse(fs.readFileSync(at(".mcp.json"), "utf8"));
97
+ mcp.mcpServers.chrome = { command: "chrome-mcp" };
98
+ fs.writeFileSync(at(".mcp.json"), JSON.stringify(mcp));
99
+ const r = report();
100
+ const un = ids(r.unmapped);
101
+ expect(un).toEqual(expect.arrayContaining(["agent:stray", "command:ship", "mcp:chrome"]));
102
+ expect(un.some((id) => id.startsWith("hook:PreToolUse") && id.includes("tools/lock.py"))).toBe(true);
103
+ expect(r.unmapped).toHaveLength(4);
104
+ expect(r.mapped).toBe(r.total - 4);
105
+ });
106
+ it("[N34] [IV3] a hand-written file inside a skill the .at declares counts under that skill", () => {
107
+ write(".claude/skills/tone/references/house-style.md", "Short sentences.\n");
108
+ expect(report().unmapped).toEqual([]);
109
+ });
110
+ it("[N34] [IV3] a declared store's data files count under the store", () => {
111
+ const mem = parse(fs.readFileSync(resolve(here, "../../../examples/memory-system.at"), "utf-8").replace('".memory/semantic/"', '".claude/stores/semantic/"'));
112
+ write(".claude/stores/semantic/index.db", "x\n");
113
+ write(".claude/stores/stray/rows.json", "x\n");
114
+ const un = ids(coverage(mem, collectInventory(root), root).unmapped);
115
+ expect(un).toContain("claude-file:stores/stray/rows.json");
116
+ expect(un).not.toContain("claude-file:stores/semantic/index.db");
117
+ });
118
+ it("[N34] [IV4] a path the .at points at that is not on disk is missing, named with what points at it", () => {
119
+ fs.rmSync(at("docs/tools/check.py"));
120
+ fs.rmSync(at("tools/render.py"));
121
+ const r = report();
122
+ expect(r.missing.map((m) => m.path).sort()).toEqual(["docs/tools/check.py", "tools/render.py"]);
123
+ expect(r.missing.find((m) => m.path === "docs/tools/check.py").from).toContain("copy-check");
124
+ expect(r.missing.find((m) => m.path === "tools/render.py").from).toContain("render");
125
+ });
126
+ it("[N34] [IV4] a generated file that is not there is reported as not scaffolded, not as missing", () => {
127
+ fs.rmSync(at(".claude/skills/compile/scripts/before-agent.sh"));
128
+ const r = report();
129
+ expect(r.missing).toEqual([]);
130
+ expect(r.notScaffolded).toContain(".claude/skills/compile/scripts/before-agent.sh");
131
+ });
132
+ it("[N34] [IV5] things tagged e are counted apart and never required", () => {
133
+ write(".claude/agents/stray.md", "x\n");
134
+ const items = collectInventory(root).map((i) => (i.id === "agent:stray" ? { ...i, tag: "e" } : i));
135
+ const r = coverage(ast, items, root);
136
+ expect(r.unmapped).toEqual([]);
137
+ expect(ids(r.external)).toEqual(["agent:stray"]);
138
+ expect(r.total).toBe(items.length - 1);
139
+ });
140
+ it("[N34] [IV6] a hand-written hook the .at declares by its run maps the hand-built settings entry", () => {
141
+ const withLock = parse(source.replace(" hook finished {", ` hook lock {\n on: PreToolUse\n matcher: "Bash"\n run: "tools/lock.py"\n }\n hook finished {`));
142
+ write("tools/lock.py", "print('ok')\n");
143
+ const s = settings();
144
+ s.hooks.PreToolUse = [...(s.hooks.PreToolUse ?? []), { matcher: "Bash", hooks: [{ type: "command", command: "python3 \"$CLAUDE_PROJECT_DIR/tools/lock.py\" --strict" }] }];
145
+ fs.writeFileSync(at(".claude/settings.json"), JSON.stringify(s, null, 2));
146
+ expect(coverage(withLock, collectInventory(root), root).unmapped).toEqual([]);
147
+ expect(coverage(ast, collectInventory(root), root).unmapped).toHaveLength(1);
148
+ });
149
+ });
150
+ describe("matcher gaps against the inventory's tool names", () => {
151
+ it("[N36] a refuse hook that guards a server is checked against every tool the inventory lists for it", () => {
152
+ const locked = parse(source.replace(" hook finished {", ` hook lock-search {\n on: PreToolUse\n matcher: "mcp__search__find"\n run: "scripts/lock.sh"\n strength: refuse\n }\n hook finished {`));
153
+ const items = [...collectInventory(root), { id: "mcp-tool:mcp__search__find", kind: "mcp-tool" }, { id: "mcp-tool:mcp__search__crawl", kind: "mcp-tool" }];
154
+ const r = coverage(locked, items, root);
155
+ // crawl comes from the inventory; find.all from the drafter's own tools: list.
156
+ expect(r.gaps.map((g) => g.tool).sort()).toEqual(["mcp__search__crawl", "mcp__search__find.all"]);
157
+ expect(ids(r.unmapped)).not.toContain("mcp-tool:mcp__search__crawl");
158
+ });
159
+ });
160
+ describe("the CLI", () => {
161
+ it("[N34] [IV7] inventory <dir> prints JSON and writes nothing", () => {
162
+ const before = fs.readdirSync(root, { recursive: true }).sort();
163
+ const r = cli("inventory", root);
164
+ expect(r.status).toBe(0);
165
+ const json = JSON.parse(r.stdout);
166
+ expect(json.version).toBe(1);
167
+ expect(json.items.map((i) => i.id)).toContain("agent:drafter");
168
+ expect(fs.readdirSync(root, { recursive: true }).sort()).toEqual(before);
169
+ });
170
+ it("[N34] [IV7] validate --inventory <dir> prints coverage; unmapped warns, exit 0", () => {
171
+ write(".claude/agents/stray.md", "x\n");
172
+ const r = cli("validate", FIXTURE, "--inventory", root);
173
+ expect(r.stdout).toMatch(/coverage: \d+ of \d+ live things are in the \.at/);
174
+ expect(r.stdout).toContain("agent:stray");
175
+ expect(r.status).toBe(0);
176
+ });
177
+ it("[N34] [IV7] validate --inventory reads a saved inventory.json; a missing path exits 1", () => {
178
+ fs.rmSync(at("tools/render.py"));
179
+ const saved = join(root, "inventory.json");
180
+ fs.writeFileSync(saved, cli("inventory", root).stdout);
181
+ const r = cli("validate", FIXTURE, "--inventory", saved, "--dir", root);
182
+ expect(r.stdout).toMatch(/missing \(1\)/);
183
+ expect(r.stdout).toContain("tools/render.py");
184
+ expect(r.status).toBe(1);
185
+ });
186
+ });
187
+ //# sourceMappingURL=inventory.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inventory.test.js","sourceRoot":"","sources":["../../../src/__tests__/e2e/inventory.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Hook matchers, read the way Claude Code reads them (N36), and the two hook
3
+ * findings that ride with them (N29, N30).
4
+ *
5
+ * From https://code.claude.com/docs/en/hooks.md (checked 2026-11-10 against
6
+ * the raw page): a matcher of only letters, digits, `_`, `-`, spaces, `,` and
7
+ * `|` is an exact name or a list of names; anything else is a JavaScript
8
+ * regex, unanchored. `*`, `""` and no matcher match all. MCP tools are named
9
+ * `mcp__<server>__<tool>`, so `mcp__memory` (exact) matches no tool. An
10
+ * argument filter goes in the handler's `if` (permission-rule syntax, tool
11
+ * events only; on any other event the hook never runs). Only exit 2 blocks.
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=matcher.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matcher.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/e2e/matcher.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Hook matchers, read the way Claude Code reads them (N36), and the two hook
3
+ * findings that ride with them (N29, N30).
4
+ *
5
+ * From https://code.claude.com/docs/en/hooks.md (checked 2026-11-10 against
6
+ * the raw page): a matcher of only letters, digits, `_`, `-`, spaces, `,` and
7
+ * `|` is an exact name or a list of names; anything else is a JavaScript
8
+ * regex, unanchored. `*`, `""` and no matcher match all. MCP tools are named
9
+ * `mcp__<server>__<tool>`, so `mcp__memory` (exact) matches no tool. An
10
+ * argument filter goes in the handler's `if` (permission-rule syntax, tool
11
+ * events only; on any other event the hook never runs). Only exit 2 blocks.
12
+ */
13
+ import { describe, it, expect } from "vitest";
14
+ import { parse } from "../../parser/index.js";
15
+ import { validate } from "../../parser/validator.js";
16
+ import { serializeAST } from "../../import/serializer.js";
17
+ import { claudeCodeBinding } from "../../bindings/claude-code.js";
18
+ import { matcherMatches } from "../../conformance/claude-code.js";
19
+ import { matcherGaps } from "../../inventory/index.js";
20
+ const topology = (hooks, agentExtra = "") => `topology probe : [pipeline] {
21
+ meta {
22
+ version: "1.0.0"
23
+ description: "Matcher probe"
24
+ }
25
+
26
+ orchestrator {
27
+ model: sonnet
28
+ }
29
+
30
+ agent writer {
31
+ model: sonnet
32
+ description: "Writes the post"
33
+ tools: [Read, mcp.chrome.file_upload, mcp.chrome.upload_image, mcp.chrome.browser_batch]
34
+ ${agentExtra} prompt {
35
+ Write the post.
36
+ }
37
+ }
38
+
39
+ flow {
40
+ writer -> writer [max 1]
41
+ }
42
+
43
+ hooks {
44
+ ${hooks} }
45
+ }
46
+ `;
47
+ const hook = (name, fields) => ` hook ${name} {\n${fields.split("\n").map((l) => ` ${l}`).join("\n")}\n }\n`;
48
+ const rules = (src, rule, level) => validate(parse(src)).filter((r) => r.rule === rule && (!level || r.level === level));
49
+ const files = (src) => claudeCodeBinding.scaffold(parse(src));
50
+ const settingsOf = (src) => JSON.parse(files(src).find((f) => f.path === ".claude/settings.json").content);
51
+ describe("a matcher is read the way Claude Code reads it", () => {
52
+ it("[N36] exact names and lists, unanchored regexes, match-all", () => {
53
+ expect(matcherMatches("PreToolUse", "Bash", "Bash")).toBe(true);
54
+ expect(matcherMatches("PreToolUse", "Bash", "BashOutput")).toBe(false);
55
+ expect(matcherMatches("PreToolUse", "Edit|Write", "Write")).toBe(true);
56
+ expect(matcherMatches("PreToolUse", "Edit, Write", "Edit")).toBe(true);
57
+ expect(matcherMatches("SubagentStop", "code-reviewer", "code-reviewer")).toBe(true);
58
+ expect(matcherMatches("PreToolUse", "^Notebook", "NotebookEdit")).toBe(true);
59
+ expect(matcherMatches("PreToolUse", "Edit.*", "NotebookEdit")).toBe(true);
60
+ expect(matcherMatches("PreToolUse", "^Edit$", "NotebookEdit")).toBe(false);
61
+ expect(matcherMatches("PreToolUse", "*", "anything")).toBe(true);
62
+ expect(matcherMatches("PreToolUse", "", "anything")).toBe(true);
63
+ });
64
+ it("[N36] MCP: the server prefix alone matches no tool; with .* it matches every tool on it", () => {
65
+ expect(matcherMatches("PreToolUse", "mcp__memory", "mcp__memory__create_entities")).toBe(false);
66
+ expect(matcherMatches("PreToolUse", "mcp__memory__.*", "mcp__memory__create_entities")).toBe(true);
67
+ });
68
+ it("[N36] on FileChanged and StopFailure a hyphen keeps the matcher on the regex path", () => {
69
+ expect(matcherMatches("FileChanged", "a-b", "xa-by")).toBe(true);
70
+ expect(matcherMatches("PreToolUse", "a-b", "xa-by")).toBe(false);
71
+ });
72
+ });
73
+ describe("V107: a matcher that can never match is an error", () => {
74
+ it("[N30] permission-rule syntax in a matcher never matches a tool name; the filter belongs in if:", () => {
75
+ const e = rules(topology(hook("guard", 'on: PreToolUse\nmatcher: "Bash(rm -rf *)"\nrun: "scripts/guard.sh"')), "V107", "error");
76
+ expect(e).toHaveLength(1);
77
+ expect(e[0].message).toMatch(/matcher: "Bash".*if: "Bash\(rm -rf \*\)"/);
78
+ });
79
+ it("[N36] the .at's dotted mcp form is not a tool name; the server prefix alone matches nothing", () => {
80
+ const dotted = rules(topology(hook("lock", 'on: PreToolUse\nmatcher: "mcp.chrome.*"\nrun: "scripts/lock.sh"')), "V107", "error");
81
+ expect(dotted[0]?.message).toContain("mcp__chrome__.*");
82
+ const bare = rules(topology(hook("lock", 'on: PreToolUse\nmatcher: "mcp__chrome"\nrun: "scripts/lock.sh"')), "V107", "error");
83
+ expect(bare[0]?.message).toContain("mcp__chrome__.*");
84
+ });
85
+ it("[N36] a matcher that is not a valid regex is an error; agent-level hooks are checked too", () => {
86
+ expect(rules(topology(hook("bad", 'on: PreToolUse\nmatcher: "Edit|(Write"\nrun: "scripts/x.sh"')), "V107", "error")).toHaveLength(1);
87
+ const agentHook = ` hooks {\n hook lint {\n on: PostToolUse\n matcher: "Write(*.md)"\n run: "scripts/lint.sh"\n }\n }\n`;
88
+ expect(rules(topology(hook("ok", 'on: PreToolUse\nmatcher: "Bash"\nrun: "scripts/x.sh"'), agentHook), "V107", "error")).toHaveLength(1);
89
+ });
90
+ it("[N36] good matchers pass", () => {
91
+ const src = topology(hook("a", 'on: PreToolUse\nmatcher: "Bash"\nrun: "scripts/a.sh"') +
92
+ hook("b", 'on: PreToolUse\nmatcher: "mcp__chrome__.*"\nrun: "scripts/b.sh"') +
93
+ hook("c", 'on: PostToolUse\nmatcher: "Edit|Write"\nrun: "scripts/c.sh"'));
94
+ expect(rules(src, "V107")).toEqual([]);
95
+ });
96
+ });
97
+ describe("if: filters on the tool's arguments", () => {
98
+ const src = topology(hook("guard", 'on: PreToolUse\nmatcher: "Bash"\nif: "Bash(rm -rf *)"\nrun: "scripts/guard.sh"'));
99
+ it("[N30] if: is read, round-trips, and is written on the settings.json handler", () => {
100
+ expect(parse(src).hooks[0]).toMatchObject({ matcher: "Bash", if: "Bash(rm -rf *)" });
101
+ expect(parse(serializeAST(parse(src))).hooks).toEqual(parse(src).hooks);
102
+ const entry = settingsOf(src).hooks.PreToolUse.find((e) => e.matcher === "Bash");
103
+ expect(entry.hooks[0].if).toBe("Bash(rm -rf *)");
104
+ expect(validate(parse(src)).filter((r) => r.level === "error")).toEqual([]);
105
+ });
106
+ it("[N30] if: on an event that is not a tool event is an error: the hook would never run", () => {
107
+ const e = rules(topology(hook("g", 'on: Stop\nif: "Bash(rm *)"\nrun: "scripts/g.sh"')), "V107", "error");
108
+ expect(e[0]?.message).toMatch(/never runs/);
109
+ });
110
+ });
111
+ describe("a run: that is a shell command, not a script", () => {
112
+ it("[N29] run: \"exit 2\" is written as the command itself, with no stub script named after exit", () => {
113
+ const out = files(topology(hook("block", 'on: PreToolUse\nmatcher: "Bash"\nrun: "exit 2"')));
114
+ expect(out.some((f) => /\/exit$/.test(f.path))).toBe(false);
115
+ const s = JSON.parse(out.find((f) => f.path === ".claude/settings.json").content);
116
+ expect(s.hooks.PreToolUse[0].hooks[0].command).toBe("exit 2");
117
+ });
118
+ it("[N29] V108: exit 1 (or any code but 0 and 2) does not block; it is a warning", () => {
119
+ const w = rules(topology(hook("block", 'on: PreToolUse\nmatcher: "Bash"\nrun: "exit 1"')), "V108", "warning");
120
+ expect(w[0]?.message).toMatch(/only exit 2 blocks/);
121
+ expect(rules(topology(hook("block", 'on: PreToolUse\nmatcher: "Bash"\nrun: "exit 2"')), "V108")).toEqual([]);
122
+ });
123
+ });
124
+ describe("V109: a refuse hook that misses a tool its server offers", () => {
125
+ const lock = (matcher, strength = "strength: refuse\n") => topology(hook("lock-chrome", `on: PreToolUse\nmatcher: "${matcher}"\nrun: "scripts/lock.sh"\n${strength}`.trimEnd()));
126
+ it("[N36] lock-chrome covers file_upload and browser_batch but not upload_image: named as a gap", () => {
127
+ const src = lock("mcp__chrome__(file_upload|browser_batch)");
128
+ const w = rules(src, "V109", "warning");
129
+ expect(w).toHaveLength(1);
130
+ expect(w[0].message).toContain("mcp__chrome__upload_image");
131
+ expect(w[0].message).toContain("lock-chrome");
132
+ expect(matcherGaps(parse(src))).toEqual([{ hook: "lock-chrome", server: "chrome", tool: "mcp__chrome__upload_image" }]);
133
+ });
134
+ it("[N36] a matcher that covers the server has no gap; a hook that declares no refuse is not held to it", () => {
135
+ expect(rules(lock("mcp__chrome__.*"), "V109")).toEqual([]);
136
+ expect(rules(lock("mcp__chrome__(file_upload|browser_batch)", ""), "V109")).toEqual([]);
137
+ });
138
+ it("[N36] tool names the inventory lists count too", () => {
139
+ const ast = parse(lock("mcp__chrome__.*").replace("mcp.chrome.upload_image, ", ""));
140
+ const gaps = matcherGaps(parse(lock("mcp__chrome__file_upload")), ["mcp__chrome__navigate"]);
141
+ expect(gaps.map((g) => g.tool)).toEqual(expect.arrayContaining(["mcp__chrome__navigate", "mcp__chrome__upload_image", "mcp__chrome__browser_batch"]));
142
+ expect(matcherGaps(ast, ["mcp__chrome__navigate"])).toEqual([]);
143
+ });
144
+ });
145
+ //# sourceMappingURL=matcher.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matcher.test.js","sourceRoot":"","sources":["../../../src/__tests__/e2e/matcher.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAE,UAAU,GAAG,EAAE,EAAE,EAAE,CAAC;;;;;;;;;;;;;;EAcnD,UAAU;;;;;;;;;;EAUV,KAAK;;CAEN,CAAC;AACF,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,MAAc,EAAE,EAAE,CAAC,YAAY,IAAI,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;AACxI,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,IAAY,EAAE,KAA2B,EAAE,EAAE,CACvE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC;AACvF,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AACtE,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,uBAAuB,CAAE,CAAC,OAAO,CAAC,CAAC;AAEpH,QAAQ,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC9D,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChE,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvE,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvE,MAAM,CAAC,cAAc,CAAC,cAAc,EAAE,eAAe,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpF,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7E,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1E,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3E,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjE,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yFAAyF,EAAE,GAAG,EAAE;QACjG,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,aAAa,EAAE,8BAA8B,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChG,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,iBAAiB,EAAE,8BAA8B,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mFAAmF,EAAE,GAAG,EAAE;QAC3F,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjE,MAAM,CAAC,cAAc,CAAC,YAAY,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kDAAkD,EAAE,GAAG,EAAE;IAChE,EAAE,CAAC,gGAAgG,EAAE,GAAG,EAAE;QACxG,MAAM,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,oEAAoE,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAChI,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,0CAA0C,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6FAA6F,EAAE,GAAG,EAAE;QACrG,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,iEAAiE,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QACjI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,gEAAgE,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9H,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0FAA0F,EAAE,GAAG,EAAE;QAClG,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,6DAA6D,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACrI,MAAM,SAAS,GAAG,2IAA2I,CAAC;QAC9J,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,sDAAsD,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC1I,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,GAAG,GAAG,QAAQ,CAClB,IAAI,CAAC,GAAG,EAAE,sDAAsD,CAAC;YACjE,IAAI,CAAC,GAAG,EAAE,iEAAiE,CAAC;YAC5E,IAAI,CAAC,GAAG,EAAE,6DAA6D,CAAC,CACzE,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qCAAqC,EAAE,GAAG,EAAE;IACnD,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,gFAAgF,CAAC,CAAC,CAAC;IAEtH,EAAE,CAAC,6EAA6E,EAAE,GAAG,EAAE;QACrF,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,gBAAgB,EAAE,CAAC,CAAC;QACrF,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QACxE,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC;QACtF,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACjD,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sFAAsF,EAAE,GAAG,EAAE;QAC9F,MAAM,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,iDAAiD,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QACzG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,8CAA8C,EAAE,GAAG,EAAE;IAC5D,EAAE,CAAC,8FAA8F,EAAE,GAAG,EAAE;QACtG,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,gDAAgD,CAAC,CAAC,CAAC,CAAC;QAC7F,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5D,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,uBAAuB,CAAE,CAAC,OAAO,CAAC,CAAC;QACnF,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8EAA8E,EAAE,GAAG,EAAE;QACtF,MAAM,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,gDAAgD,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;QAC9G,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;QACpD,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,gDAAgD,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC/G,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0DAA0D,EAAE,GAAG,EAAE;IACxE,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,QAAQ,GAAG,oBAAoB,EAAE,EAAE,CAChE,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,6BAA6B,OAAO,8BAA8B,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAExH,EAAE,CAAC,6FAA6F,EAAE,GAAG,EAAE;QACrG,MAAM,GAAG,GAAG,IAAI,CAAC,0CAA0C,CAAC,CAAC;QAC7D,MAAM,CAAC,GAAG,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;QACxC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC;QAC5D,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QAC9C,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,2BAA2B,EAAE,CAAC,CAAC,CAAC;IAC1H,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qGAAqG,EAAE,GAAG,EAAE;QAC7G,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3D,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,0CAA0C,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAC,CAAC;QACpF,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC,EAAE,CAAC,uBAAuB,CAAC,CAAC,CAAC;QAC7F,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,uBAAuB,EAAE,2BAA2B,EAAE,4BAA4B,CAAC,CAAC,CAAC,CAAC;QACtJ,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `agentopology step check <file.at> <step-id> [--unit U] [--dir D]` (N39).
3
+ * A hook guards a Claude Code session only: a script run from a terminal or a
4
+ * scheduler never sees it. The step calls step check at its start (by: code)
5
+ * and stops on exit 1. It reads ledgers and runs check scripts; it never
6
+ * writes.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=step-check.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-check.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/e2e/step-check.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * `agentopology step check <file.at> <step-id> [--unit U] [--dir D]` (N39).
3
+ * A hook guards a Claude Code session only: a script run from a terminal or a
4
+ * scheduler never sees it. The step calls step check at its start (by: code)
5
+ * and stops on exit 1. It reads ledgers and runs check scripts; it never
6
+ * writes.
7
+ */
8
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
9
+ import { spawnSync } from "node:child_process";
10
+ import * as fs from "node:fs";
11
+ import * as os from "node:os";
12
+ import { resolve, dirname, join } from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ import { parse } from "../../parser/index.js";
15
+ import { checkStep, renderStepReport } from "../../step/check.js";
16
+ const here = dirname(fileURLToPath(import.meta.url));
17
+ const CLI = resolve(here, "../../../dist/cli/index.js");
18
+ const SRC = `topology ship : [pipeline] {
19
+ meta { version: "1.0.0" description: "Render, then publish" }
20
+ orchestrator { model: sonnet handles: [render, publish, notify, tidy] }
21
+ action render { kind: external commands: ["./tool render"] }
22
+ action publish { kind: external commands: ["./tool publish"] unit-from: "./tool publish (\\S+)" }
23
+ action notify { kind: external commands: ["./tool notify"] }
24
+ action tidy { kind: external commands: ["./tool tidy"] }
25
+ action archive { kind: external commands: ["./tool archive"] }
26
+ records {
27
+ record trace { path: "logs/{unit}/trace.jsonl" }
28
+ }
29
+ gates {
30
+ gate publish-ready {
31
+ after: render
32
+ before: publish
33
+ ledger: trace
34
+ }
35
+ gate notify-ready {
36
+ before: notify
37
+ run: "./check-notify.sh"
38
+ }
39
+ gate both {
40
+ after: render
41
+ before: archive
42
+ ledger: trace
43
+ run: "./check-notify.sh"
44
+ }
45
+ gate empty-gate {
46
+ before: tidy
47
+ }
48
+ }
49
+ flow { render -> publish -> notify -> tidy -> archive }
50
+ }
51
+ `;
52
+ const ast = parse(SRC);
53
+ // With one record declared, a gate with no ledger: reads it. Case (c) needs a topology with none.
54
+ const bare = parse(SRC.replace(/ records \{[\s\S]*?\n \}\n/, "").replace(/ledger: trace\n/g, ""));
55
+ let dir;
56
+ const at = (p) => join(dir, p);
57
+ const rec = (step, ok, unit) => ({ step, ...(unit ? { unit } : {}), ok, at: "2026-01-01T00:00:00Z" });
58
+ const ledger = (unit, lines) => {
59
+ fs.mkdirSync(at(`logs/${unit}`), { recursive: true });
60
+ fs.writeFileSync(at(`logs/${unit}/trace.jsonl`), lines.map((l) => JSON.stringify(l)).join("\n") + "\n");
61
+ };
62
+ const script = (code, name = "check-notify.sh") => fs.writeFileSync(at(name), `#!/bin/sh\necho "checked from $(pwd)" > ran.txt\necho "script said no" >&2\nexit ${code}\n`, { mode: 0o755 });
63
+ beforeEach(() => { dir = fs.mkdtempSync(join(os.tmpdir(), "at-step-check-")); });
64
+ afterEach(() => fs.rmSync(dir, { recursive: true, force: true }));
65
+ describe("[N39] step check: the three ways a gate is satisfied", () => {
66
+ it("[N39] SC1 (a) a ledger whose last record for the after: step is ok satisfies the gate", () => {
67
+ ledger("ep42", [rec("render", true, "ep42")]);
68
+ const r = checkStep(ast, "publish", { unit: "ep42", dir });
69
+ expect(r.ok).toBe(true);
70
+ expect(r.gates).toEqual([expect.objectContaining({ gate: "publish-ready", ok: true })]);
71
+ });
72
+ it("[N39] SC2 (a) no record, a later ok:false, or no ledger file fails and says why", () => {
73
+ let r = checkStep(ast, "publish", { unit: "ep42", dir });
74
+ expect(r.ok).toBe(false);
75
+ expect(r.gates[0].reasons.join(" ")).toContain("logs/ep42/trace.jsonl");
76
+ ledger("ep42", [rec("render", true, "ep42"), rec("render", false, "ep42")]);
77
+ r = checkStep(ast, "publish", { unit: "ep42", dir });
78
+ expect(r.ok).toBe(false);
79
+ expect(r.gates[0].reasons.join(" ")).toMatch(/not ok/);
80
+ });
81
+ it("[N39] SC3 (a) the unit filter: another unit's ok record does not count, and a path with {unit} needs --unit", () => {
82
+ ledger("ep41", [rec("render", true, "ep41")]);
83
+ ledger("ep42", [rec("render", true, "ep41")]);
84
+ expect(checkStep(ast, "publish", { unit: "ep42", dir }).ok).toBe(false);
85
+ const none = checkStep(ast, "publish", { dir });
86
+ expect(none.ok).toBe(false);
87
+ expect(none.gates[0].reasons.join(" ")).toMatch(/--unit/);
88
+ });
89
+ it("[N39] SC4 a unit that would climb out of the project fails instead of reading a path", () => {
90
+ expect(checkStep(ast, "publish", { unit: "../../etc", dir }).ok).toBe(false);
91
+ });
92
+ it("[N39] SC5 (b) a run: that exits 0 from D satisfies the gate; non-zero fails, with its output", () => {
93
+ script(0);
94
+ let r = checkStep(ast, "notify", { dir });
95
+ expect(r.ok).toBe(true);
96
+ expect(fs.readFileSync(at("ran.txt"), "utf8")).toContain(fs.realpathSync(dir));
97
+ script(1);
98
+ r = checkStep(ast, "notify", { dir });
99
+ expect(r.ok).toBe(false);
100
+ expect(r.gates[0].reasons.join(" ")).toContain("script said no");
101
+ });
102
+ it("[N39] SC6 (c) a gate with neither ledger nor run: is unsatisfiable: nothing to check", () => {
103
+ const r = checkStep(bare, "tidy", { dir });
104
+ expect(r.ok).toBe(false);
105
+ expect(r.gates[0].reasons.join(" ")).toContain("nothing to check");
106
+ // a ledger answers "did the after: step finish ok?": with no after: there is nothing to look up, even with one record declared
107
+ expect(checkStep(ast, "tidy", { unit: "ep42", dir }).gates[0].reasons.join(" ")).toContain("nothing to check");
108
+ });
109
+ it("[N39] SC7 a gate with a ledger and a run: needs both", () => {
110
+ script(0);
111
+ expect(checkStep(ast, "archive", { unit: "ep42", dir }).ok).toBe(false);
112
+ ledger("ep42", [rec("render", true, "ep42")]);
113
+ expect(checkStep(ast, "archive", { unit: "ep42", dir }).ok).toBe(true);
114
+ script(3);
115
+ expect(checkStep(ast, "archive", { unit: "ep42", dir }).ok).toBe(false);
116
+ });
117
+ it("[N39] SC8 a step no gate guards passes with the line \"no gate guards <step>\"", () => {
118
+ const r = checkStep(ast, "render", { dir });
119
+ expect(r).toMatchObject({ ok: true, gates: [] });
120
+ expect(renderStepReport(r)).toEqual(["no gate guards render"]);
121
+ });
122
+ it("[N39] SC9 it never writes: the ledger is left as it was", () => {
123
+ ledger("ep42", [rec("render", true, "ep42")]);
124
+ const before = fs.readFileSync(at("logs/ep42/trace.jsonl"), "utf8");
125
+ checkStep(ast, "publish", { unit: "ep42", dir });
126
+ expect(fs.readFileSync(at("logs/ep42/trace.jsonl"), "utf8")).toBe(before);
127
+ expect(fs.readdirSync(dir).sort()).toEqual(["logs"]);
128
+ });
129
+ it("[N39] SC10 the rendering names each failing gate and why", () => {
130
+ const text = renderStepReport(checkStep(ast, "publish", { unit: "ep42", dir })).join("\n");
131
+ expect(text).toContain("publish-ready");
132
+ expect(text).toMatch(/fail/i);
133
+ });
134
+ });
135
+ describe("[N39] step check: the CLI", () => {
136
+ const run = (...args) => spawnSync("node", [CLI, "step", "check", ...args], { encoding: "utf8" });
137
+ let file;
138
+ beforeEach(() => { file = at("ship.at"); fs.writeFileSync(file, SRC); });
139
+ it("[N39] SC11 exits 0 when satisfied, 1 when not, naming the gate", () => {
140
+ ledger("ep42", [rec("render", true, "ep42")]);
141
+ const ok = run(file, "publish", "--unit", "ep42", "--dir", dir);
142
+ expect(ok.status).toBe(0);
143
+ const bad = run(file, "publish", "--unit", "ep43", "--dir", dir);
144
+ expect(bad.status).toBe(1);
145
+ expect(bad.stdout + bad.stderr).toContain("publish-ready");
146
+ });
147
+ it("[N39] SC12 an unguarded step exits 0 and says so; --dir defaults to .", () => {
148
+ const r = spawnSync("node", [CLI, "step", "check", file, "render"], { encoding: "utf8", cwd: dir });
149
+ expect(r.status).toBe(0);
150
+ expect(r.stdout).toContain("no gate guards render");
151
+ });
152
+ it("[N39] SC13 a step that is no node, or missing arguments, exits 1", () => {
153
+ expect(run(file, "nope", "--dir", dir).status).toBe(1);
154
+ expect(run(file).status).toBe(1);
155
+ expect(run().status).toBe(1);
156
+ });
157
+ });
158
+ //# sourceMappingURL=step-check.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step-check.test.js","sourceRoot":"","sources":["../../../src/__tests__/e2e/step-check.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * How strongly a step is enforced, written in the file (N32).
3
+ *
4
+ * `strength: refuse | detect | hope | none` says what stops a step: code or a
5
+ * hook refuses it, a critic detects it after the fact, prose hopes, or
6
+ * nothing. `by: code | hook | settings | gate` says which mechanism. A gate or
7
+ * hook can cite the code that refuses (`at: "file:line"`, `match:` the text
8
+ * expected there) and name its way around (`bypass:`). A flow edge can cite
9
+ * its guard (`[guard: lock-bash]`) or write its strength down
10
+ * (`[strength: none]`), so an absence is declared rather than implied.
11
+ *
12
+ * The validator checks the words and the references (V104 to V106). Whether
13
+ * the cited code is there and says what `match:` claims is read from disk by
14
+ * `guard --check`, not here: validate never reads the project.
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=strength.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strength.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/e2e/strength.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}