@intentius/chant 0.49.0 → 0.51.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 (294) hide show
  1. package/dist/audit/catalog.d.ts +13 -3
  2. package/dist/audit/catalog.d.ts.map +1 -1
  3. package/dist/audit/core.d.ts +9 -0
  4. package/dist/audit/core.d.ts.map +1 -1
  5. package/dist/audit/discover.d.ts +6 -0
  6. package/dist/audit/discover.d.ts.map +1 -1
  7. package/dist/audit/fetch.d.ts.map +1 -1
  8. package/dist/audit/report-html.d.ts.map +1 -1
  9. package/dist/audit/report-model.d.ts +6 -0
  10. package/dist/audit/report-model.d.ts.map +1 -1
  11. package/dist/audit/report.d.ts.map +1 -1
  12. package/dist/audit/rules-doc.d.ts.map +1 -1
  13. package/dist/audit/secrets.d.ts +95 -0
  14. package/dist/audit/secrets.d.ts.map +1 -0
  15. package/dist/audit/wrangler.d.ts +33 -0
  16. package/dist/audit/wrangler.d.ts.map +1 -0
  17. package/dist/build.d.ts.map +1 -1
  18. package/dist/cli/commands/audit.d.ts +7 -0
  19. package/dist/cli/commands/audit.d.ts.map +1 -1
  20. package/dist/cli/commands/build.d.ts +23 -0
  21. package/dist/cli/commands/build.d.ts.map +1 -1
  22. package/dist/cli/handlers/build.d.ts.map +1 -1
  23. package/dist/cli/handlers/components.d.ts +31 -0
  24. package/dist/cli/handlers/components.d.ts.map +1 -1
  25. package/dist/cli/handlers/lifecycle.d.ts +11 -0
  26. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  27. package/dist/cli/handlers/op-progress.d.ts +57 -0
  28. package/dist/cli/handlers/op-progress.d.ts.map +1 -0
  29. package/dist/cli/handlers/operator.d.ts +32 -0
  30. package/dist/cli/handlers/operator.d.ts.map +1 -0
  31. package/dist/cli/handlers/run-client.d.ts +21 -1
  32. package/dist/cli/handlers/run-client.d.ts.map +1 -1
  33. package/dist/cli/handlers/run-report.d.ts.map +1 -1
  34. package/dist/cli/handlers/run.d.ts.map +1 -1
  35. package/dist/cli/handlers/scenario.d.ts +39 -0
  36. package/dist/cli/handlers/scenario.d.ts.map +1 -0
  37. package/dist/cli/handlers/search.d.ts +22 -0
  38. package/dist/cli/handlers/search.d.ts.map +1 -1
  39. package/dist/cli/main.d.ts.map +1 -1
  40. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  41. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
  42. package/dist/cli/mcp/server.d.ts +35 -2
  43. package/dist/cli/mcp/server.d.ts.map +1 -1
  44. package/dist/cli/mcp/types.d.ts +29 -1
  45. package/dist/cli/mcp/types.d.ts.map +1 -1
  46. package/dist/cli/registry.d.ts +47 -3
  47. package/dist/cli/registry.d.ts.map +1 -1
  48. package/dist/codegen/docs-rule-scanning.d.ts.map +1 -1
  49. package/dist/components/capability.d.ts +17 -2
  50. package/dist/components/capability.d.ts.map +1 -1
  51. package/dist/components/cli-support.d.ts +7 -0
  52. package/dist/components/cli-support.d.ts.map +1 -1
  53. package/dist/components/component.d.ts +15 -0
  54. package/dist/components/component.d.ts.map +1 -1
  55. package/dist/components/driver.d.ts.map +1 -1
  56. package/dist/components/run-progress.d.ts +7 -5
  57. package/dist/components/run-progress.d.ts.map +1 -1
  58. package/dist/components/verbs/index.d.ts +6 -1
  59. package/dist/components/verbs/index.d.ts.map +1 -1
  60. package/dist/components/verbs/run-agent.d.ts +499 -0
  61. package/dist/components/verbs/run-agent.d.ts.map +1 -0
  62. package/dist/components/verbs/sign.d.ts +30 -0
  63. package/dist/components/verbs/sign.d.ts.map +1 -1
  64. package/dist/composite.d.ts +6 -1
  65. package/dist/composite.d.ts.map +1 -1
  66. package/dist/discovery/collect.d.ts.map +1 -1
  67. package/dist/discovery/fold-import.d.ts +15 -1
  68. package/dist/discovery/fold-import.d.ts.map +1 -1
  69. package/dist/discovery/fold-rank.d.ts +66 -0
  70. package/dist/discovery/fold-rank.d.ts.map +1 -0
  71. package/dist/discovery/index.d.ts +15 -0
  72. package/dist/discovery/index.d.ts.map +1 -1
  73. package/dist/discovery/param-deps.d.ts +17 -0
  74. package/dist/discovery/param-deps.d.ts.map +1 -0
  75. package/dist/fold/fold.d.ts +55 -2
  76. package/dist/fold/fold.d.ts.map +1 -1
  77. package/dist/fold/subset.d.ts +21 -14
  78. package/dist/fold/subset.d.ts.map +1 -1
  79. package/dist/lexicon-schema.d.ts +2 -0
  80. package/dist/lexicon-schema.d.ts.map +1 -1
  81. package/dist/lexicon.d.ts +134 -0
  82. package/dist/lexicon.d.ts.map +1 -1
  83. package/dist/lifecycle/assert-live.d.ts +77 -0
  84. package/dist/lifecycle/assert-live.d.ts.map +1 -0
  85. package/dist/lifecycle/change-set.d.ts +17 -0
  86. package/dist/lifecycle/change-set.d.ts.map +1 -1
  87. package/dist/lifecycle/converge-ledger.d.ts +90 -0
  88. package/dist/lifecycle/converge-ledger.d.ts.map +1 -0
  89. package/dist/lifecycle/deep-diff.d.ts +18 -0
  90. package/dist/lifecycle/deep-diff.d.ts.map +1 -1
  91. package/dist/lifecycle/deep-observe.d.ts +9 -1
  92. package/dist/lifecycle/deep-observe.d.ts.map +1 -1
  93. package/dist/lifecycle/disruption.d.ts +96 -0
  94. package/dist/lifecycle/disruption.d.ts.map +1 -0
  95. package/dist/lifecycle/gate-ledger.d.ts +33 -0
  96. package/dist/lifecycle/gate-ledger.d.ts.map +1 -0
  97. package/dist/lifecycle/git.d.ts +145 -21
  98. package/dist/lifecycle/git.d.ts.map +1 -1
  99. package/dist/lifecycle/index.d.ts +6 -0
  100. package/dist/lifecycle/index.d.ts.map +1 -1
  101. package/dist/lifecycle/lease.d.ts +113 -0
  102. package/dist/lifecycle/lease.d.ts.map +1 -0
  103. package/dist/lifecycle/replay.d.ts +2 -0
  104. package/dist/lifecycle/replay.d.ts.map +1 -1
  105. package/dist/lifecycle/scenario-eval.d.ts +42 -0
  106. package/dist/lifecycle/scenario-eval.d.ts.map +1 -0
  107. package/dist/lifecycle/scenario.d.ts +163 -0
  108. package/dist/lifecycle/scenario.d.ts.map +1 -0
  109. package/dist/lifecycle/symptoms.d.ts +63 -0
  110. package/dist/lifecycle/symptoms.d.ts.map +1 -0
  111. package/dist/lint/output-docs.d.ts +94 -0
  112. package/dist/lint/output-docs.d.ts.map +1 -0
  113. package/dist/lint/post-synth.d.ts +29 -0
  114. package/dist/lint/post-synth.d.ts.map +1 -1
  115. package/dist/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.d.ts +11 -0
  116. package/dist/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.d.ts.map +1 -0
  117. package/dist/lsp/lexicon-providers.d.ts +7 -0
  118. package/dist/lsp/lexicon-providers.d.ts.map +1 -1
  119. package/dist/op/activity-contract.d.ts +139 -0
  120. package/dist/op/activity-contract.d.ts.map +1 -0
  121. package/dist/op/builders.d.ts +42 -2
  122. package/dist/op/builders.d.ts.map +1 -1
  123. package/dist/op/converge-rule.d.ts +161 -0
  124. package/dist/op/converge-rule.d.ts.map +1 -0
  125. package/dist/op/generate-pipeline.d.ts +39 -0
  126. package/dist/op/generate-pipeline.d.ts.map +1 -0
  127. package/dist/op/index.d.ts +14 -0
  128. package/dist/op/index.d.ts.map +1 -1
  129. package/dist/op/local-executor.d.ts +7 -1
  130. package/dist/op/local-executor.d.ts.map +1 -1
  131. package/dist/op/op-verb-class.d.ts +42 -0
  132. package/dist/op/op-verb-class.d.ts.map +1 -0
  133. package/dist/op/operator.d.ts +128 -0
  134. package/dist/op/operator.d.ts.map +1 -0
  135. package/dist/op/step-output-ref.d.ts +187 -0
  136. package/dist/op/step-output-ref.d.ts.map +1 -0
  137. package/dist/op/types.d.ts +18 -1
  138. package/dist/op/types.d.ts.map +1 -1
  139. package/dist/provenance.d.ts +73 -3
  140. package/dist/provenance.d.ts.map +1 -1
  141. package/dist/runtime-adapter.d.ts +7 -1
  142. package/dist/runtime-adapter.d.ts.map +1 -1
  143. package/dist/serializer.d.ts +18 -0
  144. package/dist/serializer.d.ts.map +1 -1
  145. package/dist/testing.d.ts +23 -2
  146. package/dist/testing.d.ts.map +1 -1
  147. package/dist/toml.d.ts +40 -5
  148. package/dist/toml.d.ts.map +1 -1
  149. package/package.json +1 -1
  150. package/src/audit/catalog.test.ts +1 -1
  151. package/src/audit/catalog.ts +75 -3
  152. package/src/audit/core.ts +9 -0
  153. package/src/audit/discover.ts +29 -2
  154. package/src/audit/fetch.test.ts +216 -3
  155. package/src/audit/fetch.ts +270 -59
  156. package/src/audit/report-html.ts +5 -2
  157. package/src/audit/report-model.ts +9 -0
  158. package/src/audit/report.test.ts +22 -0
  159. package/src/audit/report.ts +3 -2
  160. package/src/audit/rules-doc.ts +2 -0
  161. package/src/audit/secrets.test.ts +303 -0
  162. package/src/audit/secrets.ts +406 -0
  163. package/src/audit/wrangler.test.ts +230 -0
  164. package/src/audit/wrangler.ts +290 -0
  165. package/src/build.ts +8 -3
  166. package/src/cli/command-group.ts +1 -1
  167. package/src/cli/commands/__fixtures__/schemas/sarif-2.1.0.schema.json +2882 -0
  168. package/src/cli/commands/audit.test.ts +215 -1
  169. package/src/cli/commands/audit.ts +86 -17
  170. package/src/cli/commands/build.test.ts +167 -2
  171. package/src/cli/commands/build.ts +114 -23
  172. package/src/cli/handlers/build.ts +2 -0
  173. package/src/cli/handlers/components.test.ts +199 -1
  174. package/src/cli/handlers/components.ts +160 -3
  175. package/src/cli/handlers/graph.test.ts +20 -0
  176. package/src/cli/handlers/graph.ts +10 -1
  177. package/src/cli/handlers/lifecycle.test.ts +90 -0
  178. package/src/cli/handlers/lifecycle.ts +30 -5
  179. package/src/cli/handlers/op-progress.test.ts +202 -0
  180. package/src/cli/handlers/op-progress.ts +192 -0
  181. package/src/cli/handlers/operator.test.ts +255 -0
  182. package/src/cli/handlers/operator.ts +240 -0
  183. package/src/cli/handlers/run-client.test.ts +82 -0
  184. package/src/cli/handlers/run-client.ts +85 -2
  185. package/src/cli/handlers/run-report.test.ts +62 -0
  186. package/src/cli/handlers/run-report.ts +20 -58
  187. package/src/cli/handlers/run.test.ts +144 -0
  188. package/src/cli/handlers/run.ts +40 -18
  189. package/src/cli/handlers/scenario.test.ts +456 -0
  190. package/src/cli/handlers/scenario.ts +330 -0
  191. package/src/cli/handlers/search-drift.test.ts +263 -0
  192. package/src/cli/handlers/search.ts +150 -1
  193. package/src/cli/main.test.ts +23 -0
  194. package/src/cli/main.ts +81 -1
  195. package/src/cli/mcp/op-tools.ts +17 -6
  196. package/src/cli/mcp/resource-handlers.ts +13 -5
  197. package/src/cli/mcp/server.test.ts +265 -2
  198. package/src/cli/mcp/server.ts +84 -7
  199. package/src/cli/mcp/types.ts +27 -1
  200. package/src/cli/registry.ts +47 -3
  201. package/src/codegen/docs-rule-scanning.test.ts +42 -0
  202. package/src/codegen/docs-rule-scanning.ts +25 -2
  203. package/src/components/README.md +7 -0
  204. package/src/components/capability.ts +17 -2
  205. package/src/components/cli-support.test.ts +17 -0
  206. package/src/components/cli-support.ts +13 -1
  207. package/src/components/component-schema.test.ts +32 -0
  208. package/src/components/component.schema.json +6 -0
  209. package/src/components/component.test.ts +21 -0
  210. package/src/components/component.ts +15 -0
  211. package/src/components/driver.ts +12 -4
  212. package/src/components/run-progress.ts +9 -5
  213. package/src/components/verbs/index.ts +6 -1
  214. package/src/components/verbs/run-agent.test.ts +683 -0
  215. package/src/components/verbs/run-agent.ts +786 -0
  216. package/src/components/verbs/sign.test.ts +19 -0
  217. package/src/components/verbs/sign.ts +34 -2
  218. package/src/composite.ts +31 -2
  219. package/src/discovery/collect.ts +11 -2
  220. package/src/discovery/fold-import.test.ts +54 -0
  221. package/src/discovery/fold-import.ts +178 -38
  222. package/src/discovery/fold-rank.test.ts +197 -0
  223. package/src/discovery/fold-rank.ts +346 -0
  224. package/src/discovery/index.ts +16 -1
  225. package/src/discovery/param-deps.test.ts +118 -0
  226. package/src/discovery/param-deps.ts +170 -0
  227. package/src/fold/fold.test.ts +6 -2
  228. package/src/fold/fold.ts +184 -3
  229. package/src/fold/subset.test.ts +82 -19
  230. package/src/fold/subset.ts +79 -41
  231. package/src/lexicon-schema.ts +3 -0
  232. package/src/lexicon.ts +154 -2
  233. package/src/lifecycle/assert-live.test.ts +125 -0
  234. package/src/lifecycle/assert-live.ts +154 -0
  235. package/src/lifecycle/change-set.ts +35 -3
  236. package/src/lifecycle/converge-ledger.test.ts +199 -0
  237. package/src/lifecycle/converge-ledger.ts +179 -0
  238. package/src/lifecycle/deep-diff.test.ts +79 -1
  239. package/src/lifecycle/deep-diff.ts +23 -0
  240. package/src/lifecycle/deep-observe.ts +13 -2
  241. package/src/lifecycle/disruption.test.ts +186 -0
  242. package/src/lifecycle/disruption.ts +224 -0
  243. package/src/lifecycle/gate-ledger.test.ts +103 -0
  244. package/src/lifecycle/gate-ledger.ts +140 -0
  245. package/src/lifecycle/git.test.ts +430 -0
  246. package/src/lifecycle/git.ts +446 -84
  247. package/src/lifecycle/index.ts +6 -0
  248. package/src/lifecycle/lease.test.ts +343 -0
  249. package/src/lifecycle/lease.ts +270 -0
  250. package/src/lifecycle/replay.test.ts +25 -0
  251. package/src/lifecycle/replay.ts +11 -3
  252. package/src/lifecycle/scenario-eval.test.ts +199 -0
  253. package/src/lifecycle/scenario-eval.ts +158 -0
  254. package/src/lifecycle/scenario.test.ts +195 -0
  255. package/src/lifecycle/scenario.ts +321 -0
  256. package/src/lifecycle/symptoms.test.ts +116 -0
  257. package/src/lifecycle/symptoms.ts +126 -0
  258. package/src/lint/output-docs.test.ts +220 -0
  259. package/src/lint/output-docs.ts +204 -0
  260. package/src/lint/post-synth.test.ts +97 -0
  261. package/src/lint/post-synth.ts +45 -0
  262. package/src/lint/rules/__fixtures__/comp/comp003/pass/agent-turn.component.ts +26 -0
  263. package/src/lint/rules/comp/comp.test.ts +49 -1
  264. package/src/lint/rules/evl001-non-literal-expression.test.ts +8 -3
  265. package/src/lint/rules/evl001-non-literal-expression.ts +6 -6
  266. package/src/lsp/lexicon-providers.test.ts +44 -0
  267. package/src/lsp/lexicon-providers.ts +11 -1
  268. package/src/op/activity-contract.test.ts +180 -0
  269. package/src/op/activity-contract.ts +278 -0
  270. package/src/op/builders-exports.test.ts +17 -1
  271. package/src/op/builders.ts +59 -5
  272. package/src/op/converge-rule.test.ts +179 -0
  273. package/src/op/converge-rule.ts +311 -0
  274. package/src/op/generate-pipeline.test.ts +53 -0
  275. package/src/op/generate-pipeline.ts +99 -0
  276. package/src/op/index.ts +30 -0
  277. package/src/op/local-executor.test.ts +92 -0
  278. package/src/op/local-executor.ts +52 -10
  279. package/src/op/local-output.ts +1 -1
  280. package/src/op/op-verb-class.test.ts +126 -0
  281. package/src/op/op-verb-class.ts +115 -0
  282. package/src/op/operator.test.ts +346 -0
  283. package/src/op/operator.ts +213 -0
  284. package/src/op/step-output-ref.test.ts +334 -0
  285. package/src/op/step-output-ref.ts +453 -0
  286. package/src/op/types.ts +18 -1
  287. package/src/provenance.test.ts +151 -4
  288. package/src/provenance.ts +118 -4
  289. package/src/runtime-adapter.ts +31 -10
  290. package/src/serializer.ts +18 -0
  291. package/src/testing.test.ts +89 -2
  292. package/src/testing.ts +63 -3
  293. package/src/toml.test.ts +157 -384
  294. package/src/toml.ts +371 -5
