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.
Files changed (142) hide show
  1. package/README.md +53 -5
  2. package/dist/analyzer/__tests__/analyzer.test.js +36 -9
  3. package/dist/analyzer/__tests__/analyzer.test.js.map +1 -1
  4. package/dist/analyzer/__tests__/observability-gap.test.js +40 -10
  5. package/dist/analyzer/__tests__/observability-gap.test.js.map +1 -1
  6. package/dist/analyzer/index.d.ts +2 -1
  7. package/dist/analyzer/index.d.ts.map +1 -1
  8. package/dist/analyzer/index.js +1 -1
  9. package/dist/analyzer/index.js.map +1 -1
  10. package/dist/bindings/__tests__/bindings.test.js +20 -5
  11. package/dist/bindings/__tests__/bindings.test.js.map +1 -1
  12. package/dist/bindings/__tests__/claude-workflow.test.js +12 -3
  13. package/dist/bindings/__tests__/claude-workflow.test.js.map +1 -1
  14. package/dist/bindings/__tests__/defaults-agreement.test.d.ts +16 -0
  15. package/dist/bindings/__tests__/defaults-agreement.test.d.ts.map +1 -0
  16. package/dist/bindings/__tests__/defaults-agreement.test.js +80 -0
  17. package/dist/bindings/__tests__/defaults-agreement.test.js.map +1 -0
  18. package/dist/bindings/__tests__/observability.test.js +8 -2
  19. package/dist/bindings/__tests__/observability.test.js.map +1 -1
  20. package/dist/bindings/__tests__/stub-generation.test.js +8 -2
  21. package/dist/bindings/__tests__/stub-generation.test.js.map +1 -1
  22. package/dist/bindings/claude-code.d.ts.map +1 -1
  23. package/dist/bindings/claude-code.js +2 -1
  24. package/dist/bindings/claude-code.js.map +1 -1
  25. package/dist/bindings/claude-workflow.d.ts.map +1 -1
  26. package/dist/bindings/claude-workflow.js +2 -1
  27. package/dist/bindings/claude-workflow.js.map +1 -1
  28. package/dist/bindings/codex.d.ts.map +1 -1
  29. package/dist/bindings/codex.js +6 -2
  30. package/dist/bindings/codex.js.map +1 -1
  31. package/dist/bindings/gemini-cli.d.ts.map +1 -1
  32. package/dist/bindings/gemini-cli.js +2 -1
  33. package/dist/bindings/gemini-cli.js.map +1 -1
  34. package/dist/bindings/kiro.d.ts.map +1 -1
  35. package/dist/bindings/kiro.js +2 -1
  36. package/dist/bindings/kiro.js.map +1 -1
  37. package/dist/bindings/openclaw.d.ts.map +1 -1
  38. package/dist/bindings/openclaw.js +2 -1
  39. package/dist/bindings/openclaw.js.map +1 -1
  40. package/dist/cli/__tests__/cli.test.d.ts +12 -0
  41. package/dist/cli/__tests__/cli.test.d.ts.map +1 -0
  42. package/dist/cli/__tests__/cli.test.js +171 -0
  43. package/dist/cli/__tests__/cli.test.js.map +1 -0
  44. package/dist/cli/index.d.ts +1 -0
  45. package/dist/cli/index.d.ts.map +1 -1
  46. package/dist/cli/index.js +149 -3
  47. package/dist/cli/index.js.map +1 -1
  48. package/dist/docs/content.d.ts.map +1 -1
  49. package/dist/docs/content.js +112 -0
  50. package/dist/docs/content.js.map +1 -1
  51. package/dist/exporters/ascii.d.ts +13 -0
  52. package/dist/exporters/ascii.d.ts.map +1 -0
  53. package/dist/exporters/ascii.js +26 -0
  54. package/dist/exporters/ascii.js.map +1 -0
  55. package/dist/exporters/brief.d.ts +14 -0
  56. package/dist/exporters/brief.d.ts.map +1 -0
  57. package/dist/exporters/brief.js +37 -0
  58. package/dist/exporters/brief.js.map +1 -0
  59. package/dist/exporters/index.d.ts +2 -0
  60. package/dist/exporters/index.d.ts.map +1 -1
  61. package/dist/exporters/index.js +6 -0
  62. package/dist/exporters/index.js.map +1 -1
  63. package/dist/exporters/markdown.d.ts.map +1 -1
  64. package/dist/exporters/markdown.js +2 -1
  65. package/dist/exporters/markdown.js.map +1 -1
  66. package/dist/exporters/mermaid.d.ts.map +1 -1
  67. package/dist/exporters/mermaid.js +2 -1
  68. package/dist/exporters/mermaid.js.map +1 -1
  69. package/dist/import/__tests__/import.test.js +8 -2
  70. package/dist/import/__tests__/import.test.js.map +1 -1
  71. package/dist/parser/__tests__/parser.test.js +1386 -210
  72. package/dist/parser/__tests__/parser.test.js.map +1 -1
  73. package/dist/parser/__tests__/spec-sync.test.d.ts +10 -0
  74. package/dist/parser/__tests__/spec-sync.test.d.ts.map +1 -0
  75. package/dist/parser/__tests__/spec-sync.test.js +60 -0
  76. package/dist/parser/__tests__/spec-sync.test.js.map +1 -0
  77. package/dist/parser/index.d.ts +50 -6
  78. package/dist/parser/index.d.ts.map +1 -1
  79. package/dist/parser/index.js +183 -28
  80. package/dist/parser/index.js.map +1 -1
  81. package/dist/parser/validator.d.ts +7 -1
  82. package/dist/parser/validator.d.ts.map +1 -1
  83. package/dist/parser/validator.js +77 -2
  84. package/dist/parser/validator.js.map +1 -1
  85. package/dist/plan/__tests__/brief.test.d.ts +2 -0
  86. package/dist/plan/__tests__/brief.test.d.ts.map +1 -0
  87. package/dist/plan/__tests__/brief.test.js +442 -0
  88. package/dist/plan/__tests__/brief.test.js.map +1 -0
  89. package/dist/plan/brief.d.ts +207 -0
  90. package/dist/plan/brief.d.ts.map +1 -0
  91. package/dist/plan/brief.js +342 -0
  92. package/dist/plan/brief.js.map +1 -0
  93. package/dist/plan/render.d.ts +20 -0
  94. package/dist/plan/render.d.ts.map +1 -0
  95. package/dist/plan/render.js +425 -0
  96. package/dist/plan/render.js.map +1 -0
  97. package/dist/render/__tests__/ascii.test.d.ts +2 -0
  98. package/dist/render/__tests__/ascii.test.d.ts.map +1 -0
  99. package/dist/render/__tests__/ascii.test.js +187 -0
  100. package/dist/render/__tests__/ascii.test.js.map +1 -0
  101. package/dist/render/ascii.d.ts +23 -0
  102. package/dist/render/ascii.d.ts.map +1 -0
  103. package/dist/render/ascii.js +187 -0
  104. package/dist/render/ascii.js.map +1 -0
  105. package/dist/resolve/__tests__/defaults.test.d.ts +2 -0
  106. package/dist/resolve/__tests__/defaults.test.d.ts.map +1 -0
  107. package/dist/resolve/__tests__/defaults.test.js +109 -0
  108. package/dist/resolve/__tests__/defaults.test.js.map +1 -0
  109. package/dist/resolve/__tests__/order.test.d.ts +2 -0
  110. package/dist/resolve/__tests__/order.test.d.ts.map +1 -0
  111. package/dist/resolve/__tests__/order.test.js +238 -0
  112. package/dist/resolve/__tests__/order.test.js.map +1 -0
  113. package/dist/resolve/__tests__/phase.test.d.ts +12 -0
  114. package/dist/resolve/__tests__/phase.test.d.ts.map +1 -0
  115. package/dist/resolve/__tests__/phase.test.js +81 -0
  116. package/dist/resolve/__tests__/phase.test.js.map +1 -0
  117. package/dist/resolve/defaults.d.ts +51 -0
  118. package/dist/resolve/defaults.d.ts.map +1 -0
  119. package/dist/resolve/defaults.js +109 -0
  120. package/dist/resolve/defaults.js.map +1 -0
  121. package/dist/resolve/order.d.ts +73 -0
  122. package/dist/resolve/order.d.ts.map +1 -0
  123. package/dist/resolve/order.js +208 -0
  124. package/dist/resolve/order.js.map +1 -0
  125. package/dist/resolve/phase.d.ts +57 -0
  126. package/dist/resolve/phase.d.ts.map +1 -0
  127. package/dist/resolve/phase.js +60 -0
  128. package/dist/resolve/phase.js.map +1 -0
  129. package/dist/scaffold/__tests__/scaffold.test.js +8 -2
  130. package/dist/scaffold/__tests__/scaffold.test.js.map +1 -1
  131. package/dist/scaffold/__tests__/settings-merge.test.js +95 -0
  132. package/dist/scaffold/__tests__/settings-merge.test.js.map +1 -1
  133. package/dist/scaffold/incremental.d.ts.map +1 -1
  134. package/dist/scaffold/incremental.js +13 -2
  135. package/dist/scaffold/incremental.js.map +1 -1
  136. package/dist/visualizer/__tests__/topology-brain-link.test.js +19 -4
  137. package/dist/visualizer/__tests__/topology-brain-link.test.js.map +1 -1
  138. package/examples/code-review.at +32 -4
  139. package/examples/data-processing.at +33 -4
  140. package/package.json +1 -1
  141. package/spec/grammar.md +25 -1
  142. package/spec/validation.md +103 -35
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The spec must not drift from the implementation.
3
+ *
4
+ * `spec/validation.md` documented 35 rules while `validator.ts` implemented 89.
5
+ * Fifty-four rules existed only in source. Nobody noticed because nothing
6
+ * compared them — which is the actual bug. Documentation drift is not a
7
+ * discipline problem, it is a missing test.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=spec-sync.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-sync.test.d.ts","sourceRoot":"","sources":["../../../src/parser/__tests__/spec-sync.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The spec must not drift from the implementation.
3
+ *
4
+ * `spec/validation.md` documented 35 rules while `validator.ts` implemented 89.
5
+ * Fifty-four rules existed only in source. Nobody noticed because nothing
6
+ * compared them — which is the actual bug. Documentation drift is not a
7
+ * discipline problem, it is a missing test.
8
+ */
9
+ import { describe, it, expect } from "vitest";
10
+ import { readFileSync } from "node:fs";
11
+ import { resolve, dirname } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
14
+ const read = (p) => readFileSync(resolve(root, p), "utf8");
15
+ /** Rule numbers the validator actually registers, from its `validate()` spread. */
16
+ function implementedRules() {
17
+ const src = read("src/parser/validator.ts");
18
+ const body = src.slice(src.indexOf("export function validate("));
19
+ const fns = [...body.matchAll(/\.\.\.(v(\d+)[A-Za-z0-9_]*)\(ast\)/g)];
20
+ return [...new Set(fns.map((m) => Number(m[2])))].sort((a, b) => a - b);
21
+ }
22
+ /** Rule numbers documented in the spec's summary table. */
23
+ function documentedRules() {
24
+ const md = read("spec/validation.md");
25
+ const rows = [...md.matchAll(/^\|\s*V(\d+)\s*\|/gm)];
26
+ return [...new Set(rows.map((m) => Number(m[1])))].sort((a, b) => a - b);
27
+ }
28
+ describe("spec/validation.md tracks the validator", () => {
29
+ it("documents every implemented rule", () => {
30
+ const impl = implementedRules();
31
+ const docs = documentedRules();
32
+ const undocumented = impl.filter((n) => !docs.includes(n));
33
+ expect(undocumented, `implemented but undocumented: ${undocumented.map((n) => "V" + n).join(", ")}`).toEqual([]);
34
+ });
35
+ it("documents no rule the validator does not implement", () => {
36
+ const impl = implementedRules();
37
+ const docs = documentedRules();
38
+ const phantom = docs.filter((n) => !impl.includes(n));
39
+ expect(phantom, `documented but not implemented: ${phantom.map((n) => "V" + n).join(", ")}`).toEqual([]);
40
+ });
41
+ it("numbers rules contiguously from V1", () => {
42
+ const impl = implementedRules();
43
+ const gaps = [];
44
+ for (let i = 1; i <= Math.max(...impl); i++)
45
+ if (!impl.includes(i))
46
+ gaps.push(i);
47
+ expect(gaps, `gaps in rule numbering: ${gaps.join(", ")}`).toEqual([]);
48
+ });
49
+ it("states the same rule count in prose as it lists in the table", () => {
50
+ const md = read("spec/validation.md");
51
+ const docs = documentedRules();
52
+ // The prose above the table names the count; a stale number is exactly the
53
+ // drift this file exists to catch.
54
+ const claimed = [...md.matchAll(/All (\d+) rules/g)].map((m) => Number(m[1]));
55
+ expect(claimed.length, "spec should state its rule count").toBeGreaterThan(0);
56
+ for (const n of claimed)
57
+ expect(n).toBe(docs.length);
58
+ });
59
+ });
60
+ //# sourceMappingURL=spec-sync.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-sync.test.js","sourceRoot":"","sources":["../../../src/parser/__tests__/spec-sync.test.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,20 @@
14
14
  */
