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,316 @@
1
+ /**
2
+ * `agentopology guard <file> --check` (GC): which gates, approval steps and
3
+ * permission rules a project actually enforces today. It reads; it never
4
+ * writes. Built on compile.at: brief-check (a gate before an agent, run by
5
+ * before-agent.sh), copy-check (a gate after an agent, a SubagentStop hook),
6
+ * approve (a human step before drafter), and settings ask and deny rules.
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 { claudeCodeBinding } from "../../bindings/claude-code.js";
16
+ import { checkGuards } from "../../guard/check.js";
17
+ import { validate } from "../../parser/validator.js";
18
+ const here = dirname(fileURLToPath(import.meta.url));
19
+ const FIXTURE = resolve(here, "fixtures/compile.at");
20
+ const CLI = resolve(here, "../../../dist/cli/index.js");
21
+ const ast = parse(fs.readFileSync(FIXTURE, "utf-8"));
22
+ const SCRIPTS = ".claude/skills/compile/scripts";
23
+ let root;
24
+ const at = (p) => join(root, p);
25
+ const settings = () => JSON.parse(fs.readFileSync(at(".claude/settings.json"), "utf8"));
26
+ const writeSettings = (s) => fs.writeFileSync(at(".claude/settings.json"), JSON.stringify(s, null, 2));
27
+ const item = (id) => checkGuards(ast, root).items.find((i) => i.id === id);
28
+ beforeEach(() => {
29
+ root = fs.mkdtempSync(join(os.tmpdir(), "at-guard-check-"));
30
+ for (const f of claudeCodeBinding.scaffold(ast)) {
31
+ fs.mkdirSync(dirname(at(f.path)), { recursive: true });
32
+ fs.writeFileSync(at(f.path), f.content);
33
+ }
34
+ // copy-check runs the user's own python script; give it one.
35
+ fs.mkdirSync(at("docs/tools"), { recursive: true });
36
+ fs.writeFileSync(at("docs/tools/check.py"), "print('ok')\n");
37
+ });
38
+ afterEach(() => fs.rmSync(root, { recursive: true, force: true }));
39
+ describe("guard --check on a freshly scaffolded project", () => {
40
+ it("[N31] [GC1] a gate after an agent refuses by its SubagentStop hook", () => {
41
+ const g = item("copy-check");
42
+ expect(g).toMatchObject({ kind: "gate", strength: "refuse", by: "hook" });
43
+ expect(g.where).toContain("SubagentStop");
44
+ expect(g.where).toContain("drafter");
45
+ });
46
+ it("[N31] [GC1] a gate before an agent refuses by the before-agent guard; its unwritten script is reported as blocking every run", () => {
47
+ const g = item("brief-check");
48
+ expect(g).toMatchObject({ kind: "gate", strength: "refuse", by: "hook" });
49
+ expect(g.where).toContain("PreToolUse");
50
+ expect(g.notes.join(" ")).toMatch(/stub.*blocks every run/);
51
+ });
52
+ it("[N31] [GC1] a human step before an agent refuses by asking in the session, and says -p denies it", () => {
53
+ const h = item("approve");
54
+ expect(h).toMatchObject({ kind: "human", strength: "refuse", by: "hook" });
55
+ expect(h.notes.join(" ")).toMatch(/-p/);
56
+ });
57
+ it("[N31] [GC1] deny and ask rules refuse by settings", () => {
58
+ expect(item("deny mcp__notes__delete")).toMatchObject({ kind: "permission", strength: "refuse", by: "settings" });
59
+ expect(item("ask Bash(git push:*)")).toMatchObject({ kind: "permission", strength: "refuse", by: "settings" });
60
+ });
61
+ it("[N31] [GC1] the summary counts steps that refuse: k of m (v10's enforcement number)", () => {
62
+ const r = checkGuards(ast, root);
63
+ expect(r.enforcement).toEqual({ refuse: 3, of: 3 });
64
+ expect(r.missing).toBe(0);
65
+ });
66
+ });
67
+ describe("guard --check reports what has gone missing", () => {
68
+ it("[N31] [GC2] the SubagentStop entry removed from settings.json: the gate enforces nothing", () => {
69
+ const s = settings();
70
+ s.hooks.SubagentStop = s.hooks.SubagentStop.filter((e) => e.matcher !== "drafter");
71
+ writeSettings(s);
72
+ const g = item("copy-check");
73
+ expect(g.strength).toBe("none");
74
+ expect(g.notes.join(" ")).toContain("settings.json");
75
+ expect(checkGuards(ast, root).enforcement).toEqual({ refuse: 2, of: 3 });
76
+ });
77
+ it("[N31] [GC2] a hook entry whose command is relative fails open off the project root", () => {
78
+ const s = settings();
79
+ for (const e of s.hooks.SubagentStop)
80
+ for (const h of e.hooks)
81
+ if (h.command)
82
+ h.command = h.command.replace(/"\$CLAUDE_PROJECT_DIR\/(.*)"/, "$1");
83
+ writeSettings(s);
84
+ const g = item("copy-check");
85
+ expect(g.strength).toBe("none");
86
+ expect(g.notes.join(" ")).toMatch(/relative/);
87
+ });
88
+ it("[N31] [GC2] the before-agent guard without onFailure: block is reported as failing open if the hook can't start", () => {
89
+ const s = settings();
90
+ for (const e of s.hooks.PreToolUse)
91
+ for (const h of e.hooks)
92
+ delete h.onFailure;
93
+ writeSettings(s);
94
+ expect(item("brief-check").strength).toBe("refuse");
95
+ expect(item("brief-check").notes.join(" ")).toMatch(/onFailure/);
96
+ });
97
+ it("[N31] [GC2] the gate wrapper script deleted: none", () => {
98
+ fs.rmSync(at(`${SCRIPTS}/gate-copy-check.sh`));
99
+ expect(item("copy-check").strength).toBe("none");
100
+ });
101
+ it("[N31] [GC2] before-agent.sh edited so it no longer runs the gate: none", () => {
102
+ const p = at(`${SCRIPTS}/before-agent.sh`);
103
+ fs.writeFileSync(p, fs.readFileSync(p, "utf8").replace(/.*gate-brief-check\.sh.*\n/, ""));
104
+ expect(item("brief-check").strength).toBe("none");
105
+ });
106
+ it("[N31] [GC2] the user's check script missing: the gate still refuses, and every run is blocked", () => {
107
+ fs.rmSync(at("docs/tools/check.py"));
108
+ const g = item("copy-check");
109
+ expect(g.strength).toBe("refuse");
110
+ expect(g.notes.join(" ")).toMatch(/missing.*blocks every run/);
111
+ });
112
+ it("[N31] [GC2] a deny rule removed from settings.json: none", () => {
113
+ const s = settings();
114
+ s.permissions.deny = [];
115
+ writeSettings(s);
116
+ expect(item("deny mcp__notes__delete").strength).toBe("none");
117
+ });
118
+ it("[N31] [GC2] a hand-built hook it cannot tie to a step is listed, not silently counted", () => {
119
+ expect(checkGuards(ast, root).unattributed).toEqual([]);
120
+ const s = settings();
121
+ s.hooks.PreToolUse.push({ matcher: "Bash", hooks: [{ type: "command", command: 'python3 "$CLAUDE_PROJECT_DIR/harness/lock/hook.py"' }] });
122
+ writeSettings(s);
123
+ const r = checkGuards(ast, root);
124
+ expect(r.unattributed).toEqual(['PreToolUse Bash: python3 "$CLAUDE_PROJECT_DIR/harness/lock/hook.py"']);
125
+ expect(r.enforcement).toEqual({ refuse: 3, of: 3 });
126
+ });
127
+ it("[N31] [GC2] no .claude/settings.json at all: every hook and rule is none", () => {
128
+ fs.rmSync(at(".claude/settings.json"));
129
+ const r = checkGuards(ast, root);
130
+ expect(r.enforcement.refuse).toBe(0);
131
+ expect(r.items.filter((i) => i.strength === "none").length).toBe(r.items.length);
132
+ });
133
+ });
134
+ describe("guard --check from the CLI", () => {
135
+ const cli = (...args) => spawnSync(process.execPath, [CLI, ...args], { cwd: root, encoding: "utf8" });
136
+ it("[N31] [GC3] prints one line per item and the enforcement count, writes nothing, exits 0 when nothing is missing", () => {
137
+ const before = spawnSync("find", [root, "-type", "f"], { encoding: "utf8" }).stdout;
138
+ const r = cli("guard", FIXTURE, "--check", "--dir", root);
139
+ expect(r.status).toBe(0);
140
+ expect(r.stdout).toMatch(/gate copy-check\s+refuse\s+by: hook/);
141
+ expect(r.stdout).toMatch(/enforcement: 3 of 3 steps refuse/);
142
+ expect(r.stdout).toMatch(/Claude Code \d+\.\d+\.\d+/);
143
+ expect(spawnSync("find", [root, "-type", "f"], { encoding: "utf8" }).stdout).toBe(before);
144
+ });
145
+ it("[N31] [GC3] exits 1 when something the binding generates is missing on disk", () => {
146
+ fs.rmSync(at(`${SCRIPTS}/gate-copy-check.sh`));
147
+ const r = cli("guard", FIXTURE, "--check", "--dir", root);
148
+ expect(r.status).toBe(1);
149
+ expect(r.stdout).toMatch(/gate copy-check\s+none/);
150
+ });
151
+ it("[N31] [GC3] guard without --check says only the read-only mode exists yet", () => {
152
+ const r = cli("guard", FIXTURE, "--dir", root);
153
+ expect(r.status).toBe(1);
154
+ expect(r.stderr).toMatch(/--check/);
155
+ });
156
+ });
157
+ /**
158
+ * Citations (N33). A hand-built guard can be credited when the file cites it:
159
+ * a gate with `by: code` and `at: "path:line"` is checked on disk (the file,
160
+ * the line, and the `match:` text within 5 lines), and a gate whose edge says
161
+ * `[guard: lock-bash]` is credited with that hook when the hook is in
162
+ * settings.json and declares its strength. A broken citation is `none` and
163
+ * exits 1; a strength the file writes down as `none` is reported, not failed.
164
+ */
165
+ // release and voice carry no commands: the binding compiles no hook for a gate
166
+ // before them, so only the cited guard can credit it. (With commands, such a
167
+ // gate is guarded by before-action.sh: see action-guard.test.ts.)
168
+ const CITED = `topology cited : [pipeline] {
169
+ meta {
170
+ version: "1.0.0"
171
+ description: "Citation probe"
172
+ }
173
+
174
+ orchestrator {
175
+ model: sonnet
176
+ handles: [release, voice]
177
+ }
178
+
179
+ agent writer {
180
+ model: sonnet
181
+ description: "Writes the post"
182
+ prompt {
183
+ Write the post.
184
+ }
185
+ }
186
+
187
+ human owner-yes {
188
+ description: "The owner approves the post"
189
+ }
190
+
191
+ action release {
192
+ kind: external
193
+ description: "Posts it"
194
+ }
195
+
196
+ action voice {
197
+ kind: external
198
+ description: "Records the voice-over"
199
+ }
200
+
201
+ hooks {
202
+ hook lock-bash {
203
+ on: PreToolUse
204
+ matcher: "Bash"
205
+ run: "python3 harness/lock/hook.py"
206
+ strength: refuse
207
+ bypass: "AT_UNLOCK"
208
+ }
209
+ hook note-bash {
210
+ on: PostToolUse
211
+ matcher: "Bash"
212
+ run: "python3 harness/lock/note.py"
213
+ }
214
+ }
215
+
216
+ gates {
217
+ gate lock-release {
218
+ after: owner-yes
219
+ before: release
220
+ run: "scripts/lock.sh"
221
+ on-fail: halt
222
+ }
223
+ gate lock-voice {
224
+ after: owner-yes
225
+ before: voice
226
+ run: "scripts/lock-voice.sh"
227
+ on-fail: halt
228
+ }
229
+ gate doctrine {
230
+ after: owner-yes
231
+ before: voice
232
+ strength: refuse
233
+ by: code
234
+ at: "tools/px.py:5"
235
+ match: "PX_ALLOW_NO_DOCTRINE"
236
+ bypass: "PX_ALLOW_NO_DOCTRINE"
237
+ }
238
+ }
239
+
240
+ flow {
241
+ writer -> owner-yes
242
+ owner-yes -> release [guard: lock-bash]
243
+ owner-yes -> voice [guard: note-bash]
244
+ writer -> voice [strength: none]
245
+ }
246
+ }
247
+ `;
248
+ describe("guard --check credits what the file cites", () => {
249
+ const cast = parse(CITED);
250
+ let proj;
251
+ const put = (p, text) => { fs.mkdirSync(dirname(join(proj, p)), { recursive: true }); fs.writeFileSync(join(proj, p), text); };
252
+ const lines = (n, at, text) => Array.from({ length: n }, (_, i) => (i + 1 === at ? text : `line ${i + 1}`)).join("\n") + "\n";
253
+ const handBuilt = (hooks) => put(".claude/settings.json", JSON.stringify({ hooks }, null, 2));
254
+ const LOCK = { PreToolUse: [{ matcher: "Bash", hooks: [{ type: "command", command: '/usr/bin/python3 "$CLAUDE_PROJECT_DIR/harness/lock/hook.py"' }] }] };
255
+ const find = (id) => checkGuards(cast, proj).items.find((i) => i.id === id);
256
+ beforeEach(() => {
257
+ proj = fs.mkdtempSync(join(os.tmpdir(), "at-guard-cite-"));
258
+ put("tools/px.py", lines(20, 7, 'if os.environ.get("PX_ALLOW_NO_DOCTRINE"): return'));
259
+ handBuilt(LOCK);
260
+ });
261
+ afterEach(() => fs.rmSync(proj, { recursive: true, force: true }));
262
+ it("[N33] the file parses and validates clean", () => {
263
+ expect(validate(cast).filter((r) => r.level === "error")).toEqual([]);
264
+ });
265
+ it("[N33] a by: code citation whose match text is within 5 lines: refuse by code, cited not proven, bypass named", () => {
266
+ const g = find("doctrine");
267
+ expect(g).toMatchObject({ kind: "gate", strength: "refuse", by: "code" });
268
+ expect(g.where).toContain("tools/px.py:5");
269
+ expect(g.notes.join(" ")).toMatch(/cited, not proven/);
270
+ expect(g.notes.join(" ")).toMatch(/bypass.*PX_ALLOW_NO_DOCTRINE/);
271
+ });
272
+ it("[N33] the match text moved away from the cited line: none, and exit-worthy", () => {
273
+ put("tools/px.py", lines(20, 15, 'if os.environ.get("PX_ALLOW_NO_DOCTRINE"): return'));
274
+ const g = find("doctrine");
275
+ expect(g.strength).toBe("none");
276
+ expect(g.notes.join(" ")).toMatch(/PX_ALLOW_NO_DOCTRINE.*not within 5 lines of line 5/);
277
+ expect(checkGuards(cast, proj).missing).toBeGreaterThan(0);
278
+ });
279
+ it("[N33] the cited file missing, or the line past its end: none", () => {
280
+ fs.rmSync(join(proj, "tools/px.py"));
281
+ expect(find("doctrine").notes.join(" ")).toMatch(/tools\/px\.py is missing/);
282
+ put("tools/px.py", lines(3, 2, "PX_ALLOW_NO_DOCTRINE"));
283
+ expect(find("doctrine").strength).toBe("none");
284
+ expect(find("doctrine").notes.join(" ")).toMatch(/has 3 lines/);
285
+ });
286
+ it("[N33] a gate with no hook of its own is credited by the guard its edge cites, when that hook is in settings.json", () => {
287
+ const g = find("lock-release");
288
+ expect(g).toMatchObject({ strength: "refuse", by: "hook" });
289
+ expect(g.where).toContain("lock-bash");
290
+ expect(g.notes.join(" ")).toMatch(/bypass.*AT_UNLOCK/);
291
+ expect(checkGuards(cast, proj).unattributed).toEqual([]);
292
+ });
293
+ it("[N33] the cited hook missing from settings.json: the gate stays hope, and says why", () => {
294
+ handBuilt({});
295
+ const g = find("lock-release");
296
+ expect(g.strength).toBe("hope");
297
+ expect(g.notes.join(" ")).toMatch(/lock-bash.*not in .claude\/settings.json/);
298
+ });
299
+ it("[N33] a cited hook that declares no strength is not credited", () => {
300
+ handBuilt({ ...LOCK, PostToolUse: [{ matcher: "Bash", hooks: [{ type: "command", command: 'python3 "$CLAUDE_PROJECT_DIR/harness/lock/note.py"' }] }] });
301
+ const g = find("lock-voice");
302
+ expect(g.strength).toBe("hope");
303
+ expect(g.notes.join(" ")).toMatch(/note-bash declares no strength/);
304
+ });
305
+ it("[N33] an edge whose strength is written down as none is reported as declared, and is not a failure", () => {
306
+ const e = find("writer -> voice");
307
+ expect(e).toMatchObject({ kind: "edge", strength: "none", by: "prose" });
308
+ expect(e.notes.join(" ")).toMatch(/declared/);
309
+ put("tools/px.py", lines(20, 7, 'if os.environ.get("PX_ALLOW_NO_DOCTRINE"): return'));
310
+ expect(checkGuards(cast, proj).missing).toBe(0);
311
+ });
312
+ it("[N33] the count reads the credit: 2 of 4 steps refuse (doctrine by code, lock-release by its cited hook)", () => {
313
+ expect(checkGuards(cast, proj).enforcement).toEqual({ refuse: 2, of: 4 });
314
+ });
315
+ });
316
+ //# sourceMappingURL=guard-check.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-check.test.js","sourceRoot":"","sources":["../../../src/__tests__/e2e/guard-check.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * [N41] `guard <file> --target claude-code` writes only the guard part of what
3
+ * scaffold would write, into a project that was built by hand: the hook
4
+ * scripts settings.json runs, the gate wrappers and check scripts they call,
5
+ * and the `hooks` and `permissions` keys of settings.json, merged. It never
6
+ * writes an agent, skill, command, rule, .mcp.json or CLAUDE.md, never
7
+ * overwrites a script the user edited or a check that is no longer a stub,
8
+ * and a rerun changes nothing.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=guard-target.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-target.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/e2e/guard-target.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,198 @@
1
+ /**
2
+ * [N41] `guard <file> --target claude-code` writes only the guard part of what
3
+ * scaffold would write, into a project that was built by hand: the hook
4
+ * scripts settings.json runs, the gate wrappers and check scripts they call,
5
+ * and the `hooks` and `permissions` keys of settings.json, merged. It never
6
+ * writes an agent, skill, command, rule, .mcp.json or CLAUDE.md, never
7
+ * overwrites a script the user edited or a check that is no longer a stub,
8
+ * and a rerun changes nothing.
9
+ */
10
+ import { describe, it, expect, beforeEach } from "vitest";
11
+ import { spawnSync } from "node:child_process";
12
+ import * as fs from "node:fs";
13
+ import * as os from "node:os";
14
+ import { createHash } from "node:crypto";
15
+ import { resolve, dirname, join, relative } from "node:path";
16
+ import { fileURLToPath } from "node:url";
17
+ import { parse } from "../../parser/index.js";
18
+ import { claudeCodeBinding } from "../../bindings/claude-code.js";
19
+ import { guardFiles, planGuardTarget } from "../../guard/target.js";
20
+ import { checkGuards } from "../../guard/check.js";
21
+ const here = dirname(fileURLToPath(import.meta.url));
22
+ const FIXTURE = resolve(here, "fixtures/compile.at");
23
+ const CLI = resolve(here, "../../../dist/cli/index.js");
24
+ const ast = parse(fs.readFileSync(FIXTURE, "utf-8"));
25
+ const SCRIPTS = ".claude/skills/compile/scripts";
26
+ const AGENT = ".claude/agents/drafter/AGENT.md";
27
+ const AGENT_BYTES = "---\nname: drafter\ndescription: by hand\n---\nWrite the copy. Hand-written, do not touch.\n";
28
+ const USER_CHECK = "#!/usr/bin/env python3\nprint('my own check')\n";
29
+ const USER_BRIEF = "#!/usr/bin/env bash\n# mine, implemented\nexit 0\n";
30
+ let root;
31
+ const at = (p) => join(root, p);
32
+ const settings = () => JSON.parse(fs.readFileSync(at(".claude/settings.json"), "utf8"));
33
+ const cli = (...a) => spawnSync("node", [CLI, "guard", FIXTURE, "--target", "claude-code", "--dir", root, ...a], { encoding: "utf8" });
34
+ function snapshot() {
35
+ const out = {};
36
+ const walk = (d) => {
37
+ for (const e of fs.readdirSync(d, { withFileTypes: true })) {
38
+ const p = join(d, e.name);
39
+ if (e.isDirectory()) {
40
+ out[relative(root, p) + "/"] = "";
41
+ walk(p);
42
+ }
43
+ else
44
+ out[relative(root, p)] = createHash("sha256").update(fs.readFileSync(p)).digest("hex");
45
+ }
46
+ };
47
+ walk(root);
48
+ return out;
49
+ }
50
+ beforeEach(() => {
51
+ root = fs.mkdtempSync(join(os.tmpdir(), "at-guard-target-"));
52
+ fs.mkdirSync(at(".claude/agents/drafter"), { recursive: true });
53
+ fs.writeFileSync(at(AGENT), AGENT_BYTES);
54
+ fs.writeFileSync(at(".claude/settings.json"), JSON.stringify({
55
+ hooks: { PreToolUse: [{ matcher: "Bash", hooks: [{ type: "command", command: "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/mine.sh\"" }] }] },
56
+ permissions: { allow: ["Bash(ls:*)"] },
57
+ model: "sonnet",
58
+ }, null, 2));
59
+ fs.mkdirSync(at("docs/tools"), { recursive: true });
60
+ fs.writeFileSync(at("docs/tools/check.py"), USER_CHECK);
61
+ fs.mkdirSync(at(SCRIPTS), { recursive: true });
62
+ fs.writeFileSync(at(`${SCRIPTS}/brief-check.sh`), USER_BRIEF);
63
+ });
64
+ describe("[N41] guard --target", () => {
65
+ it("[N41] keeps the hook scripts, the gates they call and the checks the gates run, and nothing else", () => {
66
+ const paths = guardFiles(claudeCodeBinding.scaffold(ast)).map((f) => f.path).sort();
67
+ expect(paths).toContain(`${SCRIPTS}/before-agent.sh`);
68
+ expect(paths).toContain(`${SCRIPTS}/gate-brief-check.sh`);
69
+ expect(paths).toContain(`${SCRIPTS}/gate-copy-check.sh`);
70
+ expect(paths).toContain(`${SCRIPTS}/brief-check.sh`);
71
+ expect(paths).toContain("docs/tools/check.py");
72
+ expect(paths).toContain(".claude/settings.json");
73
+ for (const p of paths)
74
+ expect(p).not.toMatch(/AGENT\.md|SKILL\.md|\.mcp\.json|CLAUDE\.md|rules\/|commands\/|crontab|schedule-|action-/);
75
+ });
76
+ it("[N41] the function plans the same as the command and writes nothing", () => {
77
+ const before = snapshot();
78
+ const plan = planGuardTarget(ast, root, "compile.at");
79
+ expect(snapshot()).toEqual(before);
80
+ expect(plan.actions.some((a) => a.path === `${SCRIPTS}/before-agent.sh` && a.type === "create")).toBe(true);
81
+ });
82
+ it("[N41] writes our guard, leaves the agent and the user's hook and permission", () => {
83
+ const r = cli();
84
+ expect(r.status).toBe(0);
85
+ expect(fs.readFileSync(at(AGENT), "utf8")).toBe(AGENT_BYTES);
86
+ const s = settings();
87
+ expect(JSON.stringify(s.hooks.PreToolUse)).toContain(".claude/hooks/mine.sh");
88
+ expect(s.permissions.allow).toContain("Bash(ls:*)");
89
+ expect(s.permissions.deny).toContain("mcp__notes__delete");
90
+ expect(JSON.stringify(s.hooks.PreToolUse)).toContain("before-agent.sh");
91
+ expect(JSON.stringify(s.hooks.SubagentStop)).toContain("gate-copy-check.sh");
92
+ expect(s.model).toBe("sonnet");
93
+ expect(fs.existsSync(at(`${SCRIPTS}/before-agent.sh`))).toBe(true);
94
+ expect(fs.existsSync(at(`${SCRIPTS}/gate-brief-check.sh`))).toBe(true);
95
+ for (const p of [".mcp.json", "CLAUDE.md", ".claude/skills/compile/SKILL.md", ".claude/rules/compile.md", ".claude/agents/auditor/AGENT.md", "crontab.fragment"]) {
96
+ expect(fs.existsSync(at(p)), p).toBe(false);
97
+ }
98
+ expect(s.hooks.Stop).toBeUndefined(); // a prompt hook is not a guard script
99
+ });
100
+ it("[N41] a second run changes nothing and adds no duplicates", () => {
101
+ cli();
102
+ const first = snapshot();
103
+ const hooks = JSON.stringify(settings().hooks);
104
+ const r = cli();
105
+ expect(r.status).toBe(0);
106
+ expect(snapshot()).toEqual(first);
107
+ expect(JSON.stringify(settings().hooks)).toBe(hooks);
108
+ const perms = settings().permissions.allow;
109
+ expect(new Set(perms).size).toBe(perms.length);
110
+ });
111
+ it("[N41] --dry-run prints the plan and writes nothing", () => {
112
+ const before = snapshot();
113
+ const r = cli("--dry-run");
114
+ expect(r.status).toBe(0);
115
+ expect(r.stdout).toContain("before-agent.sh");
116
+ expect(snapshot()).toEqual(before);
117
+ });
118
+ it("[N41] a hand-written check, and a check no longer a stub, are untouched and reported", () => {
119
+ const r = cli();
120
+ expect(fs.readFileSync(at("docs/tools/check.py"), "utf8")).toBe(USER_CHECK);
121
+ expect(fs.readFileSync(at(`${SCRIPTS}/brief-check.sh`), "utf8")).toBe(USER_BRIEF);
122
+ expect(r.stdout).toContain("docs/tools/check.py");
123
+ expect(r.stdout).toMatch(/brief-check\.sh.*(skipped|kept)/);
124
+ });
125
+ it("[N41] a script the user edited after a guard run is kept on the next one", () => {
126
+ cli();
127
+ const p = at(`${SCRIPTS}/gate-brief-check.sh`);
128
+ fs.appendFileSync(p, "# my edit\n");
129
+ const edited = fs.readFileSync(p, "utf8");
130
+ const r = cli();
131
+ expect(fs.readFileSync(p, "utf8")).toBe(edited);
132
+ expect(r.stdout).toMatch(/gate-brief-check\.sh.*(edited|kept)/);
133
+ });
134
+ it("[N41] then runs guard --check: the gates refuse and the exit is 0", () => {
135
+ const r = cli();
136
+ expect(r.status).toBe(0);
137
+ expect(r.stdout).toMatch(/brief-check[^\n]*refuse/);
138
+ expect(r.stdout).toMatch(/copy-check[^\n]*refuse/);
139
+ const report = checkGuards(ast, root);
140
+ expect(report.items.filter((i) => i.kind === "gate").every((i) => i.strength === "refuse")).toBe(true);
141
+ expect(report.missing).toBe(0);
142
+ });
143
+ it("[N41] on a bare directory it exits 1 until the stubs are written, like --check", () => {
144
+ const bare = fs.mkdtempSync(join(os.tmpdir(), "at-guard-target-bare-"));
145
+ const r = spawnSync("node", [CLI, "guard", FIXTURE, "--target", "claude-code", "--dir", bare], { encoding: "utf8" });
146
+ expect(fs.existsSync(join(bare, SCRIPTS, "before-agent.sh"))).toBe(true);
147
+ expect(r.stdout).toContain("enforcement:");
148
+ });
149
+ it("[N41] picks up the before-action.sh of a gate before a script step and guard --check reports it as refuse", () => {
150
+ const src = `topology ship : [pipeline] {
151
+ meta {
152
+ version: "1.0.0"
153
+ description: "Render then publish"
154
+ }
155
+ orchestrator {
156
+ model: sonnet
157
+ handles: [render, publish]
158
+ }
159
+ action render {
160
+ kind: external
161
+ commands: ["./tool render"]
162
+ }
163
+ action publish {
164
+ kind: external
165
+ commands: ["./tool publish"]
166
+ unit-from: "./tool publish (\\S+)"
167
+ }
168
+ records {
169
+ record trace { path: "logs/{unit}/trace.jsonl" }
170
+ }
171
+ gates {
172
+ gate publish-ready {
173
+ after: render
174
+ before: publish
175
+ ledger: trace
176
+ on-fail: halt
177
+ }
178
+ }
179
+ flow { render -> publish }
180
+ }
181
+ `;
182
+ const dir = fs.mkdtempSync(join(os.tmpdir(), "at-guard-target-action-"));
183
+ const file = join(dir, "ship.at");
184
+ fs.writeFileSync(file, src);
185
+ const r = spawnSync("node", [CLI, "guard", file, "--target", "claude-code", "--dir", dir], { encoding: "utf8" });
186
+ const script = join(dir, ".claude/skills/ship/scripts/before-action.sh");
187
+ expect(fs.existsSync(script), r.stdout + r.stderr).toBe(true);
188
+ const cfg = fs.readFileSync(join(dir, ".claude/settings.json"), "utf8");
189
+ expect(cfg).toContain("before-action.sh");
190
+ expect(r.status, r.stdout + r.stderr).toBe(0);
191
+ expect(r.stdout).toMatch(/publish-ready[^\n]*refuse/);
192
+ const report = checkGuards(parse(src), dir);
193
+ const gate = report.items.find((i) => i.kind === "gate");
194
+ expect(gate?.strength).toBe("refuse");
195
+ expect(gate?.by).toBe("hook");
196
+ });
197
+ });
198
+ //# sourceMappingURL=guard-target.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-target.test.js","sourceRoot":"","sources":["../../../src/__tests__/e2e/guard-target.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
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
+ export {};
18
+ //# sourceMappingURL=inventory.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inventory.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/e2e/inventory.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}