agentopology 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -5
- package/dist/analyzer/__tests__/analyzer.test.js +36 -9
- package/dist/analyzer/__tests__/analyzer.test.js.map +1 -1
- package/dist/analyzer/__tests__/observability-gap.test.js +40 -10
- package/dist/analyzer/__tests__/observability-gap.test.js.map +1 -1
- package/dist/analyzer/index.d.ts +2 -1
- package/dist/analyzer/index.d.ts.map +1 -1
- package/dist/analyzer/index.js +1 -1
- package/dist/analyzer/index.js.map +1 -1
- package/dist/bindings/__tests__/bindings.test.js +20 -5
- package/dist/bindings/__tests__/bindings.test.js.map +1 -1
- package/dist/bindings/__tests__/claude-workflow.test.js +12 -3
- package/dist/bindings/__tests__/claude-workflow.test.js.map +1 -1
- package/dist/bindings/__tests__/defaults-agreement.test.d.ts +16 -0
- package/dist/bindings/__tests__/defaults-agreement.test.d.ts.map +1 -0
- package/dist/bindings/__tests__/defaults-agreement.test.js +80 -0
- package/dist/bindings/__tests__/defaults-agreement.test.js.map +1 -0
- package/dist/bindings/__tests__/observability.test.js +8 -2
- package/dist/bindings/__tests__/observability.test.js.map +1 -1
- package/dist/bindings/__tests__/stub-generation.test.js +8 -2
- package/dist/bindings/__tests__/stub-generation.test.js.map +1 -1
- package/dist/bindings/claude-code.d.ts.map +1 -1
- package/dist/bindings/claude-code.js +2 -1
- package/dist/bindings/claude-code.js.map +1 -1
- package/dist/bindings/claude-workflow.d.ts.map +1 -1
- package/dist/bindings/claude-workflow.js +2 -1
- package/dist/bindings/claude-workflow.js.map +1 -1
- package/dist/bindings/codex.d.ts.map +1 -1
- package/dist/bindings/codex.js +6 -2
- package/dist/bindings/codex.js.map +1 -1
- package/dist/bindings/gemini-cli.d.ts.map +1 -1
- package/dist/bindings/gemini-cli.js +2 -1
- package/dist/bindings/gemini-cli.js.map +1 -1
- package/dist/bindings/kiro.d.ts.map +1 -1
- package/dist/bindings/kiro.js +2 -1
- package/dist/bindings/kiro.js.map +1 -1
- package/dist/bindings/openclaw.d.ts.map +1 -1
- package/dist/bindings/openclaw.js +2 -1
- package/dist/bindings/openclaw.js.map +1 -1
- package/dist/cli/__tests__/cli.test.d.ts +12 -0
- package/dist/cli/__tests__/cli.test.d.ts.map +1 -0
- package/dist/cli/__tests__/cli.test.js +171 -0
- package/dist/cli/__tests__/cli.test.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +149 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/docs/content.d.ts.map +1 -1
- package/dist/docs/content.js +112 -0
- package/dist/docs/content.js.map +1 -1
- package/dist/exporters/ascii.d.ts +13 -0
- package/dist/exporters/ascii.d.ts.map +1 -0
- package/dist/exporters/ascii.js +26 -0
- package/dist/exporters/ascii.js.map +1 -0
- package/dist/exporters/brief.d.ts +14 -0
- package/dist/exporters/brief.d.ts.map +1 -0
- package/dist/exporters/brief.js +37 -0
- package/dist/exporters/brief.js.map +1 -0
- package/dist/exporters/index.d.ts +2 -0
- package/dist/exporters/index.d.ts.map +1 -1
- package/dist/exporters/index.js +6 -0
- package/dist/exporters/index.js.map +1 -1
- package/dist/exporters/markdown.d.ts.map +1 -1
- package/dist/exporters/markdown.js +2 -1
- package/dist/exporters/markdown.js.map +1 -1
- package/dist/exporters/mermaid.d.ts.map +1 -1
- package/dist/exporters/mermaid.js +2 -1
- package/dist/exporters/mermaid.js.map +1 -1
- package/dist/import/__tests__/import.test.js +8 -2
- package/dist/import/__tests__/import.test.js.map +1 -1
- package/dist/parser/__tests__/parser.test.js +1386 -210
- package/dist/parser/__tests__/parser.test.js.map +1 -1
- package/dist/parser/__tests__/spec-sync.test.d.ts +10 -0
- package/dist/parser/__tests__/spec-sync.test.d.ts.map +1 -0
- package/dist/parser/__tests__/spec-sync.test.js +60 -0
- package/dist/parser/__tests__/spec-sync.test.js.map +1 -0
- package/dist/parser/index.d.ts +50 -6
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +183 -28
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/validator.d.ts +7 -1
- package/dist/parser/validator.d.ts.map +1 -1
- package/dist/parser/validator.js +77 -2
- package/dist/parser/validator.js.map +1 -1
- package/dist/plan/__tests__/brief.test.d.ts +2 -0
- package/dist/plan/__tests__/brief.test.d.ts.map +1 -0
- package/dist/plan/__tests__/brief.test.js +442 -0
- package/dist/plan/__tests__/brief.test.js.map +1 -0
- package/dist/plan/brief.d.ts +207 -0
- package/dist/plan/brief.d.ts.map +1 -0
- package/dist/plan/brief.js +342 -0
- package/dist/plan/brief.js.map +1 -0
- package/dist/plan/render.d.ts +20 -0
- package/dist/plan/render.d.ts.map +1 -0
- package/dist/plan/render.js +425 -0
- package/dist/plan/render.js.map +1 -0
- package/dist/render/__tests__/ascii.test.d.ts +2 -0
- package/dist/render/__tests__/ascii.test.d.ts.map +1 -0
- package/dist/render/__tests__/ascii.test.js +187 -0
- package/dist/render/__tests__/ascii.test.js.map +1 -0
- package/dist/render/ascii.d.ts +23 -0
- package/dist/render/ascii.d.ts.map +1 -0
- package/dist/render/ascii.js +187 -0
- package/dist/render/ascii.js.map +1 -0
- package/dist/resolve/__tests__/defaults.test.d.ts +2 -0
- package/dist/resolve/__tests__/defaults.test.d.ts.map +1 -0
- package/dist/resolve/__tests__/defaults.test.js +109 -0
- package/dist/resolve/__tests__/defaults.test.js.map +1 -0
- package/dist/resolve/__tests__/order.test.d.ts +2 -0
- package/dist/resolve/__tests__/order.test.d.ts.map +1 -0
- package/dist/resolve/__tests__/order.test.js +238 -0
- package/dist/resolve/__tests__/order.test.js.map +1 -0
- package/dist/resolve/__tests__/phase.test.d.ts +12 -0
- package/dist/resolve/__tests__/phase.test.d.ts.map +1 -0
- package/dist/resolve/__tests__/phase.test.js +81 -0
- package/dist/resolve/__tests__/phase.test.js.map +1 -0
- package/dist/resolve/defaults.d.ts +51 -0
- package/dist/resolve/defaults.d.ts.map +1 -0
- package/dist/resolve/defaults.js +109 -0
- package/dist/resolve/defaults.js.map +1 -0
- package/dist/resolve/order.d.ts +73 -0
- package/dist/resolve/order.d.ts.map +1 -0
- package/dist/resolve/order.js +208 -0
- package/dist/resolve/order.js.map +1 -0
- package/dist/resolve/phase.d.ts +57 -0
- package/dist/resolve/phase.d.ts.map +1 -0
- package/dist/resolve/phase.js +60 -0
- package/dist/resolve/phase.js.map +1 -0
- package/dist/scaffold/__tests__/scaffold.test.js +8 -2
- package/dist/scaffold/__tests__/scaffold.test.js.map +1 -1
- package/dist/scaffold/__tests__/settings-merge.test.js +95 -0
- package/dist/scaffold/__tests__/settings-merge.test.js.map +1 -1
- package/dist/scaffold/incremental.d.ts.map +1 -1
- package/dist/scaffold/incremental.js +13 -2
- package/dist/scaffold/incremental.js.map +1 -1
- package/dist/visualizer/__tests__/topology-brain-link.test.js +19 -4
- package/dist/visualizer/__tests__/topology-brain-link.test.js.map +1 -1
- package/examples/code-review.at +32 -4
- package/examples/data-processing.at +33 -4
- package/package.json +1 -1
- package/spec/grammar.md +25 -1
- package/spec/validation.md +103 -35
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve, dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { parse } from "../../parser/index.js";
|
|
6
|
+
import { validate } from "../../parser/validator.js";
|
|
7
|
+
import { buildExecutionBrief } from "../brief.js";
|
|
8
|
+
import { renderBriefMarkdown } from "../render.js";
|
|
9
|
+
const example = (name) => readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), "../../../examples", name), "utf8");
|
|
10
|
+
const briefFor = (name, opts = {}) => buildExecutionBrief(parse(example(name)), { source: `examples/${name}`, ...opts });
|
|
11
|
+
describe("buildExecutionBrief", () => {
|
|
12
|
+
it("computes handoffs as writes ∩ reads, one row per pair", () => {
|
|
13
|
+
const b = briefFor("code-review.at");
|
|
14
|
+
const h = b.handoffs.find((x) => x.from === "analyzer" && x.to === "reviewer");
|
|
15
|
+
expect(h.passes).toEqual(["workspace/analysis.md"]);
|
|
16
|
+
// Two conditional edges between the same roles are ONE handoff. The
|
|
17
|
+
// condition is control flow, not information flow.
|
|
18
|
+
const pairs = b.handoffs.map((x) => `${x.from}->${x.to}`);
|
|
19
|
+
expect(new Set(pairs).size).toBe(pairs.length);
|
|
20
|
+
});
|
|
21
|
+
it("derives withholds from the same intersection, so they cannot drift", () => {
|
|
22
|
+
const src = `topology t : [pipeline] {
|
|
23
|
+
meta {
|
|
24
|
+
version: "1.0.0"
|
|
25
|
+
description: "x"
|
|
26
|
+
}
|
|
27
|
+
agent builder {
|
|
28
|
+
model: sonnet
|
|
29
|
+
description: "b"
|
|
30
|
+
writes: ["workspace/notes.md", "workspace/self-review.md"]
|
|
31
|
+
}
|
|
32
|
+
agent validator {
|
|
33
|
+
model: opus
|
|
34
|
+
description: "v"
|
|
35
|
+
reads: ["workspace/notes.md"]
|
|
36
|
+
}
|
|
37
|
+
action intake {
|
|
38
|
+
kind: inline
|
|
39
|
+
description: "in"
|
|
40
|
+
}
|
|
41
|
+
flow { intake -> builder -> validator }
|
|
42
|
+
}`;
|
|
43
|
+
const b = buildExecutionBrief(parse(src));
|
|
44
|
+
const h = b.handoffs.find((x) => x.to === "validator");
|
|
45
|
+
expect(h.passes).toEqual(["workspace/notes.md"]);
|
|
46
|
+
expect(h.withholds).toEqual(["workspace/self-review.md"]);
|
|
47
|
+
// The withhold lands on the READER, which is the only role that can honour
|
|
48
|
+
// it. A writer cannot withhold a file it has already written.
|
|
49
|
+
const validator = b.roles.find((r) => r.id === "validator");
|
|
50
|
+
expect(validator.mustNotRead).toEqual(["workspace/self-review.md"]);
|
|
51
|
+
const builder = b.roles.find((r) => r.id === "builder");
|
|
52
|
+
expect(builder.mustNotRead).toEqual([]);
|
|
53
|
+
});
|
|
54
|
+
it("does not tell a writer to withhold a file it is handing to another reader", () => {
|
|
55
|
+
// Regression: `withheld` was unioned across ALL of a writer's readers, so a
|
|
56
|
+
// builder with two consumers taking one file each was told to withhold
|
|
57
|
+
// both — including the ones it was supposed to hand over.
|
|
58
|
+
const src = `topology t : [pipeline] {
|
|
59
|
+
meta {
|
|
60
|
+
version: "1.0.0"
|
|
61
|
+
description: "x"
|
|
62
|
+
}
|
|
63
|
+
agent builder {
|
|
64
|
+
model: sonnet
|
|
65
|
+
description: "b"
|
|
66
|
+
writes: ["workspace/diff.md", "workspace/notes.md"]
|
|
67
|
+
}
|
|
68
|
+
agent reviewer {
|
|
69
|
+
model: sonnet
|
|
70
|
+
description: "r"
|
|
71
|
+
reads: ["workspace/diff.md"]
|
|
72
|
+
}
|
|
73
|
+
agent archivist {
|
|
74
|
+
model: sonnet
|
|
75
|
+
description: "a"
|
|
76
|
+
reads: ["workspace/notes.md"]
|
|
77
|
+
}
|
|
78
|
+
action i {
|
|
79
|
+
kind: inline
|
|
80
|
+
description: "in"
|
|
81
|
+
}
|
|
82
|
+
flow { i -> builder -> reviewer
|
|
83
|
+
builder -> archivist }
|
|
84
|
+
}`;
|
|
85
|
+
const b = buildExecutionBrief(parse(src));
|
|
86
|
+
expect(b.roles.find((r) => r.id === "builder").mustNotRead).toEqual([]);
|
|
87
|
+
expect(b.roles.find((r) => r.id === "reviewer").mustNotRead).toEqual([
|
|
88
|
+
"workspace/notes.md",
|
|
89
|
+
]);
|
|
90
|
+
expect(b.roles.find((r) => r.id === "archivist").mustNotRead).toEqual([
|
|
91
|
+
"workspace/diff.md",
|
|
92
|
+
]);
|
|
93
|
+
});
|
|
94
|
+
it("marks same-step roles with no edge between them as mutually blind", () => {
|
|
95
|
+
const b = briefFor("code-review.at");
|
|
96
|
+
expect(b.blindPairs).toHaveLength(1);
|
|
97
|
+
expect([b.blindPairs[0].a, b.blindPairs[0].b].sort()).toEqual([
|
|
98
|
+
"analyzer",
|
|
99
|
+
"security-scanner",
|
|
100
|
+
]);
|
|
101
|
+
const analyzer = b.roles.find((r) => r.id === "analyzer");
|
|
102
|
+
expect(analyzer.blindTo).toEqual(["security-scanner"]);
|
|
103
|
+
});
|
|
104
|
+
it("lists reads nobody produces as run preconditions, and checks them on disk", () => {
|
|
105
|
+
const b = briefFor("code-review.at");
|
|
106
|
+
const paths = b.preconditions.map((p) => p.path);
|
|
107
|
+
expect(paths).toContain("workspace/pr-diff.md");
|
|
108
|
+
// Anything a role writes is produced inside the run, so it is not a precondition.
|
|
109
|
+
expect(paths).not.toContain("workspace/analysis.md");
|
|
110
|
+
// Each carries an absolute path and a real existence check — "must exist
|
|
111
|
+
// first" that nobody verifies is a runtime surprise, not a contract.
|
|
112
|
+
for (const p of b.preconditions) {
|
|
113
|
+
expect(p.absolute.startsWith("/"), p.path).toBe(true);
|
|
114
|
+
expect(typeof p.exists).toBe("boolean");
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
it("pre-flags a precondition that does not exist on disk", () => {
|
|
118
|
+
const src = `topology t : [pipeline] {
|
|
119
|
+
meta {
|
|
120
|
+
version: "1.0.0"
|
|
121
|
+
description: "x"
|
|
122
|
+
}
|
|
123
|
+
agent a {
|
|
124
|
+
model: sonnet
|
|
125
|
+
description: "a"
|
|
126
|
+
reads: ["definitely/not/here.md"]
|
|
127
|
+
}
|
|
128
|
+
action intake {
|
|
129
|
+
kind: inline
|
|
130
|
+
description: "in"
|
|
131
|
+
}
|
|
132
|
+
flow { intake -> a }
|
|
133
|
+
}`;
|
|
134
|
+
const b = buildExecutionBrief(parse(src));
|
|
135
|
+
const flag = b.preflagged.find((p) => p.kind === "precondition-missing");
|
|
136
|
+
expect(flag).toBeDefined();
|
|
137
|
+
expect(flag.question).toContain("definitely/not/here.md");
|
|
138
|
+
});
|
|
139
|
+
it("resolves every declared path to an absolute one for the role cards", () => {
|
|
140
|
+
// Dogfooding found three subagents each normalising the same relative path
|
|
141
|
+
// differently, so the declared handoff pointed nowhere.
|
|
142
|
+
const b = briefFor("code-review.at");
|
|
143
|
+
for (const r of b.roles) {
|
|
144
|
+
for (const p of [...r.readsAbs, ...r.writesAbs]) {
|
|
145
|
+
expect(p.startsWith("/"), `${r.id}: ${p}`).toBe(true);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
it("carries the task into the brief so {{TASK}} is never undefined", () => {
|
|
150
|
+
const b = briefFor("code-review.at", { task: "Review PR 42" });
|
|
151
|
+
expect(b.task).toBe("Review PR 42");
|
|
152
|
+
const md = renderBriefMarkdown(b);
|
|
153
|
+
expect(md).toContain("§1b — Run inputs");
|
|
154
|
+
expect(md).toContain("Review PR 42");
|
|
155
|
+
// §0 legitimately mentions the placeholder when explaining it. What must
|
|
156
|
+
// not contain it is the ROLE PROMPTS — those are copied verbatim.
|
|
157
|
+
const cards = md.slice(md.indexOf("# §3 — Role cards"));
|
|
158
|
+
expect(cards).not.toContain("{{TASK}}");
|
|
159
|
+
expect(cards).toContain("Review PR 42");
|
|
160
|
+
});
|
|
161
|
+
it("says what to do about {{TASK}} when no task was given", () => {
|
|
162
|
+
const md = renderBriefMarkdown(briefFor("code-review.at"));
|
|
163
|
+
const cards = md.slice(md.indexOf("# §3 — Role cards"));
|
|
164
|
+
expect(cards).toContain("{{TASK}}");
|
|
165
|
+
expect(md).toContain("Substitute the user's actual request");
|
|
166
|
+
});
|
|
167
|
+
it("marks the `name` spawn parameter optional", () => {
|
|
168
|
+
// Some hosts reject it outright ("Teammates cannot spawn other teammates").
|
|
169
|
+
const md = renderBriefMarkdown(briefFor("code-review.at"));
|
|
170
|
+
expect(md).toContain("drop it if the host rejects it");
|
|
171
|
+
});
|
|
172
|
+
it("names every unenforceable declaration rather than dropping it", () => {
|
|
173
|
+
const b = briefFor("code-review.at");
|
|
174
|
+
const nodes = b.unenforceable.filter((u) => u.field === "tools").map((u) => u.node);
|
|
175
|
+
expect(nodes).toContain("analyzer");
|
|
176
|
+
expect(nodes).toContain("reporter");
|
|
177
|
+
expect(b.roles.find((r) => r.id === "analyzer").fileless).toBe(false);
|
|
178
|
+
});
|
|
179
|
+
it("counts which of the five persistent features are present", () => {
|
|
180
|
+
const b = briefFor("code-review.at");
|
|
181
|
+
const names = b.persistent.map((p) => p.feature);
|
|
182
|
+
expect(names).toContain("triggers / slash commands");
|
|
183
|
+
expect(names).toContain("per-agent tools / permissions");
|
|
184
|
+
expect(names).not.toContain("schedules / cron");
|
|
185
|
+
});
|
|
186
|
+
it("picks the evidence tier for gates, since tiers 1-3 need files", () => {
|
|
187
|
+
const b = briefFor("code-review.at");
|
|
188
|
+
const gate = b.gates[0];
|
|
189
|
+
expect(gate.tier).toBe("evidence-orchestrator");
|
|
190
|
+
expect(gate.blocking).toBe(true);
|
|
191
|
+
});
|
|
192
|
+
it("treats an advisory gate as advisory whatever its on-fail says", () => {
|
|
193
|
+
const b = briefFor("simple-pipeline.at");
|
|
194
|
+
const gate = b.gates[0];
|
|
195
|
+
// simple-pipeline's gate is blocking with bounce-back; assert the mapping.
|
|
196
|
+
expect(["advisory", "evidence-orchestrator", "evidence-agent"]).toContain(gate.tier);
|
|
197
|
+
});
|
|
198
|
+
it("pre-flags a prompt-less role, because its whole instruction set is one line", () => {
|
|
199
|
+
const src = `topology t : [pipeline] {
|
|
200
|
+
meta {
|
|
201
|
+
version: "1.0.0"
|
|
202
|
+
description: "x"
|
|
203
|
+
}
|
|
204
|
+
agent bare {
|
|
205
|
+
model: sonnet
|
|
206
|
+
description: "does a thing"
|
|
207
|
+
}
|
|
208
|
+
action intake {
|
|
209
|
+
kind: inline
|
|
210
|
+
description: "in"
|
|
211
|
+
}
|
|
212
|
+
flow { intake -> bare }
|
|
213
|
+
}`;
|
|
214
|
+
const b = buildExecutionBrief(parse(src));
|
|
215
|
+
const flag = b.preflagged.find((p) => p.kind === "prompt-missing");
|
|
216
|
+
expect(flag.at.node).toBe("bare");
|
|
217
|
+
expect(flag.fix).toContain("prompt { }");
|
|
218
|
+
});
|
|
219
|
+
it("pre-flags an edge across which nothing is declared to cross", () => {
|
|
220
|
+
const src = `topology t : [pipeline] {
|
|
221
|
+
meta {
|
|
222
|
+
version: "1.0.0"
|
|
223
|
+
description: "x"
|
|
224
|
+
}
|
|
225
|
+
agent a {
|
|
226
|
+
model: sonnet
|
|
227
|
+
description: "a"
|
|
228
|
+
writes: ["workspace/a.md"]
|
|
229
|
+
}
|
|
230
|
+
agent b {
|
|
231
|
+
model: sonnet
|
|
232
|
+
description: "b"
|
|
233
|
+
reads: ["workspace/other.md"]
|
|
234
|
+
}
|
|
235
|
+
action intake {
|
|
236
|
+
kind: inline
|
|
237
|
+
description: "in"
|
|
238
|
+
}
|
|
239
|
+
flow { intake -> a -> b }
|
|
240
|
+
}`;
|
|
241
|
+
const brief = buildExecutionBrief(parse(src));
|
|
242
|
+
const flag = brief.preflagged.find((p) => p.kind === "handoff-overlap-empty");
|
|
243
|
+
expect(flag.at.edge).toBe("a->b");
|
|
244
|
+
expect(flag.fix).toContain("reads");
|
|
245
|
+
});
|
|
246
|
+
it("every pre-flagged ambiguity carries a concrete fix — that is the payoff", () => {
|
|
247
|
+
for (const f of ["code-review.at", "simple-pipeline.at", "data-processing.at"]) {
|
|
248
|
+
for (const p of briefFor(f).preflagged) {
|
|
249
|
+
expect(p.fix.length, `${f}/${p.kind}`).toBeGreaterThan(0);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
it("excludes the orchestrator from steps and names it as the host", () => {
|
|
254
|
+
const b = briefFor("simple-pipeline.at");
|
|
255
|
+
expect(b.orchestrator).toBe("orchestrator");
|
|
256
|
+
expect(b.steps.some((s) => s.ids.includes("orchestrator"))).toBe(false);
|
|
257
|
+
});
|
|
258
|
+
const TWO_OUTPUTS = `topology t : [pipeline] {
|
|
259
|
+
meta {
|
|
260
|
+
version: "1.0.0"
|
|
261
|
+
description: "one node routing on two different outputs, plus a default"
|
|
262
|
+
}
|
|
263
|
+
agent judge {
|
|
264
|
+
model: opus
|
|
265
|
+
description: "j"
|
|
266
|
+
outputs: {
|
|
267
|
+
verdict: pass | fail
|
|
268
|
+
severity: minor | major
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
agent sink {
|
|
272
|
+
model: sonnet
|
|
273
|
+
description: "s"
|
|
274
|
+
}
|
|
275
|
+
agent worker {
|
|
276
|
+
model: sonnet
|
|
277
|
+
description: "w"
|
|
278
|
+
}
|
|
279
|
+
agent always {
|
|
280
|
+
model: sonnet
|
|
281
|
+
description: "al"
|
|
282
|
+
}
|
|
283
|
+
action i {
|
|
284
|
+
kind: inline
|
|
285
|
+
description: "in"
|
|
286
|
+
}
|
|
287
|
+
flow { i -> judge
|
|
288
|
+
judge -> sink [when judge.verdict == pass]
|
|
289
|
+
judge -> worker [when judge.severity == major]
|
|
290
|
+
judge -> always }
|
|
291
|
+
}`;
|
|
292
|
+
it("groups routing by (source, output), not by source alone", () => {
|
|
293
|
+
// Regression: grouped by source with the key read off edges[0], so a node
|
|
294
|
+
// routing on two outputs got ONE heading naming whichever came first, and
|
|
295
|
+
// every row under it looked like it tested that output.
|
|
296
|
+
const routes = buildExecutionBrief(parse(TWO_OUTPUTS)).routes;
|
|
297
|
+
expect(routes).toHaveLength(2);
|
|
298
|
+
expect(routes.map((r) => r.key).sort()).toEqual(["severity", "verdict"]);
|
|
299
|
+
for (const r of routes) {
|
|
300
|
+
for (const e of r.edges)
|
|
301
|
+
expect(e.condition).toContain(`.${r.key}`);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
it("keeps the unconditional out-edge as a default instead of dropping it", () => {
|
|
305
|
+
// Regression: it was dropped entirely, and §5 then told the host to halt
|
|
306
|
+
// when nothing matched — a dead end the topology had an answer for.
|
|
307
|
+
const routes = buildExecutionBrief(parse(TWO_OUTPUTS)).routes;
|
|
308
|
+
for (const r of routes) {
|
|
309
|
+
expect(r.fallbacks.map((f) => f.to)).toContain("always");
|
|
310
|
+
}
|
|
311
|
+
const md = renderBriefMarkdown(buildExecutionBrief(parse(TWO_OUTPUTS)));
|
|
312
|
+
expect(md).toContain("(no condition — default)");
|
|
313
|
+
expect(md).toContain("take the unconditional default row");
|
|
314
|
+
});
|
|
315
|
+
it("still says halt when a decision has no default", () => {
|
|
316
|
+
const md = renderBriefMarkdown(briefFor("code-review.at"));
|
|
317
|
+
expect(md).toContain("log `route-unmatched` and stop");
|
|
318
|
+
});
|
|
319
|
+
it("does not mark exclusive branches as mutually blind", () => {
|
|
320
|
+
// They never co-run, so they cannot leak into each other — and marking them
|
|
321
|
+
// blind told the host to dispatch every branch of a decision at once.
|
|
322
|
+
const b = buildExecutionBrief(parse(TWO_OUTPUTS));
|
|
323
|
+
const exclusive = b.steps.filter((s) => s.exclusive).flatMap((s) => s.ids);
|
|
324
|
+
for (const p of b.blindPairs) {
|
|
325
|
+
expect(exclusive).not.toContain(p.a);
|
|
326
|
+
expect(exclusive).not.toContain(p.b);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
it("heads a routing table with the node the condition TESTS, not the edge source", () => {
|
|
330
|
+
// Regression: `worker -> judge [when judge.verdict == fail]` produced a
|
|
331
|
+
// heading of `worker.verdict` — a node/output pair that does not exist,
|
|
332
|
+
// since `worker` declares no outputs at all.
|
|
333
|
+
const src = `topology t : [pipeline] {
|
|
334
|
+
meta {
|
|
335
|
+
version: "1.0.0"
|
|
336
|
+
description: "x"
|
|
337
|
+
}
|
|
338
|
+
agent worker {
|
|
339
|
+
model: sonnet
|
|
340
|
+
description: "w"
|
|
341
|
+
}
|
|
342
|
+
agent judge {
|
|
343
|
+
model: opus
|
|
344
|
+
description: "j"
|
|
345
|
+
outputs: {
|
|
346
|
+
verdict: pass | fail
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
action i {
|
|
350
|
+
kind: inline
|
|
351
|
+
description: "in"
|
|
352
|
+
}
|
|
353
|
+
flow { i -> worker
|
|
354
|
+
worker -> judge [when judge.verdict == fail] }
|
|
355
|
+
}`;
|
|
356
|
+
const b = buildExecutionBrief(parse(src));
|
|
357
|
+
expect(b.routes[0].subject).toBe("judge");
|
|
358
|
+
expect(b.routes[0].from).toBe("worker");
|
|
359
|
+
const md = renderBriefMarkdown(b);
|
|
360
|
+
expect(md).toContain("**On `judge.verdict`:**");
|
|
361
|
+
expect(md).not.toContain("worker.verdict");
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
describe("renderBriefMarkdown", () => {
|
|
365
|
+
it("refuses to be enacted when the topology has validation errors", () => {
|
|
366
|
+
const src = `topology t : [pipeline] {
|
|
367
|
+
meta {
|
|
368
|
+
version: "1.0.0"
|
|
369
|
+
description: "x"
|
|
370
|
+
}
|
|
371
|
+
agent a {
|
|
372
|
+
model: sonnet
|
|
373
|
+
description: "a"
|
|
374
|
+
prompt: "prompts/a.md"
|
|
375
|
+
}
|
|
376
|
+
action intake {
|
|
377
|
+
kind: inline
|
|
378
|
+
description: "in"
|
|
379
|
+
}
|
|
380
|
+
flow { intake -> a }
|
|
381
|
+
}`;
|
|
382
|
+
const ast = parse(src);
|
|
383
|
+
const errors = validate(ast).filter((r) => r.level === "error");
|
|
384
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
385
|
+
const md = renderBriefMarkdown(buildExecutionBrief(ast, { errors }));
|
|
386
|
+
expect(md).toContain("DO NOT ENACT");
|
|
387
|
+
expect(md).toContain("[V89]");
|
|
388
|
+
// And it must not go on to emit an enactment loop the host might follow.
|
|
389
|
+
expect(md).not.toContain("§0 — Enactment loop");
|
|
390
|
+
});
|
|
391
|
+
it("emits all eleven sections for a valid topology", () => {
|
|
392
|
+
const md = renderBriefMarkdown(briefFor("code-review.at"));
|
|
393
|
+
for (const s of ["§0", "§1", "§2", "§3", "§4", "§5", "§6", "§7", "§8", "§9", "§10"]) {
|
|
394
|
+
expect(md, s).toContain(s);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
it("tells the host to dispatch a parallel step in one message", () => {
|
|
398
|
+
const md = renderBriefMarkdown(briefFor("code-review.at"));
|
|
399
|
+
expect(md).toContain("all Agent calls in a single");
|
|
400
|
+
expect(md).toContain("you cannot leak it");
|
|
401
|
+
});
|
|
402
|
+
it("puts the canary rule in the enactment loop", () => {
|
|
403
|
+
const md = renderBriefMarkdown(briefFor("code-review.at"));
|
|
404
|
+
expect(md).toContain("FIRST subagent of the run, stop and ask");
|
|
405
|
+
});
|
|
406
|
+
it("gives every role a return protocol naming its declared enums", () => {
|
|
407
|
+
const md = renderBriefMarkdown(briefFor("code-review.at"));
|
|
408
|
+
expect(md).toContain("```at-output");
|
|
409
|
+
expect(md).toContain("risk-level: <low|medium|high|critical>");
|
|
410
|
+
expect(md).toContain("verdict: <approve|request-changes|reject>");
|
|
411
|
+
});
|
|
412
|
+
it("states an isolation instruction in a blind role's prompt", () => {
|
|
413
|
+
const md = renderBriefMarkdown(briefFor("code-review.at"));
|
|
414
|
+
expect(md).toContain("ISOLATION");
|
|
415
|
+
expect(md).toContain("must not read, ask for, or speculate about that work");
|
|
416
|
+
});
|
|
417
|
+
it("names the persistent features and points at scaffold", () => {
|
|
418
|
+
const md = renderBriefMarkdown(briefFor("code-review.at"));
|
|
419
|
+
expect(md).toMatch(/\*\*\d of 5 present\.\*\*/);
|
|
420
|
+
expect(md).toContain("agentopology scaffold examples/code-review.at");
|
|
421
|
+
});
|
|
422
|
+
it("renders every shipped example without throwing", () => {
|
|
423
|
+
for (const f of [
|
|
424
|
+
"code-review.at",
|
|
425
|
+
"simple-pipeline.at",
|
|
426
|
+
"data-processing.at",
|
|
427
|
+
"memory-system.at",
|
|
428
|
+
"scheduled-monitor.at",
|
|
429
|
+
"company-brain.at",
|
|
430
|
+
]) {
|
|
431
|
+
expect(() => renderBriefMarkdown(briefFor(f)), f).not.toThrow();
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
it("renders a gate's `before` anchor, not just `after`", () => {
|
|
435
|
+
// Regression: only `after` was shown, so a before-only gate read as
|
|
436
|
+
// anchored to nothing.
|
|
437
|
+
const md = renderBriefMarkdown(briefFor("simple-pipeline.at"));
|
|
438
|
+
expect(md).toContain("| gate | anchor |");
|
|
439
|
+
expect(md).toMatch(/after `writer`, before `reviewer`/);
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
//# sourceMappingURL=brief.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brief.test.js","sourceRoot":"","sources":["../../../src/plan/__tests__/brief.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The execution brief — what `agentopology plan` hands to a host coding agent.
|
|
3
|
+
*
|
|
4
|
+
* WHY MARKDOWN
|
|
5
|
+
* ------------
|
|
6
|
+
* The consumer is a language model, on three different vendors. Markdown is
|
|
7
|
+
* what every binding already emits and what a model acts on without a parse
|
|
8
|
+
* step; JSON costs more tokens for identical content and invites the model to
|
|
9
|
+
* summarize the document rather than execute it. A machine format already
|
|
10
|
+
* exists for programs: `agentopology export --format json`.
|
|
11
|
+
*
|
|
12
|
+
* TWO LAYERS
|
|
13
|
+
* ----------
|
|
14
|
+
* `buildExecutionBrief` computes a typed structure; `renderBriefMarkdown` is
|
|
15
|
+
* the only renderer that ships. A `--json` twin, if one is ever needed, is
|
|
16
|
+
* `JSON.stringify` of the same structure — so the two can never drift.
|
|
17
|
+
*
|
|
18
|
+
* WHAT IT IS NOT
|
|
19
|
+
* --------------
|
|
20
|
+
* Not a translation. `.at` is already agent-readable and `exporters/markdown`
|
|
21
|
+
* already renders it for humans. This resolves what a host would otherwise
|
|
22
|
+
* have to guess: order, handoffs, what must stay blind, gate tiers, loop
|
|
23
|
+
* budgets, and which declarations cannot be honoured without files.
|
|
24
|
+
*
|
|
25
|
+
* @module
|
|
26
|
+
*/
|
|
27
|
+
import type { TopologyAST, EdgeDef } from "../parser/ast.js";
|
|
28
|
+
import { type ResolvedDefault } from "../resolve/defaults.js";
|
|
29
|
+
import { type OrderStep, type LoopInfo } from "../resolve/order.js";
|
|
30
|
+
/** Autonomy notch. Governs approval before the run and announcements during it. */
|
|
31
|
+
export type Autonomy = "plan" | "execute" | "auto";
|
|
32
|
+
/**
|
|
33
|
+
* Gate enforcement tiers, strongest first. Tier is chosen by what the host
|
|
34
|
+
* platform actually offers, not by preference.
|
|
35
|
+
*/
|
|
36
|
+
export type GateTier = "preventive" | "enforced" | "fileless-verify" | "evidence-orchestrator" | "evidence-agent" | "advisory";
|
|
37
|
+
/** One resolved handoff across a flow edge. */
|
|
38
|
+
export interface Handoff {
|
|
39
|
+
from: string;
|
|
40
|
+
to: string;
|
|
41
|
+
/** `writer.writes ∩ reader.reads` — what actually crosses. */
|
|
42
|
+
passes: string[];
|
|
43
|
+
/** The rest of the writer's `writes` — deliberately not offered to the reader. */
|
|
44
|
+
withholds: string[];
|
|
45
|
+
}
|
|
46
|
+
/** Two roles in the same step with no edge between them. */
|
|
47
|
+
export interface BlindPair {
|
|
48
|
+
step: number;
|
|
49
|
+
a: string;
|
|
50
|
+
b: string;
|
|
51
|
+
}
|
|
52
|
+
export interface GatePlan {
|
|
53
|
+
id: string;
|
|
54
|
+
after?: string;
|
|
55
|
+
before?: string;
|
|
56
|
+
tier: GateTier;
|
|
57
|
+
/** The command, when the tier can run one. */
|
|
58
|
+
run?: string;
|
|
59
|
+
blocking: boolean;
|
|
60
|
+
onFail: string;
|
|
61
|
+
}
|
|
62
|
+
export interface Unenforceable {
|
|
63
|
+
node: string;
|
|
64
|
+
field: string;
|
|
65
|
+
declared: unknown;
|
|
66
|
+
}
|
|
67
|
+
/** An ambiguity the planner can detect before the run, so the guess is reviewable. */
|
|
68
|
+
export interface PreflaggedAmbiguity {
|
|
69
|
+
kind: string;
|
|
70
|
+
at: {
|
|
71
|
+
section: string;
|
|
72
|
+
node?: string;
|
|
73
|
+
edge?: string;
|
|
74
|
+
};
|
|
75
|
+
question: string;
|
|
76
|
+
alternatives: string[];
|
|
77
|
+
/** A concrete `.at` edit that removes the ambiguity. This is the payoff. */
|
|
78
|
+
fix: string;
|
|
79
|
+
}
|
|
80
|
+
export interface RoleCard {
|
|
81
|
+
id: string;
|
|
82
|
+
model?: string;
|
|
83
|
+
isolation?: string;
|
|
84
|
+
/** Authored prompt, or null when the topology only gave a role/description. */
|
|
85
|
+
prompt: string | null;
|
|
86
|
+
role: string;
|
|
87
|
+
/** Declared, as written in the topology. */
|
|
88
|
+
reads: string[];
|
|
89
|
+
writes: string[];
|
|
90
|
+
/** Resolved against `root` — what the prompt actually names. */
|
|
91
|
+
readsAbs: string[];
|
|
92
|
+
writesAbs: string[];
|
|
93
|
+
/**
|
|
94
|
+
* Paths this role must NOT read, even though an upstream role wrote them and
|
|
95
|
+
* they are on disk.
|
|
96
|
+
*
|
|
97
|
+
* This lives on the READER, not the writer. A writer cannot enforce a
|
|
98
|
+
* withhold — it has already written the file. The constraint is "the
|
|
99
|
+
* validator must not see the builder's self-review", which is an instruction
|
|
100
|
+
* to the validator. Putting it on the writer also produced a real bug: with
|
|
101
|
+
* two readers each taking a different file, the writer's card unioned both
|
|
102
|
+
* withholds and told it to withhold everything it was supposed to hand over.
|
|
103
|
+
*/
|
|
104
|
+
mustNotRead: string[];
|
|
105
|
+
mustNotReadAbs: string[];
|
|
106
|
+
/** Sibling ids in the same step it must stay blind to. */
|
|
107
|
+
blindTo: string[];
|
|
108
|
+
outputs: Record<string, string[]>;
|
|
109
|
+
/** Declared tool restriction, which cannot be applied at runtime. */
|
|
110
|
+
declaredTools?: string[];
|
|
111
|
+
fileless: boolean;
|
|
112
|
+
}
|
|
113
|
+
export interface ExecutionBrief {
|
|
114
|
+
topology: string;
|
|
115
|
+
version: string;
|
|
116
|
+
source: string;
|
|
117
|
+
/** Absolute directory every declared path resolves against. */
|
|
118
|
+
root: string;
|
|
119
|
+
/** The task text substituted into `{{TASK}}`, or null if none was given. */
|
|
120
|
+
task: string | null;
|
|
121
|
+
/** Source-tree revision at plan time, for drift detection. */
|
|
122
|
+
revision: string | null;
|
|
123
|
+
autonomy: Autonomy;
|
|
124
|
+
ambiguityLog: string | null;
|
|
125
|
+
/** Validation errors. A non-empty list means the brief must not be enacted. */
|
|
126
|
+
errors: Array<{
|
|
127
|
+
rule: string;
|
|
128
|
+
message: string;
|
|
129
|
+
node?: string;
|
|
130
|
+
}>;
|
|
131
|
+
steps: OrderStep[];
|
|
132
|
+
/**
|
|
133
|
+
* `reads` paths no node produces — they must exist before step 1. Each is
|
|
134
|
+
* checked on disk, because "must exist first" that nobody verifies is a
|
|
135
|
+
* runtime surprise instead of a plan-time refusal.
|
|
136
|
+
*/
|
|
137
|
+
preconditions: Array<{
|
|
138
|
+
path: string;
|
|
139
|
+
absolute: string;
|
|
140
|
+
exists: boolean;
|
|
141
|
+
}>;
|
|
142
|
+
roles: RoleCard[];
|
|
143
|
+
handoffs: Handoff[];
|
|
144
|
+
blindPairs: BlindPair[];
|
|
145
|
+
/**
|
|
146
|
+
* Routing decisions, grouped by (source, output key). Previously grouped by
|
|
147
|
+
* source alone with the key read off `edges[0]`, so a node routing on two
|
|
148
|
+
* different outputs got one heading naming whichever came first — and every
|
|
149
|
+
* row underneath it looked like it tested that output.
|
|
150
|
+
*/
|
|
151
|
+
routes: Array<{
|
|
152
|
+
/** The edge's source node. */
|
|
153
|
+
from: string;
|
|
154
|
+
/** The node whose output the condition tests — NOT always `from`. */
|
|
155
|
+
subject: string;
|
|
156
|
+
key: string;
|
|
157
|
+
edges: EdgeDef[];
|
|
158
|
+
/** Unconditional out-edges from the same source: the default path. */
|
|
159
|
+
fallbacks: EdgeDef[];
|
|
160
|
+
}>;
|
|
161
|
+
loops: LoopInfo[];
|
|
162
|
+
gates: GatePlan[];
|
|
163
|
+
unenforceable: Unenforceable[];
|
|
164
|
+
/** Which of the five inherently-persistent features this topology declares. */
|
|
165
|
+
persistent: Array<{
|
|
166
|
+
feature: string;
|
|
167
|
+
count: number;
|
|
168
|
+
}>;
|
|
169
|
+
preflagged: PreflaggedAmbiguity[];
|
|
170
|
+
defaultsApplied: ResolvedDefault[];
|
|
171
|
+
orchestrator: string | null;
|
|
172
|
+
}
|
|
173
|
+
export interface BriefOptions {
|
|
174
|
+
autonomy?: Autonomy;
|
|
175
|
+
source?: string;
|
|
176
|
+
ambiguityLog?: string | null;
|
|
177
|
+
/** Validation results, so the brief can refuse to be enacted on errors. */
|
|
178
|
+
errors?: Array<{
|
|
179
|
+
rule: string;
|
|
180
|
+
message: string;
|
|
181
|
+
node?: string;
|
|
182
|
+
}>;
|
|
183
|
+
/**
|
|
184
|
+
* The concrete task this run is for — substituted into every role prompt's
|
|
185
|
+
* `{{TASK}}`. Dogfooding found the brief told the host to substitute it while
|
|
186
|
+
* never saying what it was or where it came from.
|
|
187
|
+
*/
|
|
188
|
+
task?: string | null;
|
|
189
|
+
/**
|
|
190
|
+
* Directory that `reads`/`writes` paths resolve against. Role cards emit
|
|
191
|
+
* ABSOLUTE paths: dogfooding found three subagents each normalised a relative
|
|
192
|
+
* path differently, so the declared handoff pointed nowhere and the receiving
|
|
193
|
+
* role had to be corrected by hand.
|
|
194
|
+
*/
|
|
195
|
+
root?: string;
|
|
196
|
+
/** Commit sha of the source tree, so mid-run drift is detectable. */
|
|
197
|
+
revision?: string | null;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Build the execution brief structure for a topology.
|
|
201
|
+
*
|
|
202
|
+
* @param rawAst - A parsed topology. Defaults are resolved internally.
|
|
203
|
+
* @param opts - Autonomy notch, source path, log path, validation results.
|
|
204
|
+
* @returns The typed brief. Render it with {@link renderBriefMarkdown}.
|
|
205
|
+
*/
|
|
206
|
+
export declare function buildExecutionBrief(rawAst: TopologyAST, opts?: BriefOptions): ExecutionBrief;
|
|
207
|
+
//# sourceMappingURL=brief.d.ts.map
|