15
15
  import type { TopologyAST, TopologyMeta, OrchestratorNode, ActionNode, AgentNode, GateNode, HumanNode, GroupNode, EdgeDef, DepthDef, TriggerDef, HookDef, SkillDef, ToolBlockDef, MeteringDef, ProviderDef, ScheduleJobDef, InterfaceDef, DefaultsDef, SchemaType, SchemaFieldDef, SchemaDef, SensitiveValue, ObservabilityDef, ParamDef, InterfaceEndpoints, ImportDef, IncludeDef, CheckpointDef, ArtifactDef, StoreNode, RetrievalNode } from "./ast.js";
16
16
  export type * from "./ast.js";
17
+ /**
18
+ * Placeholders the parser injects for REQUIRED fields that were not declared.
19
+ *
20
+ * The parser cannot leave them undefined without changing non-optional AST
21
+ * types, so it substitutes a sentinel — and that sentinel then satisfied every
22
+ * "is it present?" check downstream. V91 recognises them and reports the field
23
+ * as missing, which is what the spec says it is.
24
+ */
25
+ export declare const MISSING: {
26
+ /** `orchestrator.model` — spec/grammar.md, orchestrator field table. */
27
+ readonly model: "unknown";
28
+ /** `meta.version` — spec/grammar.md:282, Required: yes. */
29
+ readonly version: "0.0.0";
30
+ };
17
31
  /**
18
32
  * Parse a type expression string into a {@link SchemaType}.
19
33
  *
@@ -57,7 +71,12 @@ export declare function parseMeta(body: string): Partial<TopologyMeta>;
57
71
  /**
58
72
  * Parse the `orchestrator { ... }` block into an {@link OrchestratorNode}.
59
73
  */
60
- export declare function parseOrchestrator(body: string): OrchestratorNode;
74
+ export declare function parseOrchestrator(body: string, _blockFieldMisuseWarnings?: Array<{
75
+ rule: string;
76
+ level: "error" | "warning";
77
+ message: string;
78
+ node?: string;
79
+ }>): OrchestratorNode;
61
80
  /**
62
81
  * Parse the `roles { ... }` block into a map of role name to description.
63
82
  */
@@ -65,7 +84,12 @@ export declare function parseRoles(body: string): Record<string, string>;
65
84
  /**
66
85
  * Parse an `action <id> { ... }` block into an {@link ActionNode}.
67
86
  */
68
- export declare function parseAction(id: string, body: string): ActionNode;
87
+ export declare function parseAction(id: string, body: string, _blockFieldMisuseWarnings?: Array<{
88
+ rule: string;
89
+ level: "error" | "warning";
90
+ message: string;
91
+ node?: string;
92
+ }>): ActionNode;
69
93
  /**
70
94
  * Parse an `agent <id> { ... }` block into an {@link AgentNode}.
71
95
  *
@@ -74,19 +98,39 @@ export declare function parseAction(id: string, body: string): ActionNode;
74
98
  * @param roles - Role descriptions from the `roles` block, used to attach a
75
99
  * role description to matching agents.
76
100
  */
77
- export declare function parseAgent(id: string, body: string, roles: Record<string, string>): AgentNode;
101
+ export declare function parseAgent(id: string, body: string, roles: Record<string, string>, _blockFieldMisuseWarnings?: Array<{
102
+ rule: string;
103
+ level: "error" | "warning";
104
+ message: string;
105
+ node?: string;
106
+ }>): AgentNode;
78
107
  /**
79
108
  * Parse a `gate <id> { ... }` block into a {@link GateNode}.
80
109
  */
81
- export declare function parseGate(id: string, body: string): GateNode;
110
+ export declare function parseGate(id: string, body: string, _blockFieldMisuseWarnings?: Array<{
111
+ rule: string;
112
+ level: "error" | "warning";
113
+ message: string;
114
+ node?: string;
115
+ }>): GateNode;
82
116
  /**
83
117
  * Parse a `human <id> { ... }` block into a {@link HumanNode}.
84
118
  */
85
- export declare function parseHuman(id: string, body: string): HumanNode;
119
+ export declare function parseHuman(id: string, body: string, _blockFieldMisuseWarnings?: Array<{
120
+ rule: string;
121
+ level: "error" | "warning";
122
+ message: string;
123
+ node?: string;
124
+ }>): HumanNode;
86
125
  /**
87
126
  * Parse a `group <id> { ... }` block into a {@link GroupNode}.
88
127
  */
89
- export declare function parseGroup(id: string, body: string): GroupNode;
128
+ export declare function parseGroup(id: string, body: string, _blockFieldMisuseWarnings?: Array<{
129
+ rule: string;
130
+ level: "error" | "warning";
131
+ message: string;
132
+ node?: string;
133
+ }>): GroupNode;
90
134
  /**
91
135
  * Parse the `flow { ... }` block into an array of {@link EdgeDef}.
92
136
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,78 @@ import { stripComments, extractBlock, extractAllBlocks, parseKV, parseList, pars
16
16
  // ---------------------------------------------------------------------------
17
17
  // Helpers
18
18
  // ---------------------------------------------------------------------------
19
+ /**
20
+ * V90: detect a field value that swallowed the next field.
21
+ *
22
+ * `parseFields` is line-based, and the grammar never says so. Writing two
23
+ * fields on one line — `agent a { model: sonnet retry: 3 }` — makes the first
24
+ * key take the whole rest of the line as its value (`"sonnet retry: 3"`), and
25
+ * nothing catches it. The value is then silently wrong: a model name that does
26
+ * not exist, a version string carrying a description, an action `kind` that
27
+ * fails its enum for a confusing reason.
28
+ *
29
+ * Detection: an UNQUOTED value containing whitespace followed by
30
+ * `identifier:` is almost certainly a swallowed field. Quoted values are
31
+ * exempt (a description may legitimately contain a colon), and a leading
32
+ * scheme like `https://` is not matched because it has no preceding
33
+ * whitespace.
34
+ */
35
+ function detectSwallowedFields(fields, nodeId, out) {
36
+ for (const [key, raw] of Object.entries(fields)) {
37
+ if (typeof raw !== "string")
38
+ continue;
39
+ const value = raw.trim();
40
+ // Free-prose fields whose quotes have already been stripped are exempt: a
41
+ // colon inside a description is ordinary English ("Demo: agent pipeline"),
42
+ // and once the quotes are gone there is no way to tell prose from a
43
+ // swallow. Where quotes SURVIVE parsing — every block except `meta` — the
44
+ // check below still catches `description: "k" model: sonnet`.
45
+ const PROSE_FIELDS = new Set(["description", "role", "generates", "termination"]);
46
+ if (PROSE_FIELDS.has(key) && !value.startsWith('"'))
47
+ continue;
48
+ // A balanced brace block is a NESTED OBJECT, not a swallow. The single-line
49
+ // form is spec-legal and parses correctly — `spec/grammar.md:117` gives
50
+ // `outputs: { depth: 1 | 2 | 3 }` verbatim. Flagging it rejected valid
51
+ // topologies, which is worse than missing one.
52
+ if (value.startsWith("{") && value.endsWith("}"))
53
+ continue;
54
+ // Same for a list.
55
+ if (value.startsWith("[") && value.endsWith("]"))
56
+ continue;
57
+ // A WELL-FORMED quoted string is exempt — a description may contain a
58
+ // colon. `"1.0.0" description: "x"` is NOT well-formed: it is one quoted
59
+ // token followed by more content, i.e. a swallowed field.
60
+ if (/^"(?:[^"\\]|\\.)*"$/.test(value))
61
+ continue;
62
+ const search = value.startsWith('"')
63
+ ? value.replace(/^"(?:[^"\\]|\\.)*"/, "")
64
+ : value;
65
+ const m = /\s([a-zA-Z][a-zA-Z0-9_-]*):/.exec(search.startsWith(" ") ? search : " " + search);
66
+ if (!m)
67
+ continue;
68
+ out.push({
69
+ rule: "V90",
70
+ level: "error",
71
+ message: `"${nodeId}" field \`${key}\` has the value \`${value}\`, which looks like it swallowed the ` +
72
+ `field \`${m[1]}\` — fields must be one per line`,
73
+ node: nodeId,
74
+ });
75
+ }
76
+ }
77
+ /**
78
+ * Placeholders the parser injects for REQUIRED fields that were not declared.
79
+ *
80
+ * The parser cannot leave them undefined without changing non-optional AST
81
+ * types, so it substitutes a sentinel — and that sentinel then satisfied every
82
+ * "is it present?" check downstream. V91 recognises them and reports the field
83
+ * as missing, which is what the spec says it is.
84
+ */
85
+ export const MISSING = {
86
+ /** `orchestrator.model` — spec/grammar.md, orchestrator field table. */
87
+ model: "unknown",
88
+ /** `meta.version` — spec/grammar.md:282, Required: yes. */
89
+ version: "0.0.0",
90
+ };
19
91
  /** Convert a kebab-case or snake_case identifier to a Title Case label. */
20
92
  function toLabel(id) {
21
93
  return id
@@ -203,8 +275,11 @@ export function parseMeta(body) {
203
275
  /**
204
276
  * Parse the `orchestrator { ... }` block into an {@link OrchestratorNode}.
205
277
  */
206
- export function parseOrchestrator(body) {
278
+ export function parseOrchestrator(body, _blockFieldMisuseWarnings) {
207
279
  const fields = parseFields(body);
280
+ if (_blockFieldMisuseWarnings) {
281
+ detectSwallowedFields(fields, "orchestrator", _blockFieldMisuseWarnings);
282
+ }
208
283
  const handles = parseMultilineList(body, "handles");
209
284
  const outputs = parseOutputsBlock(body);
210
285
  const node = {
@@ -247,8 +322,11 @@ export function parseRoles(body) {
247
322
  /**
248
323
  * Parse an `action <id> { ... }` block into an {@link ActionNode}.
249
324
  */
250
- export function parseAction(id, body) {
325
+ export function parseAction(id, body, _blockFieldMisuseWarnings) {
251
326
  const fields = parseFields(body);
327
+ if (_blockFieldMisuseWarnings) {
328
+ detectSwallowedFields(fields, id, _blockFieldMisuseWarnings);
329
+ }
252
330
  const commands = parseMultilineList(body, "commands");
253
331
  const node = {
254
332
  id,
@@ -279,7 +357,7 @@ export function parseAction(id, body) {
279
357
  * @param roles - Role descriptions from the `roles` block, used to attach a
280
358
  * role description to matching agents.
281
359
  */
282
- export function parseAgent(id, body, roles) {
360
+ export function parseAgent(id, body, roles, _blockFieldMisuseWarnings) {
283
361
  const fields = parseFields(body);
284
362
  const tools = parseMultilineList(body, "tools");
285
363
  const skills = parseMultilineList(body, "skills");
@@ -298,7 +376,24 @@ export function parseAgent(id, body, roles) {
298
376
  node.model = fields.model;
299
377
  if (fields.permissions)
300
378
  node.permissions = fields.permissions;
301
- // Extract prompt from prompt {} block (not KV pair)
379
+ // Extract prompt from prompt {} block (not KV pair).
380
+ //
381
+ // V89: `agent.prompt` is a BLOCK per spec/grammar.md:350. The string form
382
+ // (`prompt: "path.md"`) belongs to `skill`, a different node type
383
+ // (spec/grammar.md:1377). A KV `prompt` on an agent is silently unparseable,
384
+ // so flag it rather than dropping the author's instructions on the floor.
385
+ if (fields.prompt) {
386
+ _blockFieldMisuseWarnings?.push({
387
+ rule: "V89",
388
+ level: "error",
389
+ message: `agent "${id}" uses \`prompt:\` as a key-value field, but agent prompts must be a \`prompt { }\` block — ` +
390
+ `this value is ignored and the agent will have no instructions`,
391
+ node: id,
392
+ });
393
+ }
394
+ if (_blockFieldMisuseWarnings) {
395
+ detectSwallowedFields(fields, id, _blockFieldMisuseWarnings);
396
+ }
302
397
  const promptBlock = extractBlock(body, "prompt");
303
398
  if (promptBlock) {
304
399
  node.prompt = dedentBlock(promptBlock.body);
@@ -555,8 +650,11 @@ export function parseAgent(id, body, roles) {
555
650
  /**
556
651
  * Parse a `gate <id> { ... }` block into a {@link GateNode}.
557
652
  */
558
- export function parseGate(id, body) {
653
+ export function parseGate(id, body, _blockFieldMisuseWarnings) {
559
654
  const fields = parseFields(body);
655
+ if (_blockFieldMisuseWarnings) {
656
+ detectSwallowedFields(fields, id, _blockFieldMisuseWarnings);
657
+ }
560
658
  const checks = parseMultilineList(body, "checks");
561
659
  const node = {
562
660
  id,
@@ -587,8 +685,11 @@ export function parseGate(id, body) {
587
685
  /**
588
686
  * Parse a `human <id> { ... }` block into a {@link HumanNode}.
589
687
  */
590
- export function parseHuman(id, body) {
688
+ export function parseHuman(id, body, _blockFieldMisuseWarnings) {
591
689
  const fields = parseFields(body);
690
+ if (_blockFieldMisuseWarnings) {
691
+ detectSwallowedFields(fields, id, _blockFieldMisuseWarnings);
692
+ }
592
693
  const node = {
593
694
  id,
594
695
  type: "human",
@@ -605,8 +706,11 @@ export function parseHuman(id, body) {
605
706
  /**
606
707
  * Parse a `group <id> { ... }` block into a {@link GroupNode}.
607
708
  */
608
- export function parseGroup(id, body) {
709
+ export function parseGroup(id, body, _blockFieldMisuseWarnings) {
609
710
  const fields = parseFields(body);
711
+ if (_blockFieldMisuseWarnings) {
712
+ detectSwallowedFields(fields, id, _blockFieldMisuseWarnings);
713
+ }
610
714
  const members = parseList(fields.members ?? "");
611
715
  const node = {
612
716
  id,
@@ -651,6 +755,24 @@ export function parseFlow(body, _edgeAttributeErrors) {
651
755
  // Match annotation at end of line: [when ..., max N, per agent-id, tolerance: N, race, wait 30s, reflection]
652
756
  // Must start with "when", "max", "per", "tolerance", "race", "wait", "join", "weight", or "reflection" to avoid matching fan-out lists.
653
757
  const bracketMatch = trimmed.match(/\[(when\s+.+?|max\s+\d+.*?|per\s+\S+.*?|tolerance\s*:.+?|race.*?|wait\s+\S+.*?|join\s+\S+.*?|weight\s+\S+.*?|reflection.*?)\]\s*$/);
758
+ // V92: two bracket groups on one edge. The grammar defines ONE annotation
759
+ // with comma-separated attributes (`[when ..., max N]`). Written as
760
+ // `[when ...] [max N]`, the matcher below spans from the first `[` to the
761
+ // last `]` and produces a corrupt condition — literally
762
+ // `a.verdict == fail] [max 3` — with no error at all.
763
+ // Count only ANNOTATION brackets. A fan-out list is also bracketed —
764
+ // `analyzer -> [reporter, alerter] [when ...]` is legal — so testing for
765
+ // `] [` alone flagged valid topologies.
766
+ const ATTR = /\[\s*(?:when|max|per|tolerance|race|wait|join|weight|reflection)\b/g;
767
+ const annotationBrackets = (trimmed.match(ATTR) ?? []).length;
768
+ if (annotationBrackets > 1 && _edgeAttributeErrors) {
769
+ _edgeAttributeErrors.push({
770
+ rule: "V92",
771
+ level: "error",
772
+ message: "Edge has two bracket groups — attributes go in ONE bracket, " +
773
+ `comma-separated: [when ..., max N] (line: ${trimmed})`,
774
+ });
775
+ }
654
776
  if (bracketMatch) {
655
777
  flowPart = trimmed.slice(0, bracketMatch.index).trim();
656
778
  const annotation = bracketMatch[1];
@@ -1062,10 +1184,7 @@ export function parseMemory(body, _unknownSubBlockWarnings) {
1062
1184
  ];
1063
1185
  const knownSubsSet = new Set([
1064
1186
  ...knownSubs,
1065
- "store", "retrieval",
1066
- // Sub-blocks within store {} and retrieval {} (not top-level memory subs,
1067
- // but the V24 scanner sees them because it doesn't track depth)
1068
- "embedding", "index", "ingestion", "search", "lifecycle", "backend-config", "scoring",
1187
+ "store", "retrieval", // the two typed top-level blocks
1069
1188
  ]);
1070
1189
  for (const name of knownSubs) {
1071
1190
  const block = extractBlock(body, name);
@@ -1098,19 +1217,47 @@ export function parseMemory(body, _unknownSubBlockWarnings) {
1098
1217
  continue;
1099
1218
  retrievals.push(parseRetrieval(block.id, block.body));
1100
1219
  }
1101
- // Detect unknown sub-blocks
1220
+ // Detect unknown sub-blocks. Only consider TOP-LEVEL blocks of the memory
1221
+ // body — names nested inside a `store {}` or `retrieval {}` (e.g. a brain
1222
+ // store's `sources { gmail {} }`) are that block's own concern, not memory
1223
+ // sub-blocks. Walk the body tracking brace depth so we only flag depth-0 names.
1102
1224
  if (_unknownSubBlockWarnings) {
1103
- const subBlockRe = /(?:^|\n)\s*([a-zA-Z][a-zA-Z0-9_-]*)\s*\{/gm;
1104
- let m;
1105
- while ((m = subBlockRe.exec(body)) !== null) {
1106
- const name = m[1];
1107
- if (!knownSubsSet.has(name)) {
1108
- _unknownSubBlockWarnings.push({
1109
- rule: "V24",
1110
- level: "warning",
1111
- message: `Unknown sub-block "${name}" in memory block — known sub-blocks are: ${knownSubs.join(", ")}`,
1112
- });
1225
+ let depth = 0;
1226
+ let i = 0;
1227
+ while (i < body.length) {
1228
+ const ch = body[i];
1229
+ if (ch === "}") {
1230
+ depth = Math.max(0, depth - 1);
1231
+ i++;
1232
+ continue;
1113
1233
  }
1234
+ if (ch === "{") {
1235
+ depth++;
1236
+ i++;
1237
+ continue;
1238
+ }
1239
+ // At top level, match a block header before `{`. Handles both the bare
1240
+ // form (`workspace {`) and the typed-block form (`store brain {`,
1241
+ // `retrieval smart {`) — in the latter the FIRST token is the keyword.
1242
+ if (depth === 0) {
1243
+ const rest = body.slice(i);
1244
+ const m = /^([a-zA-Z][a-zA-Z0-9_-]*)(?:\s+[a-zA-Z][a-zA-Z0-9_-]*)?\s*\{/.exec(rest);
1245
+ if (m && (i === 0 || /\s/.test(body[i - 1]))) {
1246
+ const name = m[1]; // the leading keyword (store / retrieval / workspace / …)
1247
+ if (!knownSubsSet.has(name)) {
1248
+ _unknownSubBlockWarnings.push({
1249
+ rule: "V24",
1250
+ level: "warning",
1251
+ message: `Unknown sub-block "${name}" in memory block — known sub-blocks are: ${knownSubs.join(", ")}`,
1252
+ });
1253
+ }
1254
+ // Advance into the block (past the `{`) so its body is depth-counted.
1255
+ i += m[0].length;
1256
+ depth++;
1257
+ continue;
1258
+ }
1259
+ }
1260
+ i++;
1114
1261
  }
1115
1262
  }
1116
1263
  return { memory, stores, retrievals };
@@ -2026,7 +2173,11 @@ export function parse(source) {
2026
2173
  const topBody = stripComments(topoBlock.body);
2027
2174
  // --- Meta ---
2028
2175
  const metaBlock = extractBlock(topBody, "meta");
2176
+ // V89/V90: collected while parsing blocks — a field written in a form the
2177
+ // grammar does not define. Attached to the AST below.
2178
+ const blockFieldMisuseWarnings = [];
2029
2179
  const metaFields = metaBlock ? parseMeta(metaBlock.body) : {};
2180
+ detectSwallowedFields(metaFields, "<meta>", blockFieldMisuseWarnings);
2030
2181
  const topology = {
2031
2182
  name: header.name,
2032
2183
  version: metaFields.version ?? "0.0.0",
@@ -2047,20 +2198,20 @@ export function parse(source) {
2047
2198
  // Orchestrator
2048
2199
  const orchBlock = extractBlock(topBody, "orchestrator");
2049
2200
  if (orchBlock) {
2050
- nodes.push(parseOrchestrator(orchBlock.body));
2201
+ nodes.push(parseOrchestrator(orchBlock.body, blockFieldMisuseWarnings));
2051
2202
  }
2052
2203
  // Actions
2053
2204
  const actionBlocks = extractAllBlocks(topBody, "action");
2054
2205
  for (const block of actionBlocks) {
2055
2206
  if (block.id) {
2056
- nodes.push(parseAction(block.id, block.body));
2207
+ nodes.push(parseAction(block.id, block.body, blockFieldMisuseWarnings));
2057
2208
  }
2058
2209
  }
2059
2210
  // Agents
2060
2211
  const agentBlocks = extractAllBlocks(topBody, "agent");
2061
2212
  for (const block of agentBlocks) {
2062
2213
  if (block.id) {
2063
- nodes.push(parseAgent(block.id, block.body, roles));
2214
+ nodes.push(parseAgent(block.id, block.body, roles, blockFieldMisuseWarnings));
2064
2215
  }
2065
2216
  }
2066
2217
  // Gates
@@ -2069,7 +2220,7 @@ export function parse(source) {
2069
2220
  const gateBlocks = extractAllBlocks(gatesBlock.body, "gate");
2070
2221
  for (const block of gateBlocks) {
2071
2222
  if (block.id) {
2072
- nodes.push(parseGate(block.id, block.body));
2223
+ nodes.push(parseGate(block.id, block.body, blockFieldMisuseWarnings));
2073
2224
  }
2074
2225
  }
2075
2226
  }
@@ -2077,14 +2228,14 @@ export function parse(source) {
2077
2228
  const humanBlocks = extractAllBlocks(topBody, "human");
2078
2229
  for (const block of humanBlocks) {
2079
2230
  if (block.id) {
2080
- nodes.push(parseHuman(block.id, block.body));
2231
+ nodes.push(parseHuman(block.id, block.body, blockFieldMisuseWarnings));
2081
2232
  }
2082
2233
  }
2083
2234
  // Group chat nodes
2084
2235
  const groupBlocks = extractAllBlocks(topBody, "group");
2085
2236
  for (const block of groupBlocks) {
2086
2237
  if (block.id) {
2087
- nodes.push(parseGroup(block.id, block.body));
2238
+ nodes.push(parseGroup(block.id, block.body, blockFieldMisuseWarnings));
2088
2239
  }
2089
2240
  }
2090
2241
  // --- Skills ---
@@ -2265,6 +2416,10 @@ export function parse(source) {
2265
2416
  if (unknownMemorySubBlockWarnings.length > 0) {
2266
2417
  ast._unknownMemorySubBlockWarnings = unknownMemorySubBlockWarnings;
2267
2418
  }
2419
+ // Attach V89 block-vs-KV field misuse errors for the validator to consume.
2420
+ if (blockFieldMisuseWarnings.length > 0) {
2421
+ ast._blockFieldMisuseWarnings = blockFieldMisuseWarnings;
2422
+ }
2268
2423
  // Attach source map for line number tracking in validation results.
2269
2424
  if (Object.keys(sourceMap).length > 0) {
2270
2425
  ast._sourceMap = sourceMap;