@@ -44,8 +44,23 @@ export interface Capability<In = unknown, Out = unknown> {
44
44
  readonly kind: string;
45
45
  /** Perform the operation. */
46
46
  run(ctx: DeployContext, input: In): Promise<Out>;
47
- /** Optional paired compensation, invoked in reverse order on saga rollback. */
48
- rollback?(ctx: DeployContext, input: In): Promise<void>;
47
+ /**
48
+ * Optional paired compensation, invoked in reverse order on saga rollback.
49
+ * `output`, when supplied, is the exact value this step's own `run()` call
50
+ * returned (#1944, epic #1564 phase 4) — a serializable identity channel a
51
+ * capability can use to recover state `rollback` needs when it cannot rely
52
+ * on in-process object identity between its `run`/`rollback` calls. The
53
+ * local interpret driver (../driver.ts) always threads it through; the
54
+ * durable Temporal path (lexicons/temporal/src/component-op/*.ts) threads
55
+ * it across the Activity boundary as plain JSON, which is exactly the case
56
+ * this exists for — see ./verbs/run-agent.ts's "Rollback identity" doc
57
+ * comment for the motivating gap (a fresh sprite's checkpoint id, recorded
58
+ * only in an in-process `WeakMap` keyed by `run()`'s `input` object, never
59
+ * survives to a `rollback()` call that runs as a separate Activity with its
60
+ * own freshly-resolved `input`). Optional and additive: a capability that
61
+ * never needs it (most of them) simply ignores the third parameter.
62
+ */
63
+ rollback?(ctx: DeployContext, input: In, output?: Out): Promise<void>;
49
64
  /**
50
65
  * How this verb relates to rollback, for the COMP003 composition check.
51
66
  * Usually derivable and left unset: a capability with a `rollback` method is
@@ -371,6 +371,23 @@ export const backup =
371
371
  expect(result.liveNames?.postgres).toEqual(["pgDeployment", "pgService", "pgClaim"]);
372
372
  expect(result.liveNames?.plain).toEqual(["plain"]);
373
373
  });
374
+
375
+ test("carries each component's declared composites, absent (not defaulted) when undeclared (#1492)", async () => {
376
+ await writeFile(
377
+ join(testDir, "aws-plane.component.ts"),
378
+ `export const awsPlane = { name: "aws-plane", composites: ["ArtifactBucket", "OperatorRole"], dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "shell", reason: "test" }] }] };`,
379
+ );
380
+ await writeFile(
381
+ join(testDir, "plain.component.ts"),
382
+ `export const plain = { name: "plain", dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "shell", reason: "test" }] }] };`,
383
+ );
384
+
385
+ const result = await computeComponentGraph(testDir);
386
+
387
+ expect(result.success).toBe(true);
388
+ expect(result.composites?.["aws-plane"]).toEqual(["ArtifactBucket", "OperatorRole"]);
389
+ expect(result.composites?.plain).toBeUndefined();
390
+ });
374
391
  });
375
392
 
376
393
  // ── runComponents (#585) ─────────────────────────────────────────────────────
@@ -141,6 +141,13 @@ export interface ComponentGraphResult {
141
141
  * `Component` contract already specifies. This is what lets a consumer join
142
142
  * the component DAG to the resource graph without guessing at kinds. */
143
143
  liveNames?: Record<string, string[]>;
144
+ /** Component name → the composite kind name(s) its resources come from (#1492),
145
+ * present only for components that declared `composites` explicitly. Unlike
146
+ * `liveNames`, there is no identity fallback — a component's own name is not
147
+ * a composite kind — so an undeclared component is left out of this map
148
+ * entirely rather than defaulting to a guess; a consumer with no entry here
149
+ * keeps applying its own naming-convention default. */
150
+ composites?: Record<string, string[]>;
144
151
  error?: string;
145
152
  }
146
153
 
@@ -173,10 +180,15 @@ export async function computeComponentGraph(
173
180
  // component name → owned live resource names (#1491), declared or the
174
181
  // contract's identity fallback.
175
182
  const liveNames: Record<string, string[]> = {};
183
+ // component name → declared composite kind(s) (#1492), only for components
184
+ // that declared them — no identity fallback (see ComponentGraphResult doc).
185
+ const composites: Record<string, string[]> = {};
176
186
  for (const [name, discovered] of result.components) {
177
187
  files[name] = relative(path, discovered.filePath);
178
188
  const declared = discovered.component.liveNames;
179
189
  liveNames[name] = declared && declared.length > 0 ? [...declared] : [name];
190
+ const declaredComposites = discovered.component.composites;
191
+ if (declaredComposites && declaredComposites.length > 0) composites[name] = [...declaredComposites];
180
192
  }
181
193
 
182
194
  try {
@@ -185,7 +197,7 @@ export async function computeComponentGraph(
185
197
  for (const c of driverComponents) {
186
198
  for (const dep of c.dependsOn ?? []) edges.push({ from: c.name, to: dep });
187
199
  }
188
- return { success: true, order, waves, edges, files, liveNames };
200
+ return { success: true, order, waves, edges, files, liveNames, composites };
189
201
  } catch (err) {
190
202
  if (err instanceof UnknownDependencyError || err instanceof DependencyCycleError) {
191
203
  return { success: false, order: [], waves: [], edges: [], error: err.message };
@@ -143,6 +143,38 @@ describe("Component JSON Schema", () => {
143
143
  expect(validate(invalid)).toBe(false);
144
144
  });
145
145
 
146
+ it("accepts an optional composites list (#1492)", () => {
147
+ const withComposites = {
148
+ name: "aws-plane",
149
+ archetype: "infra",
150
+ dependsOn: [],
151
+ composites: ["ArtifactBucket", "OperatorRole"],
152
+ deploy: [{ phase: "Apply", steps: [{ kind: "cfn-deploy", template: "t.json" }] }],
153
+ };
154
+ expect(validate(withComposites)).toBe(true);
155
+ });
156
+
157
+ it("omitting composites still validates (backward-compatible default)", () => {
158
+ const noComposites = {
159
+ name: "table",
160
+ archetype: "infra",
161
+ dependsOn: [],
162
+ deploy: [{ phase: "Apply", steps: [{ kind: "cfn-deploy", template: "t.json" }] }],
163
+ };
164
+ expect(validate(noComposites)).toBe(true);
165
+ });
166
+
167
+ it("rejects an empty composites array (minItems: 1)", () => {
168
+ const invalid = {
169
+ name: "table",
170
+ archetype: "infra",
171
+ dependsOn: [],
172
+ composites: [],
173
+ deploy: [{ phase: "Apply", steps: [{ kind: "cfn-deploy", template: "t.json" }] }],
174
+ };
175
+ expect(validate(invalid)).toBe(false);
176
+ });
177
+
146
178
  it("rejects an invalid archetype value", () => {
147
179
  const invalid = {
148
180
  name: "x",
@@ -54,6 +54,12 @@
54
54
  "description": "Optional: the live lexicon entity/resource name(s) this component owns, when they differ from `name` (#598). `chant components status --live` reconciles the release ledger against live evidence by joining on this name; omitted entirely (the default), the join falls back to `name` itself with no behavior change. A component that owns several live entities (e.g. a fan-out cluster with one stack per node) may list them all.",
55
55
  "items": { "type": "string", "minLength": 1 },
56
56
  "minItems": 1
57
+ },
58
+ "composites": {
59
+ "type": "array",
60
+ "description": "Optional: the composite kind name(s) this component's deployed resources come from, when a consumer cannot infer it from `name` (#1492). Without this, a consumer joining a composite graph to the component DAG guesses by kebab-casing each composite's kind and matching it against this component's `name`, which only holds when a component maps 1:1 to a composite named for it. Omitted entirely (the default), the naming convention stays in effect with no behavior change. A component built from several composites may list them all.",
61
+ "items": { "type": "string", "minLength": 1 },
62
+ "minItems": 1
57
63
  }
58
64
  },
59
65
  "$defs": {
@@ -202,6 +202,27 @@ describe("projectToJson()", () => {
202
202
  expect("liveNames" in projected).toBe(false);
203
203
  });
204
204
 
205
+ it("projects an explicit composites list through unchanged (#1492)", () => {
206
+ const component: Component = {
207
+ name: "aws-plane",
208
+ dependsOn: [],
209
+ composites: ["ArtifactBucket", "OperatorRole"],
210
+ deploy: [phase("Apply", [{ kind: "cfn-deploy" }])],
211
+ };
212
+ const projected = projectToJson(component) as { composites?: string[] };
213
+ expect(projected.composites).toEqual(["ArtifactBucket", "OperatorRole"]);
214
+ });
215
+
216
+ it("omits composites from the projection when not authored (#1492)", () => {
217
+ const component: Component = {
218
+ name: "table",
219
+ dependsOn: [],
220
+ deploy: [phase("Apply", [{ kind: "cfn-deploy" }])],
221
+ };
222
+ const projected = projectToJson(component) as Record<string, unknown>;
223
+ expect("composites" in projected).toBe(false);
224
+ });
225
+
205
226
  it("projects a stackOutput() wiring reference to its plain-JSON form", () => {
206
227
  const component: Component = {
207
228
  name: "svc",
@@ -148,6 +148,21 @@ export interface Component {
148
148
  * name == entity join with no behavior change.
149
149
  */
150
150
  liveNames?: string[];
151
+ /**
152
+ * Optional: the composite kind name(s) this component's deployed resources
153
+ * come from, when a consumer joining a composite graph to the component DAG
154
+ * cannot infer it from `name` (#1492). Components and composites are
155
+ * different namespaces, the same class of mismatch `liveNames` covers for
156
+ * components and lexicon entities: a consumer without this field guesses by
157
+ * kebab-casing each composite's kind and matching it against this
158
+ * component's `name` (`LoomBackend` -> `loom-backend`), which only holds
159
+ * when a component maps 1:1 to a composite named for it. A component built
160
+ * from several composites (e.g. one stack containing both an ArtifactBucket
161
+ * and an OperatorRole composite, plus loose resources) lists them all.
162
+ * Omitted entirely (the common case), the naming convention stays the
163
+ * default and nothing changes.
164
+ */
165
+ composites?: string[];
151
166
  }
152
167
 
153
168
  /** Author a named phase. `steps` may mix capability `Step`s, `Gate`s, and nested `Phase`s (fan-out). */
@@ -313,11 +313,19 @@ export function resolveStepInput(
313
313
 
314
314
  // ── Step + phase execution ───────────────────────────────────────────────
315
315
 
316
- /** A step executed and its resolved input, kept so saga rollback can call the same capability's `rollback` with the same input. */
316
+ /**
317
+ * A step executed and its resolved input, kept so saga rollback can call the
318
+ * same capability's `rollback` with the same input. `output` is the step's
319
+ * own `run()` result — threaded through to `rollback` (#1944) as the
320
+ * serializable identity channel `Capability.rollback`'s doc comment
321
+ * describes, alongside object identity (which already works here, since this
322
+ * driver runs entirely in-process — see this module's own doc comment).
323
+ */
317
324
  interface ExecutedStep {
318
325
  step: DriverStep;
319
326
  resolvedInput: Record<string, unknown>;
320
327
  phaseName: string;
328
+ output: unknown;
321
329
  }
322
330
 
323
331
  class StepFailure extends Error {
@@ -435,7 +443,7 @@ async function runPhase(
435
443
  }
436
444
  return {
437
445
  records: [record],
438
- executed: record.status === "ok" ? [{ step: entry, resolvedInput, phaseName: phaseDef.phase }] : [],
446
+ executed: record.status === "ok" ? [{ step: entry, resolvedInput, phaseName: phaseDef.phase, output }] : [],
439
447
  failed: record.status === "fail",
440
448
  };
441
449
  };
@@ -505,7 +513,7 @@ async function rollbackExecuted(
505
513
  registry: CapabilityRegistry,
506
514
  ): Promise<DriverStepRecord[]> {
507
515
  const records: DriverStepRecord[] = [];
508
- for (const { step, resolvedInput, phaseName } of [...executed].reverse()) {
516
+ for (const { step, resolvedInput, phaseName, output } of [...executed].reverse()) {
509
517
  const start = Date.now();
510
518
  try {
511
519
  const capability = registry.resolve(step.kind);
@@ -521,7 +529,7 @@ async function rollbackExecuted(
521
529
  });
522
530
  continue;
523
531
  }
524
- await capability.rollback(ctx, resolvedInput as never);
532
+ await capability.rollback(ctx, resolvedInput as never, output as never);
525
533
  records.push({
526
534
  component: ctx.component,
527
535
  phase: phaseName,
@@ -107,11 +107,15 @@ export type RunProgressSink = (event: RunProgressEvent) => void;
107
107
  /**
108
108
  * Build a sink that writes `JSON.stringify(event) + "\n"` to `write` (default:
109
109
  * `process.stdout.write`), one line per event, as they happen — the
110
- * `--progress-json` CLI wiring's sink (../cli/handlers/run.ts). Kept separate
111
- * from `driver-output.ts`'s end-of-run renderers: this emits *during* the
112
- * run, one line at a time; `renderDriverJson`/`renderDriverHuman` render the
113
- * completed `DriverRunResult` once, after the run finishes.
110
+ * `--progress-json` CLI wiring's sink (../cli/handlers/run.ts, for both the
111
+ * `--components` driver's `RunProgressEvent`s and the Temporal Op path's
112
+ * `StepRecord`s, chant #1676). Kept separate from `driver-output.ts`'s
113
+ * end-of-run renderers: this emits *during* the run, one line at a time;
114
+ * `renderDriverJson`/`renderDriverHuman` render the completed
115
+ * `DriverRunResult` once, after the run finishes.
114
116
  */
115
- export function ndjsonProgressSink(write: (chunk: string) => void = (s) => void process.stdout.write(s)): RunProgressSink {
117
+ export function ndjsonProgressSink<T = RunProgressEvent>(
118
+ write: (chunk: string) => void = (s) => void process.stdout.write(s),
119
+ ): (event: T) => void {
116
120
  return (event) => write(JSON.stringify(event) + "\n");
117
121
  }
@@ -30,7 +30,11 @@
30
30
  * ./wrangler.ts) and `r2-sync` (#1293, ./r2-sync.ts) are the Cloudflare
31
31
  * Workers apply leaves — real implementations over the same injectable
32
32
  * `ProcessRunner` `sign` uses, shelling to `wrangler`/`rclone` rather than an
33
- * AWS-shaped SDK client.
33
+ * AWS-shaped SDK client. `run-agent` (#1941, epic #1564 phase 1, ./run-agent.ts)
34
+ * is the typed schema + registry entry for a bounded, attested agent-turn
35
+ * capability over a fly Sprite — a typed stub for now (`run`/`rollback`
36
+ * reject with `CapabilityNotImplementedError`), `rollbackPolicy: "native"`
37
+ * declared up front; the real sprite-lifecycle implementation is #1942.
34
38
  */
35
39
 
36
40
  export * from "./build-archive";
@@ -44,6 +48,7 @@ export * from "./component-bom";
44
48
  export * from "./reproducibility";
45
49
  export * from "./sign";
46
50
  export * from "./verify";
51
+ export * from "./run-agent";
47
52
  export * from "./wrangler";
48
53
  export * from "./r2-sync";
49
54
  export * from "./vuln-scan";