@nodetool-ai/cli-harness 0.0.0-stage → 0.8.9-beta.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 (59) hide show
  1. package/README.md +13 -2
  2. package/dist/.nodetool-build-stamp +1 -0
  3. package/dist/affected/affected.d.ts +48 -0
  4. package/dist/affected/affected.d.ts.map +1 -0
  5. package/dist/affected/affected.js +167 -0
  6. package/dist/affected/affected.js.map +1 -0
  7. package/dist/commands/__tests__/jtbd.test.d.ts +2 -0
  8. package/dist/commands/__tests__/jtbd.test.d.ts.map +1 -0
  9. package/dist/commands/__tests__/jtbd.test.js +120 -0
  10. package/dist/commands/__tests__/jtbd.test.js.map +1 -0
  11. package/dist/commands/affected.d.ts +9 -0
  12. package/dist/commands/affected.d.ts.map +1 -0
  13. package/dist/commands/affected.js +127 -0
  14. package/dist/commands/affected.js.map +1 -0
  15. package/dist/commands/eval.d.ts +152 -0
  16. package/dist/commands/eval.d.ts.map +1 -0
  17. package/dist/commands/eval.js +693 -0
  18. package/dist/commands/eval.js.map +1 -0
  19. package/dist/commands/harness.d.ts +90 -0
  20. package/dist/commands/harness.d.ts.map +1 -0
  21. package/dist/commands/harness.js +883 -0
  22. package/dist/commands/harness.js.map +1 -0
  23. package/dist/commands/jtbd.d.ts +38 -0
  24. package/dist/commands/jtbd.d.ts.map +1 -0
  25. package/dist/commands/jtbd.js +261 -0
  26. package/dist/commands/jtbd.js.map +1 -0
  27. package/dist/commands/reliability.d.ts +14 -0
  28. package/dist/commands/reliability.d.ts.map +1 -0
  29. package/dist/commands/reliability.js +81 -0
  30. package/dist/commands/reliability.js.map +1 -0
  31. package/dist/evals/graph-runner.d.ts +8 -0
  32. package/dist/evals/graph-runner.d.ts.map +1 -0
  33. package/dist/evals/graph-runner.js +136 -0
  34. package/dist/evals/graph-runner.js.map +1 -0
  35. package/dist/harness/capability-coverage.d.ts +165 -0
  36. package/dist/harness/capability-coverage.d.ts.map +1 -0
  37. package/dist/harness/capability-coverage.js +308 -0
  38. package/dist/harness/capability-coverage.js.map +1 -0
  39. package/dist/harness/capability-table.d.ts +17 -0
  40. package/dist/harness/capability-table.d.ts.map +1 -0
  41. package/dist/harness/capability-table.js +3941 -0
  42. package/dist/harness/capability-table.js.map +1 -0
  43. package/dist/harness/changed-files.d.ts +20 -0
  44. package/dist/harness/changed-files.d.ts.map +1 -0
  45. package/dist/harness/changed-files.js +90 -0
  46. package/dist/harness/changed-files.js.map +1 -0
  47. package/dist/harness/declared-capabilities.d.ts +12 -0
  48. package/dist/harness/declared-capabilities.d.ts.map +1 -0
  49. package/dist/harness/declared-capabilities.js +21 -0
  50. package/dist/harness/declared-capabilities.js.map +1 -0
  51. package/dist/harness/registry.d.ts +190 -0
  52. package/dist/harness/registry.d.ts.map +1 -0
  53. package/dist/harness/registry.js +2419 -0
  54. package/dist/harness/registry.js.map +1 -0
  55. package/dist/index.d.ts +22 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +33 -0
  58. package/dist/index.js.map +1 -0
  59. package/package.json +63 -4
@@ -0,0 +1,2419 @@
1
+ /**
2
+ * The harness registry — the machine-readable inventory behind
3
+ * harness-first engineering (docs/HARNESS_FIRST.md).
4
+ *
5
+ * Two lists, one invariant:
6
+ *
7
+ * - HARNESSES: every headless, agent-drivable way to exercise a NodeTool
8
+ * surface — validate it, run it, replay interactions against it, or score
9
+ * a model driving it. A harness with a `selfcheck` can prove its surface
10
+ * still works with no target, no key, and no human: that's what
11
+ * `nodetool harness gate` runs.
12
+ * - SURFACES: every product surface a user (or agent) can touch. Each names
13
+ * the harnesses that cover it and the code paths that belong to it, or
14
+ * carries a `gap` note saying why nothing covers it yet and what a
15
+ * harness for it would look like.
16
+ *
17
+ * The invariant — no surface without a harness or a documented gap — is
18
+ * enforced two ways: `nodetool harness audit` reports it (exit 1 with
19
+ * `--strict` on any gap), and tests/harness-registry.test.ts fails the build
20
+ * on an undocumented one. Shipping a new surface means adding it here, which
21
+ * means either pointing at its harness or writing down the debt.
22
+ *
23
+ * The `paths` on each surface are what make the registry executable:
24
+ * `nodetool harness gate` maps a diff onto surfaces by path prefix and runs
25
+ * the selfchecks of every harness covering a touched surface. The gate — not
26
+ * the author — selects the checks.
27
+ */
28
+ import { computeAffected } from "../affected/affected.js";
29
+ import { isGlobalGateFile } from "./changed-files.js";
30
+ /**
31
+ * The pure suites behind the script↔storyboard link and the guided creation
32
+ * flows. One constant because the entry's `command` and its `selfcheck` must
33
+ * not drift: a selfcheck that runs less than the command it stands for reports
34
+ * green on code it never executed.
35
+ *
36
+ * Vitest and Jest both take these positional arguments as file-path filters.
37
+ */
38
+ const SCRIPT_STORYBOARD_LINK_SUITES = "npm run test --workspace=packages/protocol -- script-link shot-prompt " +
39
+ "render-record creative-scenes screenplay-authoring api-schemas-storyboards && " +
40
+ "npm run test --workspace=packages/timeline -- script-link linked storyboard && " +
41
+ "npm run test --workspace=packages/execution -- linked-timeline-validate && " +
42
+ "npm run test --workspace=web -- src/lib/storyboard src/components/setup";
43
+ /**
44
+ * The Workflow flow's pure suites (PRD § 11): the plan schema and the plan→graph
45
+ * builder in protocol, the shipped inspiration chips built against the real node
46
+ * registry, the plan-to-graph case's grading, and the browser flow's steps and
47
+ * tools. One constant so the entry's `command` and its `selfcheck` cannot drift.
48
+ */
49
+ const WORKFLOW_PLAN_SUITES = "npm run test --workspace=packages/protocol -- workflow && " +
50
+ "npm run test --workspace=packages/base-nodes -- workflow-plan-chips && " +
51
+ "npm run test --workspace=packages/agents -- app-build-workflow-plan && " +
52
+ "npm run test --workspace=web -- src/components/setup/workflow src/hooks/workflow src/lib/tools/builtin/__tests__/workflowSetupTools";
53
+ /**
54
+ * Native game performance commands: the tick bench on real games, the capture
55
+ * and tick budgets, and the exported-play frame benchmark. The perf suite plays
56
+ * a deliberately slow player that each `--fail-on-budget` overrun must catch.
57
+ */
58
+ const GAME_PERF_SUITES = "npm run test --workspace=packages/cli-game -- game-command-bench-cli game-command-benchmark game-command-capture-budget game-command-perf && " +
59
+ "npm run test --workspace=packages/game-renderer -- perf frame-budget";
60
+ /** Native game schemas, simulation, renderer, graph, and persistence checks. */
61
+ const GAME_FLOW_SUITES = "npm run test --workspace=packages/protocol -- game && " +
62
+ "npm run test --workspace=packages/base-nodes -- game-graph-chips && " +
63
+ "npm run test --workspace=packages/game-nodes && " +
64
+ "npm run test --workspace=packages/game-runtime && " +
65
+ "npm run test --workspace=packages/game-renderer && " +
66
+ "npm run test --workspace=packages/websocket -- trpc-games trpc-game-asset-browser gameDraftHistory style-presets && " +
67
+ "npm run test --workspace=packages/agents -- game && " +
68
+ "npm run test --workspace=packages/cli-game -- game-command && " +
69
+ "npm run test --workspace=web -- src/stores/game src/components/game src/components/setup/game src/hooks/game";
70
+ /**
71
+ * Durable generation recovery without a provider call. These suites exercise
72
+ * acceptance and lease fencing, explicit FAL queue binding, receipt ordering,
73
+ * lifecycle persistence, and signed inbox deduplication against local fakes.
74
+ */
75
+ const GENERATION_RECOVERY_SUITES = "npm run test --workspace=packages/models -- durable-generation prediction-generations schema-parity migration-schema-parity && " +
76
+ "npm run test --workspace=packages/execution -- durable-generation-lifecycle generation-recovery-worker generation-attachment-recovery fal-output-decoder fal-media-shape-parity && " +
77
+ "npm run test --workspace=packages/runtime -- generation-acceptance-order generation-receipt-binding fal-queue && " +
78
+ "npm run test --workspace=packages/websocket -- fal-webhook generation-recovery-media";
79
+ /**
80
+ * The AtlasCloud callback suites: the Ed25519 ingress route's verification and
81
+ * dispatch, the pending-prediction registry, and the submit/wait wiring that
82
+ * decides whether a run waits for a callback or polls. One constant so the
83
+ * entry's `command` and its `selfcheck` cannot drift.
84
+ */
85
+ const ATLASCLOUD_CALLBACK_SUITES = "npm run test --workspace=packages/runtime -- atlascloud-webhook && " +
86
+ "npm run test --workspace=packages/websocket -- atlascloud-webhook";
87
+ /**
88
+ * The 3D-clip suites: the render session `model3d` layers draw through in the
89
+ * browser, and the headless pre-pass that draws them for
90
+ * `preview_timeline_frame`. One constant so the entry's `command` and its
91
+ * `selfcheck` cannot drift.
92
+ *
93
+ * Vitest takes these positional arguments as file-path filters.
94
+ */
95
+ const TIMELINE_MODEL3D_SUITES = "npm run test --workspace=packages/video-nodes -- model3d-render && " +
96
+ "npm run test --workspace=packages/agents -- timeline-model3d-frames";
97
+ /**
98
+ * A trimmed imported source receives a candidate edit, keeps its active take
99
+ * inactive until the explicit promotion, then restores it with one undo. The
100
+ * package test exercises the headless contract and the web test drives the
101
+ * live TimelineStore path through its agent handler.
102
+ */
103
+ const TIMELINE_NATIVE_MEDIA_EDITING_SUITES = "npm run test --workspace=packages/timeline -- extension && " +
104
+ "npm run test --workspace=packages/runtime -- video-extension && " +
105
+ "npm run test --workspace=packages/websocket -- video-extension-inference && " +
106
+ "npm run test --workspace=packages/agents -- timeline-headless-media-parity timeline-tool-contracts timeline-generative-edit timeline-tool-loop && " +
107
+ "npm run test --workspace=web -- useTimelineAgentBridge timelineTools nativeMediaEditingAcceptance nativeMediaEditingPreview timelineExtension useTimelineExtension ExtendClipPanel" +
108
+ " && npm run test --workspace=packages/websocket -- generation-recovery-media";
109
+ /**
110
+ * The SVG and audio editor agent tools: the headless `svg-tools` and
111
+ * `audio-tools` bridges with a golden transcript per eval case, and the browser
112
+ * side of the same `ui_svg_*` / `ui_audio_*` contracts (tool parsing, the live
113
+ * editor handlers, the shared effect catalog). One constant so the entry's
114
+ * `command` and its `selfcheck` cannot drift.
115
+ */
116
+ const ASSET_EDITOR_TOOL_SUITES = "npm run test --workspace=packages/agents -- svg-tool-loop audio-tool-loop && " +
117
+ "npm run test --workspace=web -- svgTools audioTools audioAgentOps audioEffects AudioSampleEditor SvgSurface AudioSurface";
118
+ /**
119
+ * The audio-driven-motion suites: `bake_audio_animation`'s own arithmetic
120
+ * (the two clock hops, the envelope/beats curve shapes) plus the end-to-end
121
+ * demonstration that bakes a beat-synced pulse onto a clip that already
122
+ * carries a hand-authored preset and checks the two compose by multiplying
123
+ * rather than one replacing the other. One constant so the entry's `command`
124
+ * and its `selfcheck` cannot drift.
125
+ */
126
+ const TIMELINE_AUDIO_DRIVE_SUITES = "npm run test --workspace=packages/agents -- timeline-audio-drive-demo capabilities-bake-audio-animation";
127
+ /**
128
+ * The generated-matte suites: the pure helpers a clip's own cutout is written
129
+ * and re-selected through, the way the scene model resolves one into a luma
130
+ * keyhole, `isolate_subject`'s document state machine (with a fake runner —
131
+ * the provider is never called), the op/bridge parity for
132
+ * `set_generated_matte`, and the HTTP door the inspector posts to. One
133
+ * constant so the entry's `command` and its `selfcheck` cannot drift.
134
+ *
135
+ * Vitest takes these positional arguments as file-path filters.
136
+ */
137
+ const TIMELINE_GENERATED_MATTE_SUITES = "npm run test --workspace=packages/timeline -- generatedMatte && " +
138
+ "npm run test --workspace=packages/agents -- capabilities-isolate-subject timeline-op-parity && " +
139
+ "npm run test --workspace=packages/websocket -- timeline-isolate-subject-route";
140
+ /**
141
+ * The per-capability contract suites. One constant because the entry's
142
+ * `command` and its `selfcheck` must not drift — a selfcheck that runs less
143
+ * than the command it stands for reports green on code it never executed.
144
+ *
145
+ * The arguments are vitest path filters, so a suite whose filename matches
146
+ * none of them is not run however well it is written: `timeline-video-flow`
147
+ * is named explicitly for that reason.
148
+ */
149
+ const CAPABILITY_SUITES = "npm run test --workspace=packages/agents -- capabilities capability " +
150
+ "mcp-tools memory-tools workflow-version-tools nodetool-api-workflows " +
151
+ "sandbox-package-docs sandbox-package-listing browser-tools " +
152
+ "timelines-op-input timeline-video-flow game-asset-pipeline";
153
+ /**
154
+ * The pure derivations behind the graph-resource nodes plus the node suites
155
+ * that drive them, in dependency order (design §7). One constant because the
156
+ * entry's `command` and its `selfcheck` must not drift: a selfcheck that runs
157
+ * less than the command it stands for reports green on code it never executed.
158
+ *
159
+ * The arguments are vitest path filters, so a suite whose filename matches
160
+ * none of them is not run however well it is written.
161
+ */
162
+ const GRAPH_RESOURCES_SUITES = "npm run test --workspace=packages/storyboard && " +
163
+ "npm run test --workspace=packages/timeline -- derive && " +
164
+ "npm run test --workspace=packages/protocol -- script-fill game-slot-prompt && " +
165
+ "npm run test --workspace=packages/game-nodes && " +
166
+ "npm run test --workspace=packages/core-nodes -- entity-nodes && " +
167
+ "npm run test --workspace=packages/video-nodes -- storyboard-nodes script-nodes";
168
+ const LEAN_SERVER_PACKAGES = [
169
+ "cli", "pack-core", "pack-manager", "sandbox-runtime", "server-feature-packs",
170
+ "database-postgres", "database-sqlite", "document-processing", "execution-core",
171
+ "feature-documents", "models-core", "nodes-core", "provider-anthropic",
172
+ "runtime-core", "security-core", "server-core", "server-feature-sandbox",
173
+ "server-features", "server-minimal"
174
+ ];
175
+ const LEAN_SERVER_CHECK = `npm run test ${LEAN_SERVER_PACKAGES.map(name => `--workspace=packages/${name}`).join(" ")} && ` +
176
+ "node scripts/run-vitest.mjs run --config vitest.config.ts scripts/__tests__/minimal-dependency-guard.test.mjs && " +
177
+ "node scripts/generate-minimal-feature-catalog.mjs --check";
178
+ const CLI_COMMAND_PACKAGES = [
179
+ "cli-admin", "cli-apps", "cli-chat", "cli-common", "cli-execution",
180
+ "cli-game", "cli-local", "cli-mcp", "cli-models", "cli-providers",
181
+ "cli-script", "cli-server", "cli-sketch", "cli-timeline", "cli-workflow"
182
+ ];
183
+ export const HARNESSES = [
184
+ ...CLI_COMMAND_PACKAGES.map((name) => ({
185
+ id: `${name}-module`,
186
+ title: `Command package ${name}`,
187
+ command: `npm run test --workspace=packages/${name} -- --passWithNoTests=false`,
188
+ kind: "execution",
189
+ capabilities: [],
190
+ docs: "docs/harnesses.md § nodetool harness",
191
+ selfcheck: { command: `npm run test --workspace=packages/${name} -- --passWithNoTests=false`, cost: "cheap", suiteOnly: true }
192
+ })),
193
+ {
194
+ id: "cli-harness-commands",
195
+ title: "Harness and evaluation command module",
196
+ command: "npm run test --workspace=packages/cli-harness",
197
+ kind: "execution",
198
+ capabilities: [],
199
+ docs: "packages/cli-harness/README.md",
200
+ selfcheck: { command: "npm run test --workspace=packages/cli-harness", cost: "cheap", suiteOnly: true }
201
+ },
202
+ {
203
+ id: "lean-server",
204
+ title: "Minimal execution host and selected features",
205
+ command: LEAN_SERVER_CHECK,
206
+ kind: "execution",
207
+ capabilities: ["gated:pr"],
208
+ docs: "packages/server-minimal/README.md",
209
+ selfcheck: { command: LEAN_SERVER_CHECK, cost: "cheap" }
210
+ },
211
+ {
212
+ id: "lean-server-packed",
213
+ title: "Clean packed minimal server consumer",
214
+ command: "npm run test:minimal:packed",
215
+ kind: "execution",
216
+ capabilities: [],
217
+ docs: "packages/server-minimal/README.md",
218
+ selfcheck: { command: "npm run test:minimal:packed", cost: "expensive" }
219
+ },
220
+ {
221
+ id: "validate",
222
+ title: "Static workflow check",
223
+ command: "nodetool validate <id|file.json|file.ts>",
224
+ kind: "static",
225
+ capabilities: ["json", "no-db", "gated:pr"],
226
+ agentTool: "validate_workflow",
227
+ docs: "docs/harnesses.md § nodetool validate",
228
+ selfcheck: { command: "npm run validate:examples", cost: "cheap" }
229
+ },
230
+ {
231
+ id: "debug",
232
+ title: "Workflow debug harness (server + browser surfaces)",
233
+ command: "nodetool debug <id|file> [--browser --trace --watch --supervise]",
234
+ kind: "execution",
235
+ capabilities: ["json", "watch", "supervise", "browser", "gated:nightly"],
236
+ agentTool: "debug_workflow",
237
+ docs: "docs/harnesses.md § nodetool debug",
238
+ selfcheck: { command: "npm run examples:smoke", cost: "expensive" }
239
+ },
240
+ {
241
+ id: "reliability-ring0",
242
+ title: "Reliability Ring 0 (golden journeys on the kernel, strict lifecycle)",
243
+ command: "npm run reliability:ring0",
244
+ kind: "execution",
245
+ capabilities: ["gated:pr"],
246
+ docs: "docs/harnesses.md § nodetool reliability",
247
+ selfcheck: { command: "npm run reliability:ring0", cost: "cheap" }
248
+ },
249
+ {
250
+ id: "node-run",
251
+ title: "Single-node harness",
252
+ command: "nodetool node run <type> --props '{...}' [--no-secrets]",
253
+ kind: "execution",
254
+ capabilities: ["json", "no-db"],
255
+ docs: "docs/harnesses.md § nodetool node run",
256
+ selfcheck: {
257
+ command: 'npm run dev:nodetool -- node run nodetool.text.Concat --props \'{"a":"harness-","b":"gate"}\' --no-secrets',
258
+ cost: "cheap"
259
+ }
260
+ },
261
+ {
262
+ id: "blender",
263
+ title: "Blender headless render (nodetool.blender.* nodes)",
264
+ command: "nodetool node run nodetool.blender.RenderImage --props '<fixture>'",
265
+ kind: "execution",
266
+ capabilities: ["json", "no-db"],
267
+ docs: "docs/blender-headless-integration-design.md",
268
+ selfcheck: {
269
+ // Renders the checked-in triangle fixture through the real Blender.
270
+ // Expensive on purpose: Blender startup plus a render, and it needs
271
+ // the Blender binary, so the default gate skips it without --expensive.
272
+ command: 'npm run dev:nodetool -- node run nodetool.blender.RenderImage --props "$(cat packages/blender-nodes/tests/fixtures/render-image-props.json)" --no-secrets',
273
+ cost: "expensive"
274
+ }
275
+ },
276
+ {
277
+ id: "manimgx",
278
+ title: "ManimGX scene inspection and render (nodetool.manimgx.* nodes)",
279
+ command: "npm run test:real --workspace=packages/manimgx-nodes",
280
+ kind: "execution",
281
+ capabilities: ["no-db"],
282
+ docs: "packages/manimgx-nodes/README.md",
283
+ selfcheck: {
284
+ // Renders and inspects real scenes through the nodes, with an image
285
+ // input. It needs manimgx installed (Python 3.13+, GPU or lavapipe),
286
+ // so the default gate skips it without --expensive.
287
+ command: "npm run test:real --workspace=packages/manimgx-nodes",
288
+ cost: "expensive"
289
+ }
290
+ },
291
+ {
292
+ id: "dsl-native-flow",
293
+ title: "Native flow (call a node as a function, host backend + guest surface)",
294
+ // No CLI command owns it: the public surface is the sandbox pack
295
+ // @nodetool-ai/sandbox-flow, and the host side is internal to
296
+ // packages/dsl. The checked-in suites are the headless surface — they call
297
+ // nodes on the flow's own invoke path, stream both directions, and abort
298
+ // mid-call, with no key, no database, and no server.
299
+ command: "npm run test --workspace=packages/dsl -- flow-core flow-streaming flow-abort",
300
+ kind: "execution",
301
+ capabilities: ["no-db"],
302
+ docs: "docs/dsl-native-flow-design.md",
303
+ selfcheck: {
304
+ command: "npm run test --workspace=packages/dsl -- flow-core flow-streaming flow-abort",
305
+ cost: "cheap",
306
+ suiteOnly: true
307
+ }
308
+ },
309
+ {
310
+ id: "app-runs",
311
+ title: "Durable app operation and generation attachment check",
312
+ command: "npm run test --workspace=packages/execution -- app-operation app-run-generation",
313
+ kind: "execution",
314
+ capabilities: [],
315
+ docs: "docs/harnesses.md § App runs phase 1",
316
+ selfcheck: {
317
+ command: "npm run test --workspace=packages/execution -- app-operation app-run-generation",
318
+ cost: "cheap"
319
+ }
320
+ },
321
+ {
322
+ id: "run-traces",
323
+ title: "Durable app trace, content policy and ancestry check",
324
+ command: "npm run test --workspace=packages/execution --workspace=packages/runtime --workspace=packages/models --workspace=packages/websocket -- run-trace external-trace-privacy http-tracing",
325
+ kind: "execution",
326
+ capabilities: [],
327
+ docs: "docs/harnesses.md § Run traces phase 2",
328
+ selfcheck: {
329
+ command: "npm run test --workspace=packages/execution --workspace=packages/runtime --workspace=packages/models --workspace=packages/websocket -- run-trace external-trace-privacy http-tracing",
330
+ cost: "cheap"
331
+ }
332
+ },
333
+ {
334
+ id: "app-debug",
335
+ title: "Mini-app debug harness",
336
+ command: "nodetool app debug <id|bundle.json> [--interact ... --no-run]",
337
+ kind: "execution",
338
+ capabilities: ["json", "interact", "no-db"],
339
+ docs: "docs/harnesses.md § nodetool app debug",
340
+ selfcheck: {
341
+ // Ad Maker shows one step at a time, so the check walks both steps the
342
+ // way a person does before it judges whether every run is reachable.
343
+ command: "npm run dev:nodetool -- app debug packages/base-nodes/nodetool/examples/apps/ad-maker.app.json --no-run --interact '[{\"set\":{\"key\":\"step\",\"value\":\"settle-the-message\"}},{\"set\":{\"key\":\"step\",\"value\":\"direct-the-campaign-image\"}}]'",
344
+ cost: "cheap"
345
+ }
346
+ },
347
+ {
348
+ id: "run-readers",
349
+ title: "Run reader, debug history and CLI failure-path check",
350
+ command: "npm run test --workspace=packages/execution --workspace=packages/models --workspace=packages/websocket --workspace=packages/agents --workspace=packages/cli-workflow --workspace=packages/cli-apps -- runs-phase3 app-debug-versioned-targets app-debug-workflow-inputs-phase3 capabilities-runs runs-command runs-integration run-readers job-find runs-trpc sandbox-api-coverage",
351
+ kind: "execution",
352
+ capabilities: ["json"],
353
+ docs: "docs/harnesses.md § Run readers phase 3",
354
+ selfcheck: {
355
+ command: "npm run test --workspace=packages/execution --workspace=packages/models --workspace=packages/websocket --workspace=packages/agents --workspace=packages/cli-workflow --workspace=packages/cli-apps -- runs-phase3 app-debug-versioned-targets app-debug-workflow-inputs-phase3 capabilities-runs runs-command runs-integration run-readers job-find runs-trpc sandbox-api-coverage",
356
+ cost: "cheap"
357
+ }
358
+ },
359
+ {
360
+ id: "browser-run-spans",
361
+ title: "Browser run ownership, ancestry and restart check",
362
+ command: "npm run test --workspace=packages/websocket -- browser-run-spans-phase4 browser-app-run-phase4 app-runs-workflow",
363
+ kind: "execution",
364
+ capabilities: [],
365
+ docs: "docs/harnesses.md § Run inspection phase 4",
366
+ selfcheck: {
367
+ command: "npm run test --workspace=packages/websocket -- browser-run-spans-phase4 browser-app-run-phase4 app-runs-workflow",
368
+ cost: "cheap"
369
+ }
370
+ },
371
+ {
372
+ id: "run-inspection-ui",
373
+ title: "Stored trace, activity replay and browser recorder check",
374
+ command: "npm run test --workspace=web -- --runInBand --testPathPattern='browserRunTrace|browserWorkflowRunner|WorkflowRunner.test|TracePanel.test|TraceStore|useRuns|runTraceCache|appRunActivity|AgentActivityWidget|useWidgetRuntime|sketchPadWidget|workflowFormWidget|chatWidgets|mediaWidgets|buttonWidget|recorderWidgets|conditionalWidget|errorTraceReporting|ChatSurface.test|AskRunAgentButton|useAppRuntime|runJsScript.test'",
375
+ kind: "execution",
376
+ capabilities: [],
377
+ docs: "docs/harnesses.md § Run inspection phase 4",
378
+ selfcheck: {
379
+ command: "npm run test --workspace=web -- --runInBand --testPathPattern='browserRunTrace|browserWorkflowRunner|WorkflowRunner.test|TracePanel.test|TraceStore|useRuns|runTraceCache|appRunActivity|AgentActivityWidget|useWidgetRuntime|sketchPadWidget|workflowFormWidget|chatWidgets|mediaWidgets|buttonWidget|recorderWidgets|conditionalWidget|errorTraceReporting|ChatSurface.test|AskRunAgentButton|useAppRuntime|runJsScript.test'",
380
+ cost: "cheap"
381
+ }
382
+ },
383
+ {
384
+ id: "run-inspection-browser",
385
+ title: "Browser action ancestry and Trace/Logs reload journey",
386
+ command: "npm run test:journeys --workspace=web -- run-observability.spec.ts",
387
+ kind: "execution",
388
+ capabilities: ["browser"],
389
+ docs: "docs/harnesses.md § Run inspection phase 4"
390
+ },
391
+ {
392
+ id: "app-instance-history",
393
+ title: "Instance management, pinned versions and history projection check",
394
+ command: "npm run test --workspace=packages/models --workspace=packages/execution --workspace=packages/websocket -- app-instance run-list-app-metadata runs-phase3 app-operation",
395
+ kind: "execution",
396
+ capabilities: [],
397
+ docs: "docs/harnesses.md § App instances and history phase 5",
398
+ selfcheck: {
399
+ command: "npm run test --workspace=packages/models --workspace=packages/execution --workspace=packages/websocket -- app-instance run-list-app-metadata runs-phase3 app-operation",
400
+ cost: "cheap"
401
+ }
402
+ },
403
+ {
404
+ id: "app-instance-history-ui",
405
+ title: "Instance tabs, management cache and app host check",
406
+ command: "npm run test --workspace=web -- --runInBand --testPathPattern='WorkspaceTabsStore|useAppInstances|useAppInstance|ApplicationSurface|ApplicationRunView|AppRuntimeView|AppRunHistory|useAppRuntime'",
407
+ kind: "execution",
408
+ capabilities: [],
409
+ docs: "docs/harnesses.md § App instances and history phase 5",
410
+ selfcheck: {
411
+ command: "npm run test --workspace=web -- --runInBand --testPathPattern='WorkspaceTabsStore|useAppInstances|useAppInstance|ApplicationSurface|ApplicationRunView|AppRuntimeView|AppRunHistory|useAppRuntime'",
412
+ cost: "cheap"
413
+ }
414
+ },
415
+ {
416
+ id: "app-instance-history-browser",
417
+ title: "Independent app instances and read-only historical results journey",
418
+ command: "npm run test:journeys --workspace=web -- app-instances.spec.ts run-observability.spec.ts",
419
+ kind: "execution",
420
+ capabilities: ["browser"],
421
+ docs: "docs/harnesses.md § App instances and history phase 5"
422
+ },
423
+ {
424
+ id: "app-build",
425
+ title: "Mini-app build harness (spec→plan→author→check→run→judge)",
426
+ command: "nodetool app build <prompt|spec.json> -p <provider> -m <model>",
427
+ kind: "execution",
428
+ capabilities: ["json", "watch", "supervise", "gated:pr"],
429
+ docs: "docs/harnesses.md § nodetool app build",
430
+ selfcheck: {
431
+ // The suite's three keyless cases: two scripted app authors and the
432
+ // Workflow flow's plan-to-graph case, all on the real kernel with the
433
+ // provider constructed but never called — the same invocation the
434
+ // Quality Gate's app-build leg runs. `workflow-plan-to-graph` is graded
435
+ // on what its built graph produced, not only on whether it validated
436
+ // (PRD R6), so a plan that wires into a graph that runs and returns
437
+ // nothing fails here.
438
+ command: "npm run dev:nodetool -- eval app-build --cases greeting-card,draft-then-publish,workflow-plan-to-graph -p ollama -m none --no-find-model --min-success 1",
439
+ cost: "cheap"
440
+ }
441
+ },
442
+ {
443
+ id: "timeline-validate",
444
+ title: "Timeline static check",
445
+ command: "nodetool timeline validate <id|file.json>",
446
+ kind: "static",
447
+ capabilities: ["json", "no-db"],
448
+ agentTool: "validate_timeline",
449
+ docs: "docs/harnesses.md § nodetool timeline validate / debug",
450
+ selfcheck: {
451
+ // The validator's own rule suites plus the harness host that drives it.
452
+ // Keyless and target-free: every case is a document literal, so this is
453
+ // the check a diff under `packages/execution/src/timeline-debug/` or
454
+ // `packages/timeline/` earns.
455
+ command: "npm run test --workspace=packages/execution -- timeline- linked-timeline-validate && " +
456
+ "npm run test --workspace=packages/cli-timeline -- tests/timeline-harness.test.ts",
457
+ cost: "cheap"
458
+ }
459
+ },
460
+ {
461
+ id: "timeline-debug",
462
+ title: "Timeline edit-session replay",
463
+ command: "nodetool timeline debug <id|file.json> --interact '[...]'",
464
+ kind: "execution",
465
+ capabilities: ["json", "interact", "no-db"],
466
+ docs: "docs/harnesses.md § nodetool timeline validate / debug"
467
+ },
468
+ {
469
+ id: "timeline-render",
470
+ title: "Timeline GPU render (video, stills or contact sheet)",
471
+ command: "nodetool timeline render <id|file.json> [--stills] [--sheet [columns]] [--only <clips>] [--frames <spec>] [--scale <n>]",
472
+ kind: "execution",
473
+ capabilities: ["json", "no-db"],
474
+ agentTool: "render_timeline",
475
+ docs: "docs/harnesses.md § nodetool timeline render",
476
+ selfcheck: {
477
+ // Frame selection, preview scale and zero-area layers through the real
478
+ // GPU compositor. Skips with a reason when there is no WebGPU adapter.
479
+ command: "npm run test --workspace=packages/video-nodes -- timeline-render-frames.gpu timeline-contact-sheet && " +
480
+ "npm run test --workspace=packages/cli-timeline -- timeline-render timeline-command",
481
+ cost: "cheap"
482
+ }
483
+ },
484
+ {
485
+ id: "timeline-score",
486
+ title: "Timeline craft scorecard (agent output vs. shipped showcase examples)",
487
+ command: "nodetool timeline score <id|file.json> [--json] [--min-score <n>] [--sheet <out.png>]",
488
+ kind: "static",
489
+ capabilities: ["json", "no-db"],
490
+ docs: "docs/harnesses.md § nodetool timeline score",
491
+ selfcheck: {
492
+ // Deterministic: the metrics/formula core, plus the reference band
493
+ // computed from the shipped examples at test time — no model, no key.
494
+ command: "npm run test --workspace=packages/execution -- timeline-score && " +
495
+ "npm run test --workspace=packages/cli-timeline -- timeline-score",
496
+ cost: "cheap"
497
+ }
498
+ },
499
+ {
500
+ id: "serein-timeline-repro",
501
+ title: "Serein timeline motion frame reproduction",
502
+ command: "npm run test --workspace=packages/agents -- timeline-serein-gaps-frames",
503
+ kind: "execution",
504
+ capabilities: ["no-db"],
505
+ docs: "demo/benchmarks/serein/EVAL.md",
506
+ selfcheck: {
507
+ command: "npm run test --workspace=packages/agents -- timeline-serein-gaps-frames",
508
+ cost: "cheap"
509
+ }
510
+ },
511
+ {
512
+ id: "timeline-versions",
513
+ title: "Timeline version history (snapshot, restore, validate the restore)",
514
+ command: "nodetool timeline versions list|show|create|restore|delete <id> [<version>]",
515
+ kind: "execution",
516
+ capabilities: ["json"],
517
+ agentTool: "restore_timeline_version",
518
+ docs: "docs/harnesses.md § nodetool timeline versions"
519
+ },
520
+ {
521
+ id: "timeline-model3d",
522
+ title: "3D clips on the timeline (render session, headless preview frames)",
523
+ // No CLI command owns it: a 3D layer is drawn by the browser preview and,
524
+ // on the server, by the headless pre-pass behind `preview_timeline_frame`.
525
+ // The checked-in suites are the headless surface — the session's pure
526
+ // parts and the pre-pass's grouping, fallback and layer report, with no
527
+ // key, no database and no server. The Chromium half of the render suite
528
+ // skips without Chrome, as it did before the timeline used it.
529
+ command: TIMELINE_MODEL3D_SUITES,
530
+ kind: "static",
531
+ capabilities: ["no-db"],
532
+ agentTool: "preview_timeline_frame",
533
+ docs: "docs/harnesses.md § 3D clips in preview_timeline_frame",
534
+ selfcheck: { command: TIMELINE_MODEL3D_SUITES, cost: "cheap" }
535
+ },
536
+ {
537
+ id: "timeline-native-media-editing",
538
+ title: "Native media editing (candidate take, apply, undo)",
539
+ command: TIMELINE_NATIVE_MEDIA_EDITING_SUITES,
540
+ kind: "static",
541
+ capabilities: ["no-db"],
542
+ agentTool: "ui_timeline_generatively_edit_clip",
543
+ docs: "docs/native-media-editing/prd.md § 9",
544
+ selfcheck: { command: TIMELINE_NATIVE_MEDIA_EDITING_SUITES, cost: "cheap" }
545
+ },
546
+ {
547
+ id: "asset-editor-tools",
548
+ title: "SVG and audio editor agent tools (ui_svg_*, ui_audio_*)",
549
+ command: ASSET_EDITOR_TOOL_SUITES,
550
+ kind: "static",
551
+ capabilities: ["no-db"],
552
+ agentTool: "ui_audio_edit",
553
+ docs: "packages/agents/AGENTS.md § Tool-loop eval suites",
554
+ selfcheck: { command: ASSET_EDITOR_TOOL_SUITES, cost: "cheap" }
555
+ },
556
+ {
557
+ id: "timeline-audio-drive",
558
+ title: "Audio-driven clip motion (bake_audio_animation → the animation fold)",
559
+ // No CLI command owns it: the surface is the `bake_audio_animation`
560
+ // capability plus the compose rule it depends on in the animation engine
561
+ // (`scale: "multiply"` in `ANIMATED_PROPERTY_FOLD` — a baked curve must
562
+ // drive a clip alongside a hand-authored preset, not replace it). The
563
+ // checked-in suites are the headless surface: the bake's own arithmetic
564
+ // (the two clock hops, envelope vs. beats curve shapes) plus the product
565
+ // demonstration that bakes a beat-synced pulse onto a clip already
566
+ // carrying a `pop` entrance, samples the result through
567
+ // `resolveAnimatedLayerProps`, and confirms it in rendered pixels.
568
+ command: TIMELINE_AUDIO_DRIVE_SUITES,
569
+ kind: "static",
570
+ capabilities: ["no-db"],
571
+ agentTool: "bake_audio_animation",
572
+ docs: "docs/harnesses.md § Audio-driven timeline motion",
573
+ selfcheck: {
574
+ command: TIMELINE_AUDIO_DRIVE_SUITES,
575
+ cost: "cheap",
576
+ suiteOnly: true
577
+ }
578
+ },
579
+ {
580
+ id: "timeline-generated-matte",
581
+ title: "Generated mattes (isolate_subject → the clip's own luma keyhole)",
582
+ // No CLI command owns it: the surface is the `isolate_subject` capability,
583
+ // the `set_generated_matte` op both hosts write the knobs through, and the
584
+ // scene model that resolves the result into a keyhole. The provider is
585
+ // never called — the capability takes its runner as a dependency, and the
586
+ // checked-in suites drive it with a fake one, so what is actually asserted
587
+ // is the document: which matte is current, what status it carries, and
588
+ // that a failed regenerate leaves the working one in place.
589
+ command: TIMELINE_GENERATED_MATTE_SUITES,
590
+ kind: "static",
591
+ capabilities: ["no-db"],
592
+ agentTool: "isolate_subject",
593
+ docs: "docs/harnesses.md § Generated mattes",
594
+ selfcheck: { command: TIMELINE_GENERATED_MATTE_SUITES, cost: "cheap" }
595
+ },
596
+ {
597
+ id: "sketch-validate",
598
+ title: "Sketch static check",
599
+ command: "nodetool sketch validate <id|file.json>",
600
+ kind: "static",
601
+ capabilities: ["json", "no-db"],
602
+ agentTool: "validate_sketch",
603
+ docs: "docs/harnesses.md § nodetool sketch validate / debug"
604
+ },
605
+ {
606
+ id: "sketch-debug",
607
+ title: "Sketch edit-session replay",
608
+ command: "nodetool sketch debug <id|file.json> --interact '[...]'",
609
+ kind: "execution",
610
+ capabilities: ["json", "interact", "no-db"],
611
+ docs: "docs/harnesses.md § nodetool sketch validate / debug"
612
+ },
613
+ {
614
+ id: "sketch-render",
615
+ title: "Headless sketch renderer (PNG, layer, mask, PSD)",
616
+ command: "nodetool sketch render <id|file.json> -o out.png [--format psd]",
617
+ kind: "execution",
618
+ capabilities: ["json", "no-db"],
619
+ agentTool: "render_sketch",
620
+ docs: "docs/harnesses.md § nodetool sketch render"
621
+ },
622
+ {
623
+ id: "sketch-versions",
624
+ title: "Sketch version history (snapshot, restore, validate the restore)",
625
+ command: "nodetool sketch versions list|show|create|restore|delete <id> [<version>]",
626
+ kind: "execution",
627
+ capabilities: ["json"],
628
+ agentTool: "restore_sketch_version",
629
+ docs: "docs/harnesses.md § nodetool sketch versions"
630
+ },
631
+ {
632
+ id: "jsscript-validate",
633
+ title: "JS script static check",
634
+ command: "nodetool jsscript validate <id|file.json>",
635
+ kind: "static",
636
+ capabilities: ["json", "no-db"],
637
+ agentTool: "validate_js_script",
638
+ docs: "docs/harnesses.md § nodetool jsscript"
639
+ },
640
+ {
641
+ id: "jsscript-run",
642
+ title: "JS script single run (QuickJS sandbox)",
643
+ command: "nodetool jsscript run <id|file.json> --inputs '{...}'",
644
+ kind: "execution",
645
+ capabilities: ["json", "no-db"],
646
+ agentTool: "run_js_script",
647
+ docs: "docs/harnesses.md § nodetool jsscript"
648
+ },
649
+ {
650
+ id: "jsscript-test",
651
+ title: "JS script saved-case regression run",
652
+ command: "nodetool jsscript test <id|file.json>",
653
+ kind: "execution",
654
+ capabilities: ["json", "no-db"],
655
+ agentTool: "test_js_script",
656
+ docs: "docs/harnesses.md § nodetool jsscript",
657
+ selfcheck: {
658
+ // Three checked-in fixtures with deterministic cases: no network, no
659
+ // secrets, no database. One sums a buffered list input; one reads the
660
+ // numbers off `stream` from items the cases stage; one imports a shipped
661
+ // sandbox pack, which resolves only when the CLI installs the catalog.
662
+ command: "npm run dev:nodetool -- jsscript test packages/cli-script/tests/fixtures/js-script-sum.json && " +
663
+ "npm run dev:nodetool -- jsscript test packages/cli-script/tests/fixtures/js-script-running-total.json && " +
664
+ "npm run dev:nodetool -- jsscript test packages/cli-script/tests/fixtures/js-script-timeline-pack.json",
665
+ cost: "cheap"
666
+ }
667
+ },
668
+ {
669
+ id: "jsscript-debug",
670
+ title: "JS script edit-session replay",
671
+ command: "nodetool jsscript debug <id|file.json> --interact '[...]'",
672
+ kind: "execution",
673
+ capabilities: ["json", "interact", "no-db"],
674
+ docs: "docs/harnesses.md § nodetool jsscript"
675
+ },
676
+ {
677
+ id: "jsscript-versions",
678
+ title: "JS script version history (snapshot, restore, validate the restore)",
679
+ command: "nodetool jsscript versions list|show|create|restore|delete <id> [<version>]",
680
+ kind: "execution",
681
+ capabilities: ["json"],
682
+ docs: "docs/harnesses.md § nodetool jsscript"
683
+ },
684
+ {
685
+ id: "eval",
686
+ title: "Agent evaluation suites (graph-planner, graph-e2e, code-gen, task-planner, subtask, codeact, tool-loop variants, app-build)",
687
+ command: "nodetool eval <suite> -p <provider> -m <model> [--min-success N]",
688
+ kind: "eval",
689
+ // workflow_dispatch only (.github/workflows/agent-eval.yml) — no PR or
690
+ // schedule trigger, so this is not a `gated:*` harness.
691
+ capabilities: ["json"],
692
+ docs: "packages/agents/AGENTS.md"
693
+ },
694
+ {
695
+ id: "chat-terminal",
696
+ title: "Terminal chat (keyboard, viewport, sessions, approvals, transport)",
697
+ command: "npm run test --workspace=packages/cli-chat -- tests/chat-app.test.ts tests/chat-media.test.ts tests/terminal-keyboard.test.ts tests/terminal-input.test.ts tests/terminal-mouse.test.ts tests/terminal-screen.test.ts tests/chat-prompts.test.ts tests/chat-sessions.test.ts tests/websocket-client.test.ts",
698
+ kind: "execution",
699
+ capabilities: ["no-db"],
700
+ docs: "docs/harnesses.md § nodetool chat",
701
+ selfcheck: {
702
+ command: "npm run test --workspace=packages/cli-chat -- tests/chat-app.test.ts tests/chat-media.test.ts tests/terminal-keyboard.test.ts tests/terminal-input.test.ts tests/terminal-mouse.test.ts tests/terminal-screen.test.ts tests/chat-prompts.test.ts tests/chat-sessions.test.ts tests/websocket-client.test.ts",
703
+ cost: "cheap",
704
+ suiteOnly: true
705
+ }
706
+ },
707
+ {
708
+ id: "chat-stdin",
709
+ title: "Headless chat agent (piped stdin)",
710
+ command: 'echo "<prompt>" | nodetool-chat -p <provider> -m <model>',
711
+ kind: "execution",
712
+ capabilities: [],
713
+ docs: "docs/harnesses.md § nodetool chat"
714
+ },
715
+ {
716
+ id: "telegram-bridge",
717
+ title: "Telegram messaging bridge (renderer, router, identity, adapter)",
718
+ command: "nodetool telegram serve",
719
+ kind: "execution",
720
+ capabilities: ["no-db"],
721
+ docs: "docs/telegram-bot-design.md",
722
+ selfcheck: {
723
+ // The bridge suites are hermetic (fake Bot API, fake socket, injected
724
+ // clock) and include the dependency-cone test; the websocket files run
725
+ // the identity routes, the shared link-code store, and the tRPC router.
726
+ command: "npm run test --workspace=packages/telegram && npx vitest run tests/integrations-routes.test.ts tests/link-codes.test.ts tests/trpc-integrations.test.ts --root packages/websocket",
727
+ cost: "cheap"
728
+ }
729
+ },
730
+ {
731
+ id: "backend-smoke",
732
+ title: "Packaged-backend smoke (bundle staging + /health boot)",
733
+ command: "npm run backend:smoke",
734
+ kind: "meta",
735
+ capabilities: ["gated:pr"],
736
+ docs: "AGENTS.md § Common Pitfalls",
737
+ selfcheck: { command: "npm run backend:smoke", cost: "expensive" }
738
+ },
739
+ {
740
+ id: "packed-core-cli",
741
+ title: "Core-only packed CLI install and execution smoke",
742
+ command: "node scripts/smoke-packed-cli.mjs --core-only",
743
+ kind: "meta",
744
+ capabilities: ["gated:pr"],
745
+ docs: "docs/harnesses.md § Packed CLI smoke",
746
+ selfcheck: {
747
+ command: "node scripts/smoke-packed-cli.mjs --core-only",
748
+ cost: "expensive"
749
+ }
750
+ },
751
+ {
752
+ id: "release-pins",
753
+ title: "Release catalog integrity, install measurement and packing order",
754
+ command: "node scripts/run-vitest.mjs run --config vitest.config.ts scripts/__tests__/pin-catalog.test.mjs scripts/__tests__/publish-packages.test.mjs scripts/__tests__/install-pins.test.mjs scripts/__tests__/staging-release.test.mjs scripts/__tests__/publish-tarballs.test.mjs",
755
+ kind: "meta",
756
+ capabilities: ["gated:pr"],
757
+ docs: "docs/harnesses.md § Release catalog pins",
758
+ selfcheck: {
759
+ command: "node scripts/run-vitest.mjs run --config vitest.config.ts scripts/__tests__/pin-catalog.test.mjs scripts/__tests__/publish-packages.test.mjs scripts/__tests__/install-pins.test.mjs scripts/__tests__/staging-release.test.mjs scripts/__tests__/publish-tarballs.test.mjs",
760
+ cost: "cheap"
761
+ }
762
+ },
763
+ {
764
+ id: "docker-smoke",
765
+ title: "Deploy-image smoke (build, boot, load app in a browser)",
766
+ command: "node scripts/docker-smoke.mjs http://localhost:7777",
767
+ kind: "meta",
768
+ capabilities: ["browser", "gated:pr"],
769
+ docs: "AGENTS.md § Common Pitfalls"
770
+ },
771
+ {
772
+ id: "antigravity-install",
773
+ title: "Managed Antigravity CLI download and provider discovery",
774
+ command: "npm run test --workspace=packages/antigravity-cli",
775
+ kind: "static",
776
+ capabilities: ["no-db"],
777
+ docs: "packages/antigravity-cli/README.md",
778
+ selfcheck: {
779
+ command: "npm run test --workspace=packages/antigravity-cli",
780
+ cost: "cheap",
781
+ suiteOnly: true
782
+ }
783
+ },
784
+ {
785
+ id: "provider-contract",
786
+ title: "Provider contract probes (raw response fixtures + one live request per provider)",
787
+ command: "npm run probe:providers [--json] [--out report.json]",
788
+ kind: "meta",
789
+ // Nightly-only (.github/workflows/provider-contract-probe.yml: schedule +
790
+ // workflow_dispatch, no pull_request) — the offline selfcheck below is
791
+ // exercised there, not on every PR.
792
+ capabilities: ["json", "gated:nightly"],
793
+ docs: "docs/provider-contract-probes.md",
794
+ selfcheck: {
795
+ // The offline half: every manifest entry decodes its checked-in raw
796
+ // response, and every declared required field is removed once to prove
797
+ // the check can fail. No key, no network.
798
+ command: "npm run test --workspace=packages/runtime -- provider-contract-probes",
799
+ cost: "cheap"
800
+ }
801
+ },
802
+ {
803
+ id: "affected",
804
+ title: "Changed-file → workspace mapping",
805
+ command: "nodetool affected [--base main]",
806
+ kind: "meta",
807
+ capabilities: ["json", "no-db"],
808
+ docs: "docs/harnesses.md § nodetool affected"
809
+ },
810
+ {
811
+ id: "packs-compile",
812
+ title: "Sandbox npm module compiler (bundle, scan, probe, cache) and the shipped bridge packs",
813
+ command: "nodetool packs compile [--json] [--force]",
814
+ kind: "static",
815
+ capabilities: ["json", "no-db"],
816
+ docs: "docs/sandbox-package-design.md § Config-only modules from npm packages",
817
+ selfcheck: {
818
+ command: "npm run test --workspace=packages/sandbox-compiler",
819
+ cost: "cheap"
820
+ }
821
+ },
822
+ {
823
+ id: "script-storyboard-link",
824
+ title: "Script ↔ storyboard link (extract, scaffold, joint assemble, link validation)",
825
+ // No CLI command owns the link: it is pure functions in protocol and
826
+ // timeline plus the tools that call them. The checked-in suites are the
827
+ // headless surface — they build a linked document and hand the assembled
828
+ // timeline to the same validator `nodetool timeline validate` runs.
829
+ //
830
+ // The guided-creation-flow suites ride here too (PRD § 7.7): prompt
831
+ // composition, scene ordering, staleness, the Director's scene output and
832
+ // the P5 import parsers (`parseFdx`, `verifyImportedText`,
833
+ // `parseShotlistCsv`) are pure functions on the same documents, and a diff
834
+ // that touches one usually touches the link. `web/src/components/setup`
835
+ // rides along because the flow steps are where those functions are wired:
836
+ // an FDX import that stops reading back verbatim shows up there first.
837
+ command: SCRIPT_STORYBOARD_LINK_SUITES,
838
+ kind: "static",
839
+ capabilities: ["no-db"],
840
+ docs: "docs/script-storyboard-link/design.md § 6",
841
+ selfcheck: {
842
+ command: SCRIPT_STORYBOARD_LINK_SUITES,
843
+ cost: "cheap"
844
+ }
845
+ },
846
+ {
847
+ id: "workflow-plan",
848
+ title: "Workflow plan → graph (planner contract, builder, inspiration chips)",
849
+ // No CLI command owns the planner: the surface is a plan on a workflow's
850
+ // `settings.setup` and the graph it builds. The checked-in suites are the
851
+ // headless surface — they build every shipped chip's plan against the real
852
+ // node registry and hand the graph to the same validator
853
+ // `validate_workflow` runs. What a graph *produces* is graded one harness
854
+ // over, by the app-build suite's `workflow-plan-to-graph` case (PRD R6).
855
+ command: WORKFLOW_PLAN_SUITES,
856
+ kind: "static",
857
+ capabilities: ["no-db"],
858
+ docs: "docs/creation-flows/prd.md § 11",
859
+ selfcheck: { command: WORKFLOW_PLAN_SUITES, cost: "cheap" }
860
+ },
861
+ {
862
+ id: "game-flow",
863
+ title: "Native game engine (validate, simulate, render, stage, persist)",
864
+ command: GAME_FLOW_SUITES,
865
+ kind: "static",
866
+ capabilities: ["no-db"],
867
+ docs: "docs/plans/builtin-game-engine-design.md",
868
+ selfcheck: { command: GAME_FLOW_SUITES, cost: "cheap" }
869
+ },
870
+ {
871
+ id: "game-perf",
872
+ title: "Native game performance (tick bench, frame counters, exported frame time)",
873
+ command: "nodetool game perf <game_file> --inputs <route.json> --fail-on-budget",
874
+ kind: "execution",
875
+ capabilities: ["json", "browser", "no-db"],
876
+ docs: "docs/harnesses.md § Native game performance",
877
+ selfcheck: { command: GAME_PERF_SUITES, cost: "cheap" }
878
+ },
879
+ {
880
+ id: "capability-suites",
881
+ title: "Agent capability suites (per-capability contract tests)",
882
+ // No CLI command owns a capability: the surface is the wire name a guest
883
+ // or a model calls. The checked-in suites are the headless surface, and
884
+ // `packages/cli-harness/src/harness/capability-table.ts` says which suite covers
885
+ // which capability — the audit fails on one that names none.
886
+ command: CAPABILITY_SUITES,
887
+ kind: "static",
888
+ // Runs in CI as part of the whole-package `--filter=@nodetool-ai/agents`
889
+ // leg, but no workflow names this filtered command specifically, so it
890
+ // is not a `gated:*` harness by the substring-in-a-workflow rule.
891
+ capabilities: ["no-db"],
892
+ docs: "packages/agents/AGENTS.md § Capability coverage",
893
+ selfcheck: {
894
+ // `capabilities:check` re-derives the table from the live registry, so
895
+ // a capability added without a mapping fails here rather than in review.
896
+ command: "npm run capabilities:check && " + CAPABILITY_SUITES,
897
+ cost: "cheap"
898
+ }
899
+ },
900
+ {
901
+ id: "generation-recovery",
902
+ title: "Durable generation recovery (leases, FAL queue, and webhook inbox)",
903
+ command: GENERATION_RECOVERY_SUITES,
904
+ kind: "execution",
905
+ capabilities: ["no-db"],
906
+ docs: "docs/harnesses.md § Durable generation recovery selfcheck",
907
+ selfcheck: { command: GENERATION_RECOVERY_SUITES, cost: "cheap" }
908
+ },
909
+ {
910
+ id: "atlascloud-callbacks",
911
+ title: "AtlasCloud prediction callbacks (Ed25519 ingress and the wait it settles)",
912
+ command: ATLASCLOUD_CALLBACK_SUITES,
913
+ kind: "static",
914
+ capabilities: ["no-db"],
915
+ docs: "docs/developer/providers/atlascloud.md § Prediction callbacks (webhooks)",
916
+ selfcheck: { command: ATLASCLOUD_CALLBACK_SUITES, cost: "cheap" }
917
+ },
918
+ {
919
+ id: "graph-resources",
920
+ title: "Graph resources (recast, fill, retarget, slot prompts — pure suites plus fake-mode runs of the shipped examples)",
921
+ // Two halves, and the second is what the first cannot claim. The suites
922
+ // pin every derivation on fixtures; `fixtures:graph-resources` seeds a
923
+ // scratch install with a template board, its approved cut and an entity
924
+ // library, then runs the example graphs through `nodetool debug` on the
925
+ // fake provider — no key, no network — and reads the result back out of
926
+ // the database: E1's overlay has to come out filled on the exported
927
+ // sequence, E4's retargets have to leave every clip's start and duration
928
+ // where the approved cut put them, E3's export has to write every slot the
929
+ // platformer manifest declares.
930
+ command: `${GRAPH_RESOURCES_SUITES} && npm run fixtures:graph-resources`,
931
+ kind: "execution",
932
+ capabilities: [],
933
+ docs: "docs/harnesses.md § graph-resource fixtures",
934
+ selfcheck: {
935
+ command: `${GRAPH_RESOURCES_SUITES} && npm run fixtures:graph-resources`,
936
+ cost: "cheap"
937
+ }
938
+ },
939
+ {
940
+ id: "jtbd",
941
+ title: "Jobs to be done (end-to-end agent jobs, recorded for review)",
942
+ command: "nodetool jtbd <list|run|optimize> [-p <provider> -m <model>]",
943
+ kind: "eval",
944
+ capabilities: ["json"],
945
+ docs: "docs/harnesses.md § nodetool jtbd",
946
+ selfcheck: {
947
+ // Keyless: the catalogue's own invariants (every job states a purpose,
948
+ // grades an outcome, and fails its checks on an untouched world) plus
949
+ // the transcript capture the review step reads. No model is called.
950
+ command: "npm run test --workspace=packages/agents -- jtbd-friction jtbd-transcript",
951
+ cost: "cheap"
952
+ }
953
+ },
954
+ {
955
+ id: "provider-codegen",
956
+ title: "Generated provider metadata drift (FAL and KIE fixture mode)",
957
+ command: "npm run generate:fal:check && npm run generate:kie:check",
958
+ kind: "static",
959
+ capabilities: ["json", "no-db", "gated:pr"],
960
+ docs: "AGENTS.md § Common Pitfalls",
961
+ selfcheck: {
962
+ command: "npm run generate:fal:check -- --strict && " +
963
+ "npm run generate:kie:check -- --strict",
964
+ cost: "cheap"
965
+ }
966
+ },
967
+ {
968
+ id: "node-catalog",
969
+ title: "Node catalog ownership and metadata drift",
970
+ command: "npm run check:node-catalog",
971
+ kind: "static",
972
+ capabilities: ["no-db", "gated:pr"],
973
+ docs: "docs/cli-pack-system-plan.md § A1. The node catalog",
974
+ selfcheck: {
975
+ command: "npm run check:node-catalog && node scripts/run-vitest.mjs run --config vitest.config.ts scripts/__tests__/node-catalog.test.mjs scripts/__tests__/bump-version.test.mjs tools/oxlint/pack-boundaries/tests/no-direct-pack-imports.test.ts",
976
+ cost: "cheap"
977
+ }
978
+ },
979
+ {
980
+ id: "mutator",
981
+ title: "Mutation testing (nodetool-mutator)",
982
+ command: "npm run mutate -- --base origin/main packages/<name>/src",
983
+ kind: "static",
984
+ capabilities: ["no-db"],
985
+ docs: "docs/harnesses.md § npm run mutate",
986
+ selfcheck: {
987
+ // Parses and binds every site in the tool's own source through the
988
+ // built CLI, without running a test.
989
+ command: "npm run mutate -- --scan packages/mutator/src",
990
+ cost: "cheap"
991
+ }
992
+ },
993
+ {
994
+ id: "recipes",
995
+ title: "Recipe chains (shipped manifests, the app listing, the site pages)",
996
+ // A recipe names shipped example workflows and stores no graph, so what
997
+ // rots is a step whose example was renamed: the app drops the recipe from
998
+ // its Examples listing and the site's page loses a workflow. Both readers
999
+ // are checked — the resolver the app calls, and the generator the site
1000
+ // builds its pages and bundles with.
1001
+ command: "npx tsx marketing/scripts/generate-recipes.mjs --check",
1002
+ kind: "static",
1003
+ capabilities: ["no-db", "gated:pr"],
1004
+ docs: "docs/harnesses.md § Shipped recipes",
1005
+ selfcheck: {
1006
+ command: "npm run test --workspace=packages/websocket -- example-recipes && " +
1007
+ "npx tsx marketing/scripts/generate-recipes.mjs --check",
1008
+ cost: "cheap"
1009
+ }
1010
+ },
1011
+ {
1012
+ id: "repo-scripts",
1013
+ title: "Repo tooling scripts (test selection, validators, generators)",
1014
+ // The scripts a contributor and CI run from the repo root. Their pure
1015
+ // logic (test-affected's plan builder, the validators' rules) is pinned
1016
+ // by the suites under scripts/__tests__; a script with no suite is
1017
+ // covered only by the CI leg that runs it.
1018
+ command: "npm run test:scripts",
1019
+ kind: "meta",
1020
+ capabilities: ["json", "no-db"],
1021
+ docs: "AGENTS.md § Build, Lint & Test Commands",
1022
+ selfcheck: { command: "npm run test:scripts", cost: "cheap" }
1023
+ },
1024
+ {
1025
+ id: "workflow-contracts",
1026
+ title: "GitHub workflow contracts (release gates, scheduled agent routines)",
1027
+ // Parses the workflow YAML and pins the shape the release and agent
1028
+ // routines depend on, and unit-tests the scripts they call.
1029
+ command: "node --test .github/tests/docker-deploy.test.mjs .github/tests/agent-workflows.test.mjs",
1030
+ kind: "static",
1031
+ capabilities: ["no-db"],
1032
+ docs: ".github/workflows/README.md § Scheduled agent workflows",
1033
+ selfcheck: {
1034
+ command: "node --test .github/tests/docker-deploy.test.mjs .github/tests/agent-workflows.test.mjs",
1035
+ cost: "cheap"
1036
+ }
1037
+ },
1038
+ {
1039
+ id: "tutorial-rendering",
1040
+ title: "Tutorial render catalog, camera, and replay checks",
1041
+ command: "npm run test --workspace=demo",
1042
+ kind: "static",
1043
+ capabilities: ["no-db"],
1044
+ docs: "docs/plans/tutorial-focus-animation.md",
1045
+ selfcheck: {
1046
+ command: "npm run test --workspace=demo",
1047
+ cost: "cheap",
1048
+ suiteOnly: true
1049
+ }
1050
+ },
1051
+ {
1052
+ id: "harness-audit",
1053
+ title: "Harness coverage audit (this registry)",
1054
+ command: "nodetool harness audit [--strict]",
1055
+ kind: "meta",
1056
+ // Not run by name in any workflow today (the invariant it checks is
1057
+ // enforced by the registry test below, in the normal test:packages leg),
1058
+ // so it carries no `gated:*` tag under the substring-in-a-workflow rule.
1059
+ capabilities: ["json", "no-db"],
1060
+ docs: "docs/HARNESS_FIRST.md",
1061
+ selfcheck: {
1062
+ command: "npm run dev:nodetool -- harness audit && " +
1063
+ "npm run test --workspace=packages/cli-harness -- harness-registry.test.ts " +
1064
+ "harness-gate-cli.test.ts capability-coverage.test.ts",
1065
+ cost: "cheap"
1066
+ }
1067
+ },
1068
+ {
1069
+ id: "api-routes",
1070
+ title: "API server routes (Fastify HTTP + WebSocket)",
1071
+ command: "nodetool serve",
1072
+ kind: "execution",
1073
+ capabilities: ["no-db"],
1074
+ docs: "docs/harnesses.md § nodetool serve",
1075
+ selfcheck: {
1076
+ // The websocket package's own suite: routes, the /ws runner, MCP tool
1077
+ // resolution, drain/shutdown. Multi-minute (255 files), so this stays
1078
+ // an --expensive selfcheck rather than the default gate.
1079
+ command: "npm run test --workspace=packages/websocket",
1080
+ cost: "expensive",
1081
+ suiteOnly: true
1082
+ }
1083
+ },
1084
+ {
1085
+ id: "data-models",
1086
+ title: "Data models (Drizzle ORM persistence layer)",
1087
+ command: "npm run test --workspace=packages/models",
1088
+ kind: "static",
1089
+ capabilities: ["no-db"],
1090
+ docs: "packages/models/AGENTS.md",
1091
+ selfcheck: {
1092
+ command: "npm run test --workspace=packages/models",
1093
+ cost: "cheap",
1094
+ suiteOnly: true
1095
+ }
1096
+ },
1097
+ {
1098
+ id: "analytics",
1099
+ title: "Product analytics (event schema, privacy validation, consent client)",
1100
+ command: "npm run test --workspace=packages/analytics",
1101
+ kind: "static",
1102
+ capabilities: ["no-db"],
1103
+ docs: "docs/analytics.md",
1104
+ selfcheck: {
1105
+ command: "npm run test --workspace=packages/analytics",
1106
+ cost: "cheap",
1107
+ suiteOnly: true
1108
+ }
1109
+ },
1110
+ {
1111
+ id: "storage-and-security",
1112
+ title: "Storage, security, auth, and config packages",
1113
+ command: "npm run test --workspace=packages/storage && " +
1114
+ "npm run test --workspace=packages/security && " +
1115
+ "npm run test --workspace=packages/auth && " +
1116
+ "npm run test --workspace=packages/config",
1117
+ kind: "static",
1118
+ capabilities: ["no-db"],
1119
+ docs: "packages/AGENTS.md",
1120
+ selfcheck: {
1121
+ command: "npm run test --workspace=packages/storage && " +
1122
+ "npm run test --workspace=packages/security && " +
1123
+ "npm run test --workspace=packages/auth && " +
1124
+ "npm run test --workspace=packages/config",
1125
+ cost: "cheap"
1126
+ }
1127
+ },
1128
+ {
1129
+ id: "node-pack-parity",
1130
+ title: "Node-pack example-workflow parity (every shipped node covered by an example)",
1131
+ command: "npm test --workspace=@nodetool-ai/base-nodes -- parity example-workflows",
1132
+ kind: "static",
1133
+ // Runs in the quality-checks.yml typecheck leg (pull_request-triggered),
1134
+ // by this exact command.
1135
+ capabilities: ["no-db", "gated:pr"],
1136
+ docs: "packages/base-nodes/AGENTS.md",
1137
+ selfcheck: {
1138
+ command: "npm test --workspace=@nodetool-ai/base-nodes -- parity example-workflows",
1139
+ cost: "cheap"
1140
+ }
1141
+ },
1142
+ {
1143
+ id: "shared-services",
1144
+ title: "Shared service libraries (vector store, model pricing)",
1145
+ command: "npm run test --workspace=packages/vectorstore && " +
1146
+ "npm run test --workspace=packages/model-pricing",
1147
+ kind: "static",
1148
+ capabilities: ["no-db"],
1149
+ docs: "packages/AGENTS.md",
1150
+ selfcheck: {
1151
+ command: "npm run test --workspace=packages/vectorstore && " +
1152
+ "npm run test --workspace=packages/model-pricing",
1153
+ cost: "cheap"
1154
+ }
1155
+ },
1156
+ {
1157
+ id: "execution-session-audit",
1158
+ title: "Execution session hydration audit (registry handed without a resolver)",
1159
+ command: "npm run test --workspace=packages/execution -- execution-session-hydration-audit",
1160
+ kind: "static",
1161
+ // No workflow names this test file by substring today (it runs inside
1162
+ // the whole-package `packages/execution` leg), so no `gated:*` tag.
1163
+ capabilities: ["no-db"],
1164
+ docs: "docs/HARNESS_FIRST.md",
1165
+ selfcheck: {
1166
+ command: "npm run test --workspace=packages/execution -- execution-session-hydration-audit",
1167
+ cost: "cheap"
1168
+ }
1169
+ }
1170
+ ];
1171
+ export const SURFACES = [
1172
+ ...CLI_COMMAND_PACKAGES.map((name) => ({
1173
+ id: `${name}-module`,
1174
+ title: `Command package ${name}`,
1175
+ harnesses: [`${name}-module`],
1176
+ paths: [`packages/${name}/`]
1177
+ })),
1178
+ {
1179
+ id: "cli-harness-commands",
1180
+ title: "Harness, affected-workspace, evaluation and reliability commands",
1181
+ harnesses: ["cli-harness-commands"],
1182
+ paths: ["packages/cli-harness/"]
1183
+ },
1184
+ {
1185
+ id: "lean-server",
1186
+ title: "Separately installed execution server and explicit features",
1187
+ harnesses: ["lean-server", "lean-server-packed"],
1188
+ paths: [
1189
+ ...LEAN_SERVER_PACKAGES.map(name => `packages/${name}/`),
1190
+ "scripts/minimal-",
1191
+ "scripts/__tests__/minimal-",
1192
+ "scripts/generate-minimal-feature-catalog.mjs"
1193
+ ]
1194
+ },
1195
+ {
1196
+ id: "telegram-bridge",
1197
+ title: "Telegram bot bridge + messaging-integration identity",
1198
+ harnesses: ["telegram-bridge"],
1199
+ paths: [
1200
+ "packages/telegram/",
1201
+ "packages/websocket/src/routes/integrations.ts",
1202
+ "packages/websocket/src/lib/link-codes.ts",
1203
+ "packages/websocket/src/trpc/routers/integrations.ts",
1204
+ "packages/auth/src/providers/delegated-token-provider.ts",
1205
+ "packages/models/src/external-identity.ts"
1206
+ ]
1207
+ },
1208
+ {
1209
+ id: "workflow-execution",
1210
+ title: "Workflow execution (kernel runner)",
1211
+ harnesses: [
1212
+ "validate",
1213
+ "debug",
1214
+ "node-run",
1215
+ "reliability-ring0",
1216
+ "execution-session-audit"
1217
+ ],
1218
+ paths: [
1219
+ "packages/protocol/",
1220
+ "packages/kernel/",
1221
+ "packages/execution/",
1222
+ "packages/runtime/",
1223
+ "packages/node-sdk/",
1224
+ "packages/base-nodes/",
1225
+ "reliability/",
1226
+ // The CLI-facing example graphs. `npm run validate:examples` scans them,
1227
+ // so a diff that edits one runs the validate selfcheck.
1228
+ "examples/workflows/"
1229
+ ]
1230
+ },
1231
+ {
1232
+ id: "agent-capabilities",
1233
+ title: "Agent capabilities (the wire names a guest or a model calls)",
1234
+ harnesses: ["capability-suites", "eval", "jtbd"],
1235
+ paths: [
1236
+ "packages/agents/src/capabilities/",
1237
+ // The signal analysis behind the `analyze_*` / `detect_*` capabilities.
1238
+ // It exists only to serve them, so a change here is a change to them.
1239
+ "packages/agents/src/analysis/",
1240
+ "packages/agents/src/evals/",
1241
+ "packages/agents/src/jtbd/",
1242
+ "packages/cli-harness/src/harness/capability-table.ts",
1243
+ "packages/cli-harness/src/harness/capability-coverage.ts",
1244
+ "scripts/sync-capability-coverage.mjs"
1245
+ ]
1246
+ },
1247
+ {
1248
+ id: "generation-tracking",
1249
+ title: "Media generation tracking (the seam, the tracker, the generations capabilities)",
1250
+ harnesses: ["capability-suites", "generation-recovery"],
1251
+ // `capability-suites` runs the tracker, seam and capability suites named
1252
+ // in the capability table, plus the seam audit that fails on a provider
1253
+ // media call outside `runGeneration` (packages/execution/tests/
1254
+ // generation-seam-audit.test.ts). Design:
1255
+ // docs/media-generation-tracking-design.md.
1256
+ paths: [
1257
+ "packages/execution/src/generation-tracker.ts",
1258
+ "packages/execution/src/generation-lifecycle.ts",
1259
+ "packages/execution/src/generation-recovery-worker.ts",
1260
+ "packages/execution/src/fal-output-decoder.ts",
1261
+ "packages/models/src/durable-generation.ts",
1262
+ "packages/models/src/schema/generation-",
1263
+ "packages/models/src/schema-pg/generation-",
1264
+ "packages/runtime/src/generation-receipt.ts",
1265
+ "packages/runtime/src/generation-registry.ts",
1266
+ "packages/runtime/src/providers/fal-queue.ts",
1267
+ "packages/runtime/src/providers/provider-queue.ts",
1268
+ "packages/runtime/src/redact-params.ts",
1269
+ "packages/websocket/src/routes/fal-webhook.ts",
1270
+ "packages/websocket/src/generation-recovery.ts",
1271
+ "packages/agents/src/capabilities/generations.ts",
1272
+ "packages/agents/src/capabilities/generations.specs.ts",
1273
+ "packages/cli-workflow/src/commands/generations.ts"
1274
+ ]
1275
+ },
1276
+ {
1277
+ id: "live-browser",
1278
+ title: "Live browser (browser_* capabilities, CDP, the Chrome extension relay)",
1279
+ harnesses: ["capability-suites"],
1280
+ // `capability-suites` covers the seam — dispatch, classification, and what
1281
+ // a process with no action layer answers. The half below it runs in
1282
+ // `npm run test:integration --workspace=packages/browser`, which
1283
+ // launches a real Chrome with the built extension loaded and its native
1284
+ // host registered, and drives the production transport end to end. It is
1285
+ // not a selfcheck: it downloads Chrome, so a diff to the relay still
1286
+ // needs it run by hand.
1287
+ paths: [
1288
+ "chrome-extension/",
1289
+ "packages/browser/",
1290
+ "packages/agents/src/capabilities/browser.ts",
1291
+ "packages/agents/src/capabilities/browser.specs.ts"
1292
+ ]
1293
+ },
1294
+ {
1295
+ id: "antigravity-install",
1296
+ title: "Managed Antigravity CLI installation",
1297
+ paths: ["packages/antigravity-cli/"],
1298
+ harnesses: ["antigravity-install"]
1299
+ },
1300
+ {
1301
+ id: "provider-clients",
1302
+ title: "LLM and media provider clients (request shaping + response decoding)",
1303
+ harnesses: ["provider-contract", "eval"],
1304
+ paths: [
1305
+ "packages/runtime/src/providers/",
1306
+ // Local-models BaseProvider (transformers.js/kokoro-js) — same request/
1307
+ // response contract as the hosted providers, packaged separately
1308
+ // because its weights and native deps are optional.
1309
+ "packages/transformers-js-provider/"
1310
+ ]
1311
+ },
1312
+ {
1313
+ id: "atlascloud-callbacks",
1314
+ title: "AtlasCloud prediction callbacks (signed ingress, pending registry)",
1315
+ harnesses: ["atlascloud-callbacks"],
1316
+ paths: [
1317
+ "packages/runtime/src/providers/atlascloud-webhook-registry.ts",
1318
+ "packages/runtime/src/providers/atlascloud-transport.ts",
1319
+ "packages/websocket/src/routes/atlascloud-webhook.ts"
1320
+ ]
1321
+ },
1322
+ {
1323
+ id: "workflow-authoring",
1324
+ title: "Workflow authoring (planners + ui_* graph tools)",
1325
+ harnesses: ["validate", "eval"],
1326
+ paths: ["packages/agents/", "packages/dsl/"]
1327
+ },
1328
+ {
1329
+ id: "dsl-native-flow",
1330
+ title: "Native flow (typed node calls from guest code)",
1331
+ harnesses: ["dsl-native-flow", "packs-compile"],
1332
+ paths: [
1333
+ "packages/dsl/src/flow/",
1334
+ "packages/agents/src/capabilities/flow.ts",
1335
+ "packages/agents/src/capabilities/flow.specs.ts",
1336
+ "packages/sandbox-packs/sandbox-flow/"
1337
+ ]
1338
+ },
1339
+ {
1340
+ id: "workflow-creation-flow",
1341
+ title: "Workflow creation flow (settings.setup, planner, plan review, build from plan)",
1342
+ harnesses: ["workflow-plan", "app-build", "capability-suites", "validate"],
1343
+ paths: [
1344
+ "packages/protocol/src/workflow-plan.ts",
1345
+ "packages/protocol/src/api-schemas/workflows.ts",
1346
+ "packages/agents/src/evals/app-build-plan.ts",
1347
+ "packages/base-nodes/tests/workflow-plan-chips.test.ts",
1348
+ "web/src/components/setup/workflow/",
1349
+ "web/src/hooks/workflow/",
1350
+ "web/src/lib/tools/builtin/workflowSetup.ts"
1351
+ ]
1352
+ },
1353
+ {
1354
+ id: "game-creation-flow",
1355
+ title: "Native game creation and editing",
1356
+ harnesses: ["game-flow", "game-perf", "capability-suites", "validate"],
1357
+ paths: [
1358
+ "packages/cli-game/src/commands/game.ts",
1359
+ "packages/protocol/src/game.ts",
1360
+ "packages/protocol/src/game3d.ts",
1361
+ "packages/protocol/src/game-design.ts",
1362
+ "packages/protocol/src/game-graph.ts",
1363
+ "packages/protocol/src/game-flow-prompt.ts",
1364
+ "packages/protocol/src/game-screens.ts",
1365
+ "packages/protocol/src/game2d/index.ts",
1366
+ "packages/protocol/src/game3d/index.ts",
1367
+ "packages/protocol/src/native-game-templates.ts",
1368
+ "packages/protocol/src/style-presets.ts",
1369
+ "packages/game-nodes/",
1370
+ "packages/game-runtime/",
1371
+ "packages/game-renderer/",
1372
+ "packages/base-nodes/tests/game-graph-chips.test.ts",
1373
+ "packages/websocket/src/trpc/routers/games.ts",
1374
+ "packages/websocket/src/trpc/routers/gameDraftHistory.ts",
1375
+ "packages/websocket/src/lib/style-presets.ts",
1376
+ "web/src/components/game/",
1377
+ "web/src/components/setup/game/",
1378
+ "web/src/hooks/game/",
1379
+ "web/src/stores/game/",
1380
+ "web/tests/journeys/native-game3d-editor.spec.ts",
1381
+ "web/src/components/workspace/GameSurface.tsx",
1382
+ "web/src/components/projects/NewProjectSurface.tsx",
1383
+ "packages/protocol/src/game-script-params.ts"
1384
+ ]
1385
+ },
1386
+ {
1387
+ id: "app-runs",
1388
+ title: "App instances and durable operation runs",
1389
+ harnesses: ["app-runs"],
1390
+ paths: [
1391
+ "packages/models/src/app-instance.ts",
1392
+ "packages/models/src/migrations/versions.ts",
1393
+ "packages/protocol/src/api-schemas/app-runs.ts",
1394
+ "packages/execution/src/service/app-operation.ts",
1395
+ "packages/execution/src/app-run-generation.ts",
1396
+ "packages/websocket/src/lib/app-instances-service.ts",
1397
+ "packages/websocket/src/routes/app-runs.ts",
1398
+ "packages/websocket/src/trpc/routers/app-instances.ts",
1399
+ "packages/websocket/src/trpc/routers/app-runs.ts",
1400
+ "web/src/components/appbuilder/runtime/"
1401
+ ]
1402
+ },
1403
+ {
1404
+ id: "run-traces",
1405
+ title: "Server run trace persistence and privacy",
1406
+ harnesses: ["run-traces"],
1407
+ paths: [
1408
+ "packages/protocol/src/run-trace.ts",
1409
+ "packages/models/src/run-trace.ts",
1410
+ "packages/models/src/migrations/run-traces.ts",
1411
+ "packages/models/src/schema/run-traces.ts",
1412
+ "packages/models/src/schema-pg/run-traces.ts",
1413
+ "packages/runtime/src/run-trace-",
1414
+ "packages/runtime/src/telemetry.ts",
1415
+ "packages/runtime/src/trace-exporters.ts",
1416
+ "packages/runtime/src/providers/loop-tracing.ts",
1417
+ "packages/execution/src/run-trace-store.ts",
1418
+ "packages/execution/src/service/run-trace-lifecycle.ts",
1419
+ "packages/websocket/src/lib/http-tracing.ts",
1420
+ "packages/websocket/src/storage-retention.ts"
1421
+ ]
1422
+ },
1423
+ {
1424
+ id: "run-readers",
1425
+ title: "Common run inspection for agents and CLI",
1426
+ harnesses: ["run-readers"],
1427
+ paths: [
1428
+ "packages/protocol/src/run-readers.ts",
1429
+ "packages/protocol/src/api-schemas/runs.ts",
1430
+ "packages/models/src/run-readers.ts",
1431
+ "packages/execution/src/runs.ts",
1432
+ "packages/execution/src/app-debug/run-recording.ts",
1433
+ "packages/execution/src/service/app-debug-service.ts",
1434
+ "packages/agents/src/capabilities/runs",
1435
+ "packages/agents/src/capabilities/jobs.ts",
1436
+ "packages/agents/src/evals/codeact-api-runs.ts",
1437
+ "packages/cli-workflow/src/commands/runs.ts",
1438
+ "packages/cli-apps/src/app-debug/harness.ts",
1439
+ "packages/websocket/src/trpc/routers/runs.ts",
1440
+ "packages/websocket/src/lib/app-debug-service.ts"
1441
+ ]
1442
+ },
1443
+ {
1444
+ id: "run-inspection",
1445
+ title: "Browser run ingestion and durable UI inspection",
1446
+ harnesses: ["browser-run-spans", "run-inspection-ui", "run-inspection-browser"],
1447
+ paths: [
1448
+ "packages/protocol/src/browser-run-traces.ts",
1449
+ "packages/execution/src/browser-app-run.ts",
1450
+ "packages/execution/src/browser-run-spans.ts",
1451
+ "packages/execution/src/browser-trace-policy.ts",
1452
+ "packages/websocket/src/routes/run-spans.ts",
1453
+ "packages/websocket/src/lib/test-ui-run-routes.ts",
1454
+ "web/src/lib/browserRunTrace.ts",
1455
+ "web/src/lib/workflow/browserWorkflowRunner.ts",
1456
+ "web/src/components/panels/TracePanel.tsx",
1457
+ "web/src/components/appbuilder/runtime/",
1458
+ "web/src/components/appbuilder/AppRuntimeView.tsx",
1459
+ "web/src/components/appbuilder/puck/useWidgetRuntime.ts",
1460
+ "web/src/components/appbuilder/puck/conditionalWidget.tsx",
1461
+ "web/src/components/workspace/ChatSurface.tsx",
1462
+ "web/src/stores/ChatDraftStore.ts",
1463
+ "web/src/components/appbuilder/puck/AgentActivityWidget.tsx",
1464
+ "web/src/components/runs/",
1465
+ "web/src/serverState/useRuns.ts",
1466
+ "web/src/serverState/runTraceCache.ts",
1467
+ "web/src/serverState/appRunActivity.ts",
1468
+ "web/src/hooks/useAppOperationRun.ts",
1469
+ "web/src/hooks/useRunInspection.ts",
1470
+ "web/src/stores/TraceStore.ts",
1471
+ "web/tests/journeys/run-observability.spec.ts"
1472
+ ]
1473
+ },
1474
+ {
1475
+ id: "app-instance-history",
1476
+ title: "App instance management and historical results",
1477
+ harnesses: ["app-instance-history", "app-instance-history-ui", "app-instance-history-browser"],
1478
+ paths: [
1479
+ "packages/models/src/app-instance",
1480
+ "packages/models/src/run-readers.ts",
1481
+ "packages/execution/src/runs.ts",
1482
+ "packages/protocol/src/api-schemas/app-runs.ts",
1483
+ "packages/websocket/src/lib/app-instances-service.ts",
1484
+ "packages/websocket/src/routes/app-runs.ts",
1485
+ "packages/websocket/src/trpc/routers/app-instances.ts",
1486
+ "web/src/components/appbuilder/",
1487
+ "web/src/components/workspace/ApplicationSurface.tsx",
1488
+ "web/src/components/workspace/TabContent.tsx",
1489
+ "web/src/stores/WorkspaceTabsStore.ts",
1490
+ "web/src/serverState/useAppInstances.ts",
1491
+ "web/src/serverState/useRuns.ts",
1492
+ "web/tests/journeys/app-instances.spec.ts"
1493
+ ]
1494
+ },
1495
+ {
1496
+ id: "mini-apps",
1497
+ title: "Mini apps (documents, bindings, operations)",
1498
+ harnesses: ["app-debug", "app-build", "eval"],
1499
+ paths: [
1500
+ "packages/app-runtime/",
1501
+ "packages/app-export/",
1502
+ "packages/agents/src/app-build/",
1503
+ "packages/execution/src/app-debug/",
1504
+ "packages/base-nodes/nodetool/examples/apps/",
1505
+ // The directed-campaign kit's five stage graphs. They are excluded from
1506
+ // the auto-discovered e2e suite (each consumes the stage before it, so
1507
+ // none runs alone on its defaults), so name them here instead: a diff
1508
+ // touching one still selects the harnesses that cover the app.
1509
+ "packages/base-nodes/nodetool/examples/nodetool-base/Propose Three Campaign Directions.json",
1510
+ "packages/base-nodes/nodetool/examples/nodetool-base/Render a Directed Campaign Hero.json",
1511
+ "packages/base-nodes/nodetool/examples/nodetool-base/Compose Directed Campaign Formats.json",
1512
+ "packages/base-nodes/nodetool/examples/nodetool-base/Revise an Accepted Campaign Hero.json",
1513
+ "packages/base-nodes/nodetool/examples/nodetool-base/Reopen a Directed Campaign.json"
1514
+ ]
1515
+ },
1516
+ {
1517
+ id: "timeline",
1518
+ title: "Timeline sequences (ui_timeline_* tools, version history)",
1519
+ harnesses: [
1520
+ "timeline-validate",
1521
+ "timeline-debug",
1522
+ "timeline-render",
1523
+ "timeline-score",
1524
+ "timeline-versions",
1525
+ "eval"
1526
+ ],
1527
+ paths: [
1528
+ "demo/benchmarks/serein/",
1529
+ "packages/timeline/",
1530
+ "packages/execution/src/timeline-debug/",
1531
+ "packages/cli-timeline/src/timeline-debug/",
1532
+ "packages/cli-timeline/src/commands/timeline-versions.ts",
1533
+ "packages/cli-timeline/src/commands/timeline-render.ts",
1534
+ "packages/cli-timeline/src/commands/timeline-score.ts",
1535
+ "packages/video-nodes/src/nodes/timeline/compositeRender.ts",
1536
+ "packages/agents/src/capabilities/timelines.ts",
1537
+ "packages/agents/src/capabilities/timelines.specs.ts",
1538
+ "packages/models/src/timeline-sequence-version.ts",
1539
+ "packages/websocket/src/trpc/routers/timeline.ts",
1540
+ "scripts/motion-craft-eval.mjs"
1541
+ ]
1542
+ },
1543
+ {
1544
+ id: "serein-timeline-repro",
1545
+ title: "Serein timeline frame reproduction",
1546
+ harnesses: ["serein-timeline-repro"],
1547
+ paths: [
1548
+ "demo/benchmarks/serein/",
1549
+ "packages/agents/tests/timeline-serein-gaps-frames.test.ts"
1550
+ ]
1551
+ },
1552
+ {
1553
+ id: "timeline-model3d",
1554
+ title: "3D clips on the timeline (render session, camera channels, bake)",
1555
+ harnesses: ["timeline-model3d", "capability-suites"],
1556
+ // Overlaps the surfaces that claim these trees wholesale — `domain-nodes`
1557
+ // (packages/video-nodes/), `workflow-authoring` (packages/agents/) and
1558
+ // `web-editor` (web/) — so a diff here runs their checks as well.
1559
+ paths: [
1560
+ "packages/video-nodes/src/nodes/model3d/",
1561
+ "packages/agents/src/timeline-preview/",
1562
+ "web/src/components/timeline/preview/Model3DLayerSource.ts",
1563
+ "web/src/components/timeline/preview/bakeDecoding.ts",
1564
+ "web/src/components/timeline/Tracks/model3dClipFrames.ts"
1565
+ ]
1566
+ },
1567
+ {
1568
+ id: "timeline-native-media-editing",
1569
+ title: "Native media editing (generative candidates and explicit apply)",
1570
+ harnesses: ["timeline-native-media-editing"],
1571
+ paths: [
1572
+ "packages/timeline/src/generative.ts",
1573
+ "packages/agents/src/capabilities/timelines.ts",
1574
+ "packages/agents/src/capabilities/timeline-media-edit.ts",
1575
+ "packages/agents/src/capabilities/timeline-clip-frames.ts",
1576
+ "packages/agents/tests/timeline-headless-media-parity.test.ts",
1577
+ "packages/runtime/src/trim-video-window.ts",
1578
+ "packages/websocket/src/timeline-generation-recovery.ts",
1579
+ "packages/websocket/src/lib/media.ts",
1580
+ "packages/websocket/tests/generation-recovery-media.test.ts",
1581
+ "packages/timeline/src/extension.ts",
1582
+ "packages/timeline/src/extensionRequest.ts",
1583
+ "packages/timeline/src/takes.ts",
1584
+ "packages/protocol/src/api-schemas/timeline-tool-contracts.ts",
1585
+ "packages/protocol/src/api-schemas/timeline-tool-params.ts",
1586
+ "packages/agents/src/evals/surfaces/timeline.ts",
1587
+ "web/src/components/timeline/timelineAgentBridge.ts",
1588
+ "web/src/hooks/timeline/useTimelineAgentBridge.ts",
1589
+ "web/src/hooks/timeline/useTimelineDirectGenJob.ts",
1590
+ "web/src/hooks/timeline/directGenPending.ts",
1591
+ "web/src/hooks/timeline/useTimelineExtension.ts",
1592
+ "web/src/lib/timelineExtension.ts",
1593
+ "web/src/components/timeline/Inspector/ExtendClipPanel.tsx",
1594
+ "web/src/hooks/timeline/__tests__/nativeMediaEditingAcceptance.test.tsx",
1595
+ "web/src/components/timeline/Inspector/TimelineInspector.tsx",
1596
+ "web/src/components/timeline/preview/__tests__/nativeMediaEditingPreview.test.tsx",
1597
+ "web/src/lib/tools/builtin/timeline.ts"
1598
+ ]
1599
+ },
1600
+ {
1601
+ id: "timeline-audio-drive",
1602
+ title: "Audio-driven timeline motion (bake_audio_animation, audio analysis)",
1603
+ harnesses: ["timeline-audio-drive", "capability-suites"],
1604
+ // Overlaps the wholesale claims on `packages/timeline/` (surface
1605
+ // `timeline`) and `packages/agents/` (surface `workflow-authoring`), so a
1606
+ // diff here runs their checks too.
1607
+ paths: [
1608
+ "packages/timeline/src/animation/",
1609
+ "packages/agents/src/capabilities/timeline-audio-bake.ts",
1610
+ "packages/agents/src/capabilities/analysis.ts"
1611
+ ]
1612
+ },
1613
+ {
1614
+ id: "timeline-generated-matte",
1615
+ title: "Generated mattes (isolate_subject, set_generated_matte)",
1616
+ harnesses: ["timeline-generated-matte", "capability-suites"],
1617
+ // Overlaps the wholesale claims on `packages/timeline/` (surface
1618
+ // `timeline`) and `packages/agents/` (surface `workflow-authoring`), so a
1619
+ // diff here runs their checks too.
1620
+ paths: [
1621
+ "packages/timeline/src/generatedMatte.ts",
1622
+ "packages/agents/src/capabilities/timeline-isolate-subject.ts",
1623
+ "packages/websocket/src/routes/timeline-isolate-subject.ts"
1624
+ ]
1625
+ },
1626
+ {
1627
+ id: "sketch",
1628
+ title: "Sketches (image documents, ui_sketch_* tools, version history)",
1629
+ harnesses: [
1630
+ "sketch-validate",
1631
+ "sketch-debug",
1632
+ "sketch-render",
1633
+ "sketch-versions",
1634
+ "eval"
1635
+ ],
1636
+ paths: [
1637
+ // The paint core behind sketches: types, dependency hashing, seeded
1638
+ // layer templates, host-neutral raster ops.
1639
+ "packages/image-editor/",
1640
+ "packages/execution/src/sketch-debug/",
1641
+ "packages/cli-sketch/src/sketch-debug/",
1642
+ "packages/cli-sketch/src/commands/sketch-versions.ts",
1643
+ "packages/cli-sketch/src/commands/sketch-render.ts",
1644
+ "packages/cli-sketch/src/commands/sketch.ts",
1645
+ "packages/agents/src/sketch-render/",
1646
+ "packages/agents/src/capabilities/sketches.ts",
1647
+ "packages/agents/src/capabilities/sketches.specs.ts",
1648
+ "packages/models/src/image-document-version.ts",
1649
+ "packages/websocket/src/trpc/routers/sketch.ts"
1650
+ ]
1651
+ },
1652
+ {
1653
+ id: "jsscript",
1654
+ title: "JS scripts (documents, ui_jsscript_* tools, version history)",
1655
+ harnesses: [
1656
+ "jsscript-validate",
1657
+ "jsscript-run",
1658
+ "jsscript-test",
1659
+ "jsscript-debug",
1660
+ "jsscript-versions",
1661
+ "eval"
1662
+ ],
1663
+ paths: [
1664
+ "packages/execution/src/js-script-debug/",
1665
+ "packages/cli-script/src/js-script-debug/",
1666
+ "packages/cli-script/src/commands/js-script.ts",
1667
+ "packages/cli-script/src/commands/js-script-versions.ts",
1668
+ "packages/cli-script/src/commands/js-script-validation-output.ts",
1669
+ "packages/agents/src/capabilities/js-scripts.ts",
1670
+ "packages/agents/src/capabilities/js-scripts.specs.ts",
1671
+ "packages/agents/src/evals/surfaces/js-script.ts",
1672
+ "packages/models/src/js-script.ts",
1673
+ "packages/models/src/js-script-version.ts",
1674
+ "packages/protocol/src/api-schemas/js-scripts.ts",
1675
+ "packages/websocket/src/trpc/routers/js-scripts.ts",
1676
+ "packages/websocket/src/routes/js-scripts.ts"
1677
+ ]
1678
+ },
1679
+ {
1680
+ id: "storyboard",
1681
+ title: "Storyboards (shots, keyframes, clips, ui_storyboard_* tools, script link)",
1682
+ // `validate` covers the shipped example boards: its selfcheck,
1683
+ // `npm run validate:examples`, checks each one's shot text and media.
1684
+ harnesses: [
1685
+ "script-storyboard-link",
1686
+ "timeline-validate",
1687
+ "validate",
1688
+ "eval"
1689
+ ],
1690
+ paths: [
1691
+ "packages/protocol/src/script-link.ts",
1692
+ "packages/protocol/src/api-schemas/storyboards.ts",
1693
+ // The guided creation flow's pure modules (PRD § 7.7): one prompt
1694
+ // composition for both surfaces, the derived scene order, and the
1695
+ // staleness comparison the board's marks come from.
1696
+ "packages/protocol/src/shot-prompt.ts",
1697
+ "packages/protocol/src/render-record.ts",
1698
+ "packages/protocol/src/screenplay-authoring.ts",
1699
+ "web/src/lib/storyboard/",
1700
+ "web/src/components/setup/",
1701
+ "packages/timeline/src/storyboard.ts",
1702
+ "packages/timeline/src/script-link.ts",
1703
+ "packages/timeline/src/linked.ts",
1704
+ "packages/agents/src/capabilities/storyboards.ts",
1705
+ "packages/agents/src/capabilities/storyboards.specs.ts",
1706
+ "packages/agents/src/evals/surfaces/storyboard.ts",
1707
+ "packages/agents/src/evals/surfaces/creative-pipeline.ts",
1708
+ "packages/models/src/schema/storyboards.ts",
1709
+ "packages/websocket/src/trpc/routers/storyboards.ts",
1710
+ "packages/websocket/src/lib/example-storyboards.ts",
1711
+ // The shipped boards and the generator behind them. `validate:examples`
1712
+ // checks each one's shot text and that its stills and clips are on disk.
1713
+ "packages/base-nodes/nodetool/examples/storyboards/",
1714
+ "scripts/build-example-storyboards.mjs",
1715
+ "scripts/example-storyboards/",
1716
+ "web/src/components/storyboard/",
1717
+ "web/src/lib/tools/builtin/storyboard.ts",
1718
+ "web/src/studio/"
1719
+ ]
1720
+ },
1721
+ {
1722
+ id: "script",
1723
+ title: "Scripts (lines, cast, voicing, ui_script_* tools, storyboard link)",
1724
+ harnesses: ["script-storyboard-link", "timeline-validate", "eval"],
1725
+ paths: [
1726
+ "packages/protocol/src/script-link.ts",
1727
+ "packages/protocol/src/api-schemas/scripts.ts",
1728
+ "packages/timeline/src/script.ts",
1729
+ "packages/timeline/src/script-link.ts",
1730
+ "packages/timeline/src/linked.ts",
1731
+ "packages/agents/src/capabilities/scripts.ts",
1732
+ "packages/agents/src/capabilities/scripts.specs.ts",
1733
+ "packages/agents/src/evals/surfaces/script.ts",
1734
+ "packages/agents/src/evals/surfaces/creative-pipeline.ts",
1735
+ "packages/models/src/schema/scripts.ts",
1736
+ "packages/websocket/src/trpc/routers/scripts.ts",
1737
+ "web/src/components/script/",
1738
+ "web/src/lib/tools/builtin/script.ts",
1739
+ "web/src/studio/"
1740
+ ]
1741
+ },
1742
+ {
1743
+ id: "model3d",
1744
+ title: "3D models (glTF scenes, ui_3d_* tools, model3d capabilities)",
1745
+ harnesses: ["capability-suites", "eval"],
1746
+ paths: [
1747
+ "packages/model3d/",
1748
+ "packages/agents/src/capabilities/model3d.ts",
1749
+ "packages/agents/src/capabilities/model3d.specs.ts",
1750
+ "packages/agents/src/evals/surfaces/model3d.ts",
1751
+ "web/src/components/model_editor/",
1752
+ "web/src/components/workspace/Model3DSurface.tsx",
1753
+ "web/src/lib/tools/builtin/model3d.ts"
1754
+ ]
1755
+ },
1756
+ {
1757
+ id: "svg-editor",
1758
+ title: "SVG editor (markup draft, ui_svg_* tools, assistant panel)",
1759
+ harnesses: ["asset-editor-tools", "eval"],
1760
+ paths: [
1761
+ "packages/protocol/src/api-schemas/asset-editor-tool-contracts.ts",
1762
+ "packages/agents/src/evals/surfaces/svg.ts",
1763
+ "web/src/components/workspace/SvgSurface.tsx",
1764
+ "web/src/components/workspace/SvgPreview.tsx",
1765
+ "web/src/components/workspace/svgAgentBridge.ts",
1766
+ "web/src/components/workspace/AssetAgentPanel.tsx",
1767
+ "web/src/lib/tools/builtin/svg.ts"
1768
+ ]
1769
+ },
1770
+ {
1771
+ id: "audio-editor",
1772
+ title: "Audio sample editor (PCM edits, effects, ui_audio_* tools, assistant panel)",
1773
+ harnesses: ["asset-editor-tools", "eval"],
1774
+ paths: [
1775
+ "packages/protocol/src/api-schemas/asset-editor-tool-contracts.ts",
1776
+ "packages/agents/src/evals/surfaces/audio.ts",
1777
+ "web/src/components/audio_editor/",
1778
+ "web/src/components/workspace/AudioSurface.tsx",
1779
+ "web/src/components/workspace/AssetAgentPanel.tsx",
1780
+ "web/src/lib/tools/builtin/audio.ts"
1781
+ ]
1782
+ },
1783
+ {
1784
+ id: "native-game",
1785
+ title: "Native game assets, nodes, renderer, and agent capabilities",
1786
+ harnesses: ["capability-suites"],
1787
+ paths: [
1788
+ "packages/protocol/src/game-assets.ts",
1789
+ "packages/protocol/fixtures/game-assets/",
1790
+ "packages/game-runtime/",
1791
+ "packages/game-renderer/",
1792
+ "packages/image-nodes/src/nodes/game.ts",
1793
+ "packages/audio-nodes/src/nodes/game.ts",
1794
+ "packages/game-nodes/src/nodes/game.ts",
1795
+ "packages/agents/src/capabilities/game.ts",
1796
+ "packages/agents/src/capabilities/game.specs.ts",
1797
+ "packages/agents/src/capabilities/game3d.ts",
1798
+ "packages/agents/src/capabilities/game-asset-source.ts"
1799
+ ]
1800
+ },
1801
+ {
1802
+ id: "blender",
1803
+ title: "Blender headless render (op scripts, runner, blender nodes)",
1804
+ harnesses: ["blender"],
1805
+ paths: ["packages/blender-nodes/"]
1806
+ },
1807
+ {
1808
+ id: "manimgx",
1809
+ title: "ManimGX scenes (runner, nodes, authoring skill)",
1810
+ harnesses: ["manimgx"],
1811
+ paths: ["packages/manimgx-nodes/"]
1812
+ },
1813
+ {
1814
+ id: "entities",
1815
+ title: "Entity library (ingredients, prompt injection, ui_entity_* tools)",
1816
+ harnesses: ["capability-suites"],
1817
+ paths: [
1818
+ "packages/agents/src/capabilities/entities.ts",
1819
+ "packages/agents/src/capabilities/entities.specs.ts",
1820
+ "web/src/components/entities/",
1821
+ "web/src/serverState/useEntities.ts",
1822
+ "web/src/lib/tools/builtin/entities.ts"
1823
+ ]
1824
+ },
1825
+ {
1826
+ id: "graph-resources",
1827
+ title: "Graph resources (storyboard/entity/script/timeline/game-template nodes and the derivations under them)",
1828
+ // The documents a director approves, as graph values. The derivations are
1829
+ // pure and live in their own packages; the nodes are the only callers that
1830
+ // can get the write contract or the spend gates wrong, and the shipped
1831
+ // examples are the only place all of it is wired together.
1832
+ harnesses: ["graph-resources", "validate"],
1833
+ paths: [
1834
+ "packages/storyboard/",
1835
+ "packages/game-nodes/",
1836
+ "packages/timeline/src/retarget.ts",
1837
+ "packages/timeline/src/fill-text.ts",
1838
+ "packages/timeline/src/clone.ts",
1839
+ "packages/protocol/src/script-fill.ts",
1840
+ "packages/protocol/src/game-slot-prompt.ts",
1841
+ "packages/core-nodes/src/nodes/entity.ts",
1842
+ "packages/video-nodes/src/nodes/storyboard.ts",
1843
+ "packages/base-nodes/nodetool/examples/nodetool-base/Per-SKU Ad Factory.json",
1844
+ "packages/base-nodes/nodetool/examples/nodetool-base/Localized Explainer.json",
1845
+ "packages/base-nodes/nodetool/examples/nodetool-base/Top-down Native Asset Pack.json",
1846
+ "packages/base-nodes/nodetool/examples/nodetool-base/Three Ratios.json",
1847
+ "packages/cli-workflow/fixtures/graph-resources/",
1848
+ "scripts/graph-resources-fixtures.mjs"
1849
+ ]
1850
+ },
1851
+ {
1852
+ id: "sandbox-packages",
1853
+ title: "Sandbox packages (guest modules, host modules, npm compilation, catalog)",
1854
+ harnesses: ["packs-compile", "validate"],
1855
+ paths: [
1856
+ "packages/sandbox-compiler/",
1857
+ "packages/sandbox-packs/",
1858
+ "packages/agents/src/host-modules/",
1859
+ "packages/protocol/src/sandbox-host.ts",
1860
+ "packages/node-sdk/src/sandbox-bridge-packs.ts",
1861
+ "packages/node-sdk/src/sandbox-pack-discovery.ts",
1862
+ "packages/node-sdk/src/sandbox-module-catalog.ts",
1863
+ "packages/node-sdk/src/sandbox-catalog-host.ts",
1864
+ "packages/node-sdk/src/sandbox-npm-artifacts.ts",
1865
+ "packages/protocol/src/sandbox-package.ts",
1866
+ "packages/websocket/src/sandbox-catalog.ts",
1867
+ "electron/src/nodePackManager.ts"
1868
+ ]
1869
+ },
1870
+ {
1871
+ id: "chat-agent",
1872
+ title: "Chat agent loop (unified tool-calling loop)",
1873
+ harnesses: ["chat-terminal", "chat-stdin", "eval"],
1874
+ paths: ["packages/chat/", "packages/cli-chat/"]
1875
+ },
1876
+ {
1877
+ id: "web-editor",
1878
+ title: "Web editor (graph canvas, run rendering)",
1879
+ harnesses: ["debug", "docker-smoke"],
1880
+ paths: ["web/"]
1881
+ },
1882
+ {
1883
+ id: "tutorial-rendering",
1884
+ title: "Tutorial rendering and inspection",
1885
+ harnesses: ["tutorial-rendering"],
1886
+ paths: [
1887
+ "demo/src/",
1888
+ "demo/scripts/render-tutorials.ts",
1889
+ "demo/scripts/render-marketing.ts",
1890
+ "demo/scripts/encode-hero.mjs",
1891
+ "demo/scripts/vibe-track.ts",
1892
+ "demo/scripts/build-pattern-cast-media.mjs",
1893
+ "demo/webpack-stubs/"
1894
+ ]
1895
+ },
1896
+ {
1897
+ id: "desktop-backend",
1898
+ title: "Packaged Electron backend (bundled server.mjs)",
1899
+ harnesses: ["backend-smoke"],
1900
+ paths: [
1901
+ "packages/server/",
1902
+ "scripts/bundle-backend.mjs",
1903
+ "scripts/backend-server-build-options.mjs",
1904
+ "scripts/verify-backend-bundle.mjs",
1905
+ "packages/config/src/package-asset-registry.ts"
1906
+ ]
1907
+ },
1908
+ {
1909
+ id: "packed-core-cli",
1910
+ title: "Isolated core CLI package installation",
1911
+ harnesses: ["packed-core-cli"],
1912
+ paths: [
1913
+ "scripts/smoke-packed-cli.mjs",
1914
+ "scripts/fixtures/codec-identity.mp4",
1915
+ ".github/workflows/packed-core-cli.yml"
1916
+ ]
1917
+ },
1918
+ {
1919
+ id: "release-pins",
1920
+ title: "Release tarball catalog pins",
1921
+ harnesses: ["release-pins"],
1922
+ paths: [
1923
+ "scripts/release/",
1924
+ "scripts/publish-packages.mjs",
1925
+ "scripts/prepare-publish.mjs",
1926
+ "scripts/__tests__/pin-catalog.test.mjs",
1927
+ "scripts/__tests__/publish-packages.test.mjs",
1928
+ "scripts/__tests__/install-pins.test.mjs",
1929
+ "scripts/__tests__/staging-release.test.mjs",
1930
+ "scripts/__tests__/publish-tarballs.test.mjs"
1931
+ ]
1932
+ },
1933
+ {
1934
+ id: "node-catalog",
1935
+ title: "Exact node pack ownership and catalog metadata",
1936
+ harnesses: ["node-catalog"],
1937
+ paths: [
1938
+ "packages/node-catalog/",
1939
+ "packages/node-sdk/src/",
1940
+ "scripts/node-catalog/",
1941
+ "tools/oxlint/pack-boundaries/",
1942
+ "scripts/generate-node-catalog.mjs",
1943
+ "scripts/__tests__/node-catalog.test.mjs",
1944
+ "scripts/bump-version.mjs",
1945
+ "scripts/__tests__/bump-version.test.mjs",
1946
+ "packages/atlascloud-nodes/",
1947
+ "packages/audio-nodes/",
1948
+ "packages/automation-nodes/",
1949
+ "packages/blender-nodes/",
1950
+ "packages/code-nodes/",
1951
+ "packages/core-nodes/",
1952
+ "packages/data-nodes/",
1953
+ "packages/document-nodes/",
1954
+ "packages/elevenlabs-nodes/",
1955
+ "packages/fal-nodes/",
1956
+ "packages/game-nodes/",
1957
+ "packages/higgsfield-nodes/",
1958
+ "packages/huggingface-nodes/",
1959
+ "packages/image-nodes/",
1960
+ "packages/integration-nodes/",
1961
+ "packages/kie-nodes/",
1962
+ "packages/llm-nodes/",
1963
+ "packages/manimgx-nodes/",
1964
+ "packages/minimax-nodes/",
1965
+ "packages/replicate-nodes/",
1966
+ "packages/reve-nodes/",
1967
+ "packages/text-nodes/",
1968
+ "packages/together-nodes/",
1969
+ "packages/topaz-nodes/",
1970
+ "packages/transformers-js-nodes/",
1971
+ "packages/video-nodes/",
1972
+ "packages/whisper-cpp/"
1973
+ ]
1974
+ },
1975
+ {
1976
+ id: "provider-codegen",
1977
+ title: "Generated FAL, KIE, and Replicate provider metadata (node manifests, node source)",
1978
+ harnesses: ["provider-codegen"],
1979
+ paths: [
1980
+ "packages/fal-codegen/",
1981
+ "packages/kie-codegen/",
1982
+ // Replicate's generator has no fixture-mode drift check of its own yet
1983
+ // (unlike fal/kie) — a diff here still runs the fal/kie selfcheck,
1984
+ // which is the closest real coverage until one is written.
1985
+ "packages/replicate-codegen/",
1986
+ "packages/fal-nodes/src/fal-manifest.json",
1987
+ "packages/kie-nodes/src/kie-manifest.json",
1988
+ "scripts/provider-codegen-check.mjs"
1989
+ ]
1990
+ },
1991
+ {
1992
+ id: "mutation-testing",
1993
+ title: "Mutation testing tool and its score report",
1994
+ harnesses: ["mutator"],
1995
+ paths: ["packages/mutator/", "scripts/mutation-score.mjs"]
1996
+ },
1997
+ {
1998
+ id: "deploy-image",
1999
+ title: "Deployed GHCR image (Fly.io / self-hosted)",
2000
+ harnesses: ["docker-smoke"],
2001
+ paths: [
2002
+ "Dockerfile",
2003
+ "docker-compose.yml",
2004
+ "fly.toml",
2005
+ "scripts/docker-smoke.mjs"
2006
+ ]
2007
+ },
2008
+ {
2009
+ id: "public-app-deployments",
2010
+ title: "Deployed mini apps (hidden URL, no login)",
2011
+ harnesses: [],
2012
+ paths: [
2013
+ "packages/websocket/src/lib/app-deployment-service.ts",
2014
+ "packages/websocket/src/lib/app-session-scope.ts",
2015
+ "packages/websocket/src/routes/public-apps.ts",
2016
+ "packages/auth/src/providers/app-session-token-provider.ts",
2017
+ "packages/models/src/application-deployment.ts",
2018
+ "web/src/components/applications/PublicAppPage.tsx"
2019
+ ],
2020
+ gap: "Covered by unit tests only, and the two halves they cannot reach are " +
2021
+ "the ones that matter: what a real logged-out browser can load from " +
2022
+ "the link, and what the auth hook actually refuses an `nda_` token " +
2023
+ "outside `/ws`. A harness would boot the server with " +
2024
+ "NODETOOL_ENV=production, deploy a published example app, then (a) " +
2025
+ "walk every non-`/ws` path with the minted session and assert 401, and " +
2026
+ "(b) drive the page with Playwright carrying no account session, the " +
2027
+ "way the debug harness drives the graph canvas."
2028
+ },
2029
+ {
2030
+ id: "mobile",
2031
+ title: "Mobile app (documents via chat agent ui_* tools)",
2032
+ harnesses: [],
2033
+ paths: ["mobile/"],
2034
+ gap: "No headless harness yet. Mobile edits already flow through the chat " +
2035
+ "agent's ui_* tools (mobile/ARCHITECTURE.md § Documents), so the harness " +
2036
+ "shape exists: drive the same tool contract against a headless bridge " +
2037
+ "the way the tool-loop evals do, and validate the document each session " +
2038
+ "leaves behind."
2039
+ },
2040
+ {
2041
+ id: "electron-shell",
2042
+ title: "Electron shell (windows, IPC, menus, auto-update)",
2043
+ harnesses: [],
2044
+ paths: ["electron/"],
2045
+ gap: "Covered by Jest unit tests only. A harness would boot the packaged " +
2046
+ "shell headlessly (Playwright's Electron driver), assert the " +
2047
+ "IPC surface, and reuse backend-smoke for the server half."
2048
+ },
2049
+ {
2050
+ id: "api-server",
2051
+ title: "API server (Fastify HTTP + WebSocket routes)",
2052
+ harnesses: ["api-routes"],
2053
+ paths: [
2054
+ "packages/websocket/", "packages/asset-services/", "packages/document-services/",
2055
+ "packages/error-trace-services/", "packages/pack-host/", "packages/project-services/",
2056
+ "packages/workflow-services/"
2057
+ ]
2058
+ },
2059
+ {
2060
+ id: "data-models",
2061
+ title: "Data models (SQLite/Postgres persistence, Drizzle ORM)",
2062
+ harnesses: ["data-models"],
2063
+ paths: ["packages/models/"]
2064
+ },
2065
+ {
2066
+ id: "analytics",
2067
+ title: "Product analytics (shared schema and client core)",
2068
+ harnesses: ["analytics"],
2069
+ paths: ["packages/analytics/"]
2070
+ },
2071
+ {
2072
+ id: "storage-and-security",
2073
+ title: "Storage, secrets, auth middleware, and config loading",
2074
+ harnesses: ["storage-and-security"],
2075
+ paths: [
2076
+ "packages/storage/",
2077
+ "packages/security/",
2078
+ "packages/auth/",
2079
+ "packages/config/"
2080
+ ]
2081
+ },
2082
+ {
2083
+ id: "domain-nodes",
2084
+ title: "Domain node packages (provider and media node factories)",
2085
+ harnesses: ["node-run", "validate", "node-pack-parity"],
2086
+ paths: [
2087
+ "packages/atlascloud-nodes/",
2088
+ "packages/audio-nodes/",
2089
+ "packages/automation-nodes/",
2090
+ "packages/code-nodes/",
2091
+ "packages/core-nodes/",
2092
+ "packages/data-nodes/",
2093
+ "packages/document-nodes/",
2094
+ "packages/elevenlabs-nodes/",
2095
+ "packages/fal-nodes/",
2096
+ // Shared by the node packages, not a node package itself: the blend-mode
2097
+ // catalog/WGSL functions (gpu) and HuggingFace cache/discovery
2098
+ // (huggingface, paired with huggingface-nodes). CI groups both with the
2099
+ // `*-nodes` filter in quality-checks.yml's test-packages-nodes leg.
2100
+ "packages/gpu/",
2101
+ "packages/huggingface/",
2102
+ "packages/huggingface-nodes/",
2103
+ "packages/higgsfield-nodes/",
2104
+ "packages/image-nodes/",
2105
+ "packages/integration-nodes/",
2106
+ "packages/kie-nodes/",
2107
+ "packages/llm-nodes/",
2108
+ "packages/minimax-nodes/",
2109
+ "packages/nodes-utils/",
2110
+ "packages/replicate-nodes/",
2111
+ "packages/reve-nodes/",
2112
+ "packages/text-nodes/",
2113
+ "packages/together-nodes/",
2114
+ "packages/topaz-nodes/",
2115
+ "packages/transformers-js-nodes/",
2116
+ "packages/whisper-cpp/",
2117
+ "packages/video-nodes/",
2118
+ // Portable runner (graph + registry → a Request/Response handler); CI
2119
+ // groups it with the node packages for the same reason.
2120
+ "packages/workflow-runner/"
2121
+ ]
2122
+ },
2123
+ {
2124
+ id: "shared-services",
2125
+ title: "Shared service libraries (vector store, model pricing)",
2126
+ harnesses: ["shared-services"],
2127
+ paths: ["packages/vectorstore/", "packages/model-pricing/"]
2128
+ },
2129
+ {
2130
+ id: "repo-scripts",
2131
+ title: "Repo tooling scripts (scripts/)",
2132
+ harnesses: ["repo-scripts"],
2133
+ // Overlaps with the surfaces that claim individual scripts (bundle
2134
+ // staging, codegen drift, docker smoke); a diff there runs both.
2135
+ paths: ["scripts/"]
2136
+ },
2137
+ {
2138
+ id: "workflow-contracts",
2139
+ title: "GitHub workflow scripts and contract tests (.github/)",
2140
+ harnesses: ["workflow-contracts"],
2141
+ paths: [".github/scripts/", ".github/tests/"]
2142
+ },
2143
+ {
2144
+ id: "recipes",
2145
+ title: "Recipes (ordered chains of shipped examples)",
2146
+ harnesses: ["recipes"],
2147
+ // The manifests, the resolver behind the app's Examples listing, and the
2148
+ // site pages built from the same files. Overlaps the surfaces that claim
2149
+ // the packages and the site; a diff here runs both.
2150
+ paths: [
2151
+ "packages/base-nodes/nodetool/examples/recipes/",
2152
+ "packages/protocol/src/api-schemas/recipes.ts",
2153
+ "packages/websocket/src/lib/example-recipes.ts",
2154
+ "web/src/components/portal/DashboardRecipes.tsx",
2155
+ "web/src/hooks/useRecipeActions.ts",
2156
+ "marketing/scripts/generate-recipes.mjs",
2157
+ "marketing/scripts/recipes.mjs",
2158
+ "marketing/src/app/recipes/",
2159
+ "marketing/src/data/recipes.ts"
2160
+ ]
2161
+ },
2162
+ {
2163
+ id: "marketing-site",
2164
+ title: "Marketing site (nodetool.ai)",
2165
+ harnesses: [],
2166
+ paths: ["marketing/"],
2167
+ gap: "A separate npm project with its own lockfile, so the root gate cannot " +
2168
+ "install it: marketing-ci.yml runs its typecheck, lint, Next build, " +
2169
+ "generated-data --check steps, a Playwright smoke suite and a route " +
2170
+ "loader against its own tree. The one part the root gate does reach is " +
2171
+ "the recipe pages, whose data comes from the shipped manifests — that " +
2172
+ "is the `recipes` harness above. A fuller harness would drive the built " +
2173
+ "site the way the debug harness drives the graph canvas."
2174
+ },
2175
+ {
2176
+ id: "harness-registry",
2177
+ title: "Harness registry (this file, its tests, capability sync)",
2178
+ harnesses: ["harness-audit"],
2179
+ paths: [
2180
+ "packages/cli-harness/src/harness/",
2181
+ "packages/cli-harness/src/commands/harness.ts",
2182
+ "packages/cli-harness/tests/harness-gate-cli.test.ts",
2183
+ "packages/cli-harness/tests/harness-registry.test.ts",
2184
+ "packages/cli-harness/tests/capability-coverage.test.ts",
2185
+ "scripts/sync-capability-coverage.mjs"
2186
+ ]
2187
+ }
2188
+ ];
2189
+ /**
2190
+ * Package/app directories with no `SurfaceEntry.paths` prefix pointing into
2191
+ * them, and why: what a diff there cannot verify headlessly today, and what
2192
+ * closing the gap would need. This is the layer below `SurfaceEntry.gap` — a
2193
+ * surface can be uncovered by a *harness* and still claim its paths (mobile,
2194
+ * electron-shell do exactly that); an entry here has no *surface* touching it
2195
+ * at all, so a diff under it selects nothing in `nodetool harness gate` and
2196
+ * `auditPathClaims` would report it silently otherwise.
2197
+ *
2198
+ * Every directory under `packages/`, plus `web/`, `electron/`, `mobile/`,
2199
+ * must appear in some `SurfaceEntry.paths` or here —
2200
+ * `tests/harness-registry.test.ts` walks the real repo and fails the build on
2201
+ * one that is in neither.
2202
+ */
2203
+ export const UNCLAIMED_PATHS = {
2204
+ ".design-sync/": "Preview stubs and config for syncing ui_primitives into the external " +
2205
+ "design tool; nothing in the repo builds or runs them. The primitives " +
2206
+ "they render are covered by the web surface.",
2207
+ "examples/nextjs-vercel/": "Deployment example app. CI typechecks it (`npm run typecheck:examples`); " +
2208
+ "its `next build` costs minutes and belongs to deploys, not the PR gate. " +
2209
+ "No harness drives it headlessly.",
2210
+ "examples/nextjs-cloudflare/": "Deployment example app, same standing as examples/nextjs-vercel/: " +
2211
+ "typechecked in CI, built only on deploy, no headless harness.",
2212
+ "packages/compute/": "Provisions and reaps real RunPod/Vast.ai GPU workers; only its hermetic " +
2213
+ "unit tests run headlessly today. A harness needs a recorded-fixture " +
2214
+ "mode against a fake provider API, the way `blender` and " +
2215
+ "`provider-contract` fake their externals.",
2216
+ "packages/deploy/": "The `nodetool deploy` toolkit (Docker/SSH/RunPod/GCP/Supabase " +
2217
+ "self-hosting) needs a real target to prove a rollout; only its " +
2218
+ "planning-logic unit tests run headlessly today.",
2219
+ "packages/sdk/": "Generated TypeScript client re-exporting the tRPC/WebSocket surface; " +
2220
+ "has no test script of its own (build + lint only) — exercised " +
2221
+ "indirectly through the `api-routes` suite it wraps.",
2222
+ "packages/system-skills/": "Not an npm workspace, just SKILL.md files staged into the packaged " +
2223
+ "backend by scripts/bundle-backend.mjs; verify-backend-bundle.mjs " +
2224
+ "checks every directory ships, shipped-skill-names.test.ts checks the " +
2225
+ "calls each one names against the registry, and check-agents-docs.mjs " +
2226
+ "checks the frontmatter name, the one-document rule and the " +
2227
+ ".claude/skills symlinks."
2228
+ };
2229
+ /**
2230
+ * Whether a repo-relative file sits under a directory {@link UNCLAIMED_PATHS}
2231
+ * already documents as having no harness. `nodetool harness gate` uses this so
2232
+ * a diff touching such a directory is not reported as an unmapped code file —
2233
+ * the judgement, and its reason, are already recorded above.
2234
+ */
2235
+ export function isUnclaimedPath(file) {
2236
+ const path = normalizeGatePath(file);
2237
+ return Object.keys(UNCLAIMED_PATHS).some((prefix) => path.startsWith(prefix));
2238
+ }
2239
+ /**
2240
+ * Repo-relative, forward-slashed, with no leading `./`. Surface paths are
2241
+ * written that way; a file passed on the command line (`gate ./packages/…`,
2242
+ * or a Windows `packages\…`) otherwise matched no prefix and failed the gate
2243
+ * as an unmapped code file.
2244
+ */
2245
+ function normalizeGatePath(file) {
2246
+ return file.replace(/\\/g, "/").replace(/^(\.\/)+/, "");
2247
+ }
2248
+ export function auditHarnessCoverage(harnesses = HARNESSES, surfaces = SURFACES) {
2249
+ const known = new Set(harnesses.map((h) => h.id));
2250
+ const referenced = new Set();
2251
+ const unknownHarnessRefs = [];
2252
+ const undocumentedGaps = [];
2253
+ const rows = surfaces.map((s) => {
2254
+ for (const id of s.harnesses) {
2255
+ referenced.add(id);
2256
+ if (!known.has(id))
2257
+ unknownHarnessRefs.push(`${s.id} → ${id}`);
2258
+ }
2259
+ const covered = s.harnesses.length > 0;
2260
+ if (!covered && !s.gap)
2261
+ undocumentedGaps.push(s.id);
2262
+ return {
2263
+ id: s.id,
2264
+ title: s.title,
2265
+ harnesses: s.harnesses,
2266
+ covered,
2267
+ ...(s.gap && { gap: s.gap })
2268
+ };
2269
+ });
2270
+ return {
2271
+ surfaces: rows,
2272
+ coveredCount: rows.filter((r) => r.covered).length,
2273
+ gapCount: rows.filter((r) => !r.covered).length,
2274
+ undocumentedGaps,
2275
+ unknownHarnessRefs,
2276
+ orphanHarnesses: harnesses
2277
+ .map((h) => h.id)
2278
+ .filter((id) => !referenced.has(id) && id !== "affected")
2279
+ };
2280
+ }
2281
+ /**
2282
+ * Directories under `packages/` (plus `web/`, `electron/`, `mobile/`) that no
2283
+ * `SurfaceEntry.paths` prefix and no `UNCLAIMED_PATHS` entry references. A
2284
+ * non-empty result means a diff under one of them selects nothing in
2285
+ * `nodetool harness gate` — silently, since no surface even names the path.
2286
+ *
2287
+ * `rootDirs` are repo-relative directory paths (e.g. `"packages/models"`, a
2288
+ * trailing slash is added if missing). A directory is claimed when some
2289
+ * surface path or `UNCLAIMED_PATHS` key falls inside it, equals it, or is one
2290
+ * of its ancestors — the same "prefix either way" relationship `planGate`
2291
+ * uses to route a changed file to a surface.
2292
+ */
2293
+ export function auditPathClaims(rootDirs, surfaces = SURFACES, unclaimed = UNCLAIMED_PATHS) {
2294
+ const claims = [
2295
+ ...surfaces.flatMap((s) => s.paths),
2296
+ ...Object.keys(unclaimed)
2297
+ ];
2298
+ return rootDirs.filter((dir) => {
2299
+ const prefix = dir.endsWith("/") ? dir : `${dir}/`;
2300
+ return !claims.some((p) => p.startsWith(prefix) || prefix.startsWith(p));
2301
+ });
2302
+ }
2303
+ /**
2304
+ * Select checks for a diff. Without `packages`, a surface is touched only by a
2305
+ * changed path under one of its prefixes. With them, a surface is also touched
2306
+ * when one of its paths lies inside a workspace downstream of a changed one,
2307
+ * since its code then runs against changed code (`viaDependency`).
2308
+ */
2309
+ export function planGate(files, harnesses = HARNESSES, surfaces = SURFACES, packages) {
2310
+ const changedFiles = [...new Set(files.map(normalizeGatePath))];
2311
+ const byId = new Map(harnesses.map((h) => [h.id, h]));
2312
+ const touched = new Map();
2313
+ const viaDependency = new Set();
2314
+ const unmappedFiles = [];
2315
+ const globalFiles = changedFiles.filter(isGlobalGateFile);
2316
+ for (const file of changedFiles) {
2317
+ if (isGlobalGateFile(file))
2318
+ continue;
2319
+ let matched = false;
2320
+ for (const s of surfaces) {
2321
+ if (s.paths.some((p) => file === p || file.startsWith(p))) {
2322
+ matched = true;
2323
+ const files = touched.get(s.id) ?? [];
2324
+ files.push(file);
2325
+ touched.set(s.id, files);
2326
+ }
2327
+ }
2328
+ if (!matched)
2329
+ unmappedFiles.push(file);
2330
+ }
2331
+ if (packages) {
2332
+ const dirs = computeAffected(changedFiles, packages)
2333
+ .affected.map((name) => packages.find((p) => p.name === name)?.dir)
2334
+ .filter((d) => d !== undefined)
2335
+ .map((d) => d.replace(/^\.\//, "").replace(/\/+$/, ""));
2336
+ for (const s of surfaces) {
2337
+ if (touched.has(s.id))
2338
+ continue;
2339
+ const inside = s.paths.some((p) => dirs.some((d) => p === d || p.startsWith(`${d}/`)));
2340
+ if (inside) {
2341
+ touched.set(s.id, []);
2342
+ viaDependency.add(s.id);
2343
+ }
2344
+ }
2345
+ }
2346
+ const checkByHarness = new Map();
2347
+ const manualByHarness = new Map();
2348
+ const uncoveredSurfaces = [];
2349
+ for (const s of surfaces) {
2350
+ if (!touched.has(s.id))
2351
+ continue;
2352
+ if (s.harnesses.length === 0) {
2353
+ uncoveredSurfaces.push(s.id);
2354
+ continue;
2355
+ }
2356
+ for (const id of s.harnesses) {
2357
+ const h = byId.get(id);
2358
+ if (!h)
2359
+ continue;
2360
+ if (h.selfcheck) {
2361
+ const existing = checkByHarness.get(id);
2362
+ if (existing) {
2363
+ existing.surfaces.push(s.id);
2364
+ }
2365
+ else {
2366
+ checkByHarness.set(id, {
2367
+ harnessId: id,
2368
+ command: h.selfcheck.command,
2369
+ cost: h.selfcheck.cost,
2370
+ suiteOnly: h.selfcheck.suiteOnly ?? false,
2371
+ surfaces: [s.id]
2372
+ });
2373
+ }
2374
+ }
2375
+ else {
2376
+ const existing = manualByHarness.get(id);
2377
+ if (existing) {
2378
+ existing.surfaces.push(s.id);
2379
+ }
2380
+ else {
2381
+ manualByHarness.set(id, {
2382
+ harnessId: id,
2383
+ command: h.command,
2384
+ surfaces: [s.id]
2385
+ });
2386
+ }
2387
+ }
2388
+ }
2389
+ }
2390
+ // A global file selects every selfcheck, as `--all` does; surfaces it
2391
+ // would not otherwise name stay with an empty list.
2392
+ if (globalFiles.length > 0) {
2393
+ for (const h of harnesses) {
2394
+ if (!h.selfcheck || checkByHarness.has(h.id))
2395
+ continue;
2396
+ checkByHarness.set(h.id, {
2397
+ harnessId: h.id,
2398
+ command: h.selfcheck.command,
2399
+ cost: h.selfcheck.cost,
2400
+ suiteOnly: h.selfcheck.suiteOnly ?? false,
2401
+ surfaces: []
2402
+ });
2403
+ }
2404
+ }
2405
+ return {
2406
+ changedFiles,
2407
+ surfaces: [...touched.entries()].map(([id, files]) => ({
2408
+ id,
2409
+ files,
2410
+ ...(viaDependency.has(id) && { viaDependency: true })
2411
+ })),
2412
+ checks: [...checkByHarness.values()],
2413
+ manual: [...manualByHarness.values()],
2414
+ uncoveredSurfaces,
2415
+ unmappedFiles,
2416
+ globalFiles
2417
+ };
2418
+ }
2419
+ //# sourceMappingURL=registry.js.map