@tiic-tech/openworkflow 0.1.0 → 0.1.2

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 (217) hide show
  1. package/README.md +10 -0
  2. package/dist/adapters/codex/src/cleanCodexAdapter.d.ts +7 -0
  3. package/dist/adapters/codex/src/cleanCodexAdapter.js +99 -0
  4. package/dist/adapters/codex/src/cleanCodexAdapter.js.map +1 -0
  5. package/dist/adapters/codex/src/constants.d.ts +1 -0
  6. package/dist/adapters/codex/src/constants.js +2 -0
  7. package/dist/adapters/codex/src/constants.js.map +1 -0
  8. package/dist/adapters/codex/src/generateCommands.js +14 -9
  9. package/dist/adapters/codex/src/generateCommands.js.map +1 -1
  10. package/dist/adapters/codex/src/generateSkills.js +13 -0
  11. package/dist/adapters/codex/src/generateSkills.js.map +1 -1
  12. package/dist/adapters/codex/src/generatedFiles.js +1 -1
  13. package/dist/adapters/codex/src/manifest.js +11 -2
  14. package/dist/adapters/codex/src/manifest.js.map +1 -1
  15. package/dist/adapters/codex/src/templates.d.ts +0 -1
  16. package/dist/adapters/codex/src/templates.js +0 -1
  17. package/dist/adapters/codex/src/templates.js.map +1 -1
  18. package/dist/adapters/src/registry.d.ts +20 -0
  19. package/dist/adapters/src/registry.js +81 -0
  20. package/dist/adapters/src/registry.js.map +1 -0
  21. package/dist/cli/src/commands/brief.d.ts +46 -0
  22. package/dist/cli/src/commands/brief.js +294 -0
  23. package/dist/cli/src/commands/brief.js.map +1 -0
  24. package/dist/cli/src/commands/check.d.ts +42 -0
  25. package/dist/cli/src/commands/check.js +326 -0
  26. package/dist/cli/src/commands/check.js.map +1 -0
  27. package/dist/cli/src/commands/clean.d.ts +1 -0
  28. package/dist/cli/src/commands/clean.js +98 -0
  29. package/dist/cli/src/commands/clean.js.map +1 -0
  30. package/dist/cli/src/commands/context.d.ts +1 -0
  31. package/dist/cli/src/commands/context.js +471 -0
  32. package/dist/cli/src/commands/context.js.map +1 -0
  33. package/dist/cli/src/commands/doctor.js +122 -12
  34. package/dist/cli/src/commands/doctor.js.map +1 -1
  35. package/dist/cli/src/commands/draft.d.ts +1 -0
  36. package/dist/cli/src/commands/draft.js +175 -0
  37. package/dist/cli/src/commands/draft.js.map +1 -0
  38. package/dist/cli/src/commands/gitAutomation.d.ts +1 -0
  39. package/dist/cli/src/commands/gitAutomation.js +378 -0
  40. package/dist/cli/src/commands/gitAutomation.js.map +1 -0
  41. package/dist/cli/src/commands/handoff.d.ts +22 -0
  42. package/dist/cli/src/commands/handoff.js +122 -0
  43. package/dist/cli/src/commands/handoff.js.map +1 -0
  44. package/dist/cli/src/commands/init.js +52 -1
  45. package/dist/cli/src/commands/init.js.map +1 -1
  46. package/dist/cli/src/commands/inspect.d.ts +23 -0
  47. package/dist/cli/src/commands/inspect.js +157 -0
  48. package/dist/cli/src/commands/inspect.js.map +1 -0
  49. package/dist/cli/src/commands/register.d.ts +1 -0
  50. package/dist/cli/src/commands/register.js +251 -0
  51. package/dist/cli/src/commands/register.js.map +1 -0
  52. package/dist/cli/src/commands/resume.d.ts +59 -0
  53. package/dist/cli/src/commands/resume.js +280 -0
  54. package/dist/cli/src/commands/resume.js.map +1 -0
  55. package/dist/cli/src/commands/shared.js +6 -2
  56. package/dist/cli/src/commands/shared.js.map +1 -1
  57. package/dist/cli/src/commands/summaries.d.ts +1 -0
  58. package/dist/cli/src/commands/summaries.js +77 -0
  59. package/dist/cli/src/commands/summaries.js.map +1 -0
  60. package/dist/cli/src/commands/summarize.d.ts +1 -0
  61. package/dist/cli/src/commands/summarize.js +316 -0
  62. package/dist/cli/src/commands/summarize.js.map +1 -0
  63. package/dist/cli/src/commands/sync.js +135 -12
  64. package/dist/cli/src/commands/sync.js.map +1 -1
  65. package/dist/cli/src/commands/validate.js +25 -1
  66. package/dist/cli/src/commands/validate.js.map +1 -1
  67. package/dist/cli/src/dev/verifyAgentE2E.d.ts +2 -0
  68. package/dist/cli/src/dev/verifyAgentE2E.js +391 -0
  69. package/dist/cli/src/dev/verifyAgentE2E.js.map +1 -0
  70. package/dist/cli/src/dev/verifyCleanCommand.d.ts +2 -0
  71. package/dist/cli/src/dev/verifyCleanCommand.js +338 -0
  72. package/dist/cli/src/dev/verifyCleanCommand.js.map +1 -0
  73. package/dist/cli/src/dev/verifyRuntimeSurface.js +4940 -54
  74. package/dist/cli/src/dev/verifyRuntimeSurface.js.map +1 -1
  75. package/dist/cli/src/dev/verifyWorkflowE2E.js +477 -45
  76. package/dist/cli/src/dev/verifyWorkflowE2E.js.map +1 -1
  77. package/dist/cli/src/index.js +189 -5
  78. package/dist/cli/src/index.js.map +1 -1
  79. package/dist/cli/src/report.d.ts +26 -0
  80. package/dist/cli/src/report.js +17 -0
  81. package/dist/cli/src/report.js.map +1 -0
  82. package/dist/core/src/artifacts/readiness.d.ts +7 -0
  83. package/dist/core/src/artifacts/readiness.js +240 -0
  84. package/dist/core/src/artifacts/readiness.js.map +1 -0
  85. package/dist/core/src/artifacts/registry.d.ts +9 -2
  86. package/dist/core/src/artifacts/registry.js +687 -60
  87. package/dist/core/src/artifacts/registry.js.map +1 -1
  88. package/dist/core/src/commands/registry.js +1425 -146
  89. package/dist/core/src/commands/registry.js.map +1 -1
  90. package/dist/core/src/contracts/index.d.ts +1 -1
  91. package/dist/core/src/fs/index.d.ts +24 -0
  92. package/dist/core/src/fs/index.js +48 -1
  93. package/dist/core/src/fs/index.js.map +1 -1
  94. package/dist/core/src/git/autonomousSimulator.d.ts +46 -0
  95. package/dist/core/src/git/autonomousSimulator.js +163 -0
  96. package/dist/core/src/git/autonomousSimulator.js.map +1 -0
  97. package/dist/core/src/git/branchIdentity.d.ts +19 -0
  98. package/dist/core/src/git/branchIdentity.js +75 -0
  99. package/dist/core/src/git/branchIdentity.js.map +1 -0
  100. package/dist/core/src/git/draftPrPilot.d.ts +47 -0
  101. package/dist/core/src/git/draftPrPilot.js +196 -0
  102. package/dist/core/src/git/draftPrPilot.js.map +1 -0
  103. package/dist/core/src/git/localEvidenceReader.d.ts +21 -0
  104. package/dist/core/src/git/localEvidenceReader.js +142 -0
  105. package/dist/core/src/git/localEvidenceReader.js.map +1 -0
  106. package/dist/core/src/git/localGitAutomation.d.ts +68 -0
  107. package/dist/core/src/git/localGitAutomation.js +470 -0
  108. package/dist/core/src/git/localGitAutomation.js.map +1 -0
  109. package/dist/core/src/git/mergeReadinessCheckpoint.d.ts +31 -0
  110. package/dist/core/src/git/mergeReadinessCheckpoint.js +110 -0
  111. package/dist/core/src/git/mergeReadinessCheckpoint.js.map +1 -0
  112. package/dist/core/src/git/prReadySummary.d.ts +16 -0
  113. package/dist/core/src/git/prReadySummary.js +144 -0
  114. package/dist/core/src/git/prReadySummary.js.map +1 -0
  115. package/dist/core/src/git/remoteReadonlyPlanner.d.ts +60 -0
  116. package/dist/core/src/git/remoteReadonlyPlanner.js +223 -0
  117. package/dist/core/src/git/remoteReadonlyPlanner.js.map +1 -0
  118. package/dist/core/src/onboarding/agentsGuide.d.ts +32 -0
  119. package/dist/core/src/onboarding/agentsGuide.js +164 -0
  120. package/dist/core/src/onboarding/agentsGuide.js.map +1 -0
  121. package/dist/core/src/validators/validateOpenWorkflow.js +1331 -15
  122. package/dist/core/src/validators/validateOpenWorkflow.js.map +1 -1
  123. package/dist/core/src/validators/validateRepositoryContracts.js +2327 -306
  124. package/dist/core/src/validators/validateRepositoryContracts.js.map +1 -1
  125. package/dist/core/src/workflow/cleanOpenWorkflow.d.ts +18 -0
  126. package/dist/core/src/workflow/cleanOpenWorkflow.js +124 -0
  127. package/dist/core/src/workflow/cleanOpenWorkflow.js.map +1 -0
  128. package/dist/core/src/workflow/doctorOpenWorkflow.d.ts +7 -0
  129. package/dist/core/src/workflow/doctorOpenWorkflow.js +26 -0
  130. package/dist/core/src/workflow/doctorOpenWorkflow.js.map +1 -0
  131. package/dist/core/src/workflow/initOpenWorkflow.d.ts +7 -0
  132. package/dist/core/src/workflow/initOpenWorkflow.js +96 -8
  133. package/dist/core/src/workflow/initOpenWorkflow.js.map +1 -1
  134. package/dist/core/src/workflow/planningQueueResume.d.ts +105 -0
  135. package/dist/core/src/workflow/planningQueueResume.js +596 -0
  136. package/dist/core/src/workflow/planningQueueResume.js.map +1 -0
  137. package/dist/core/src/workflow/readWorkflowConfig.d.ts +6 -0
  138. package/dist/core/src/workflow/readWorkflowConfig.js +28 -0
  139. package/dist/core/src/workflow/readWorkflowConfig.js.map +1 -0
  140. package/dist/core/src/workflow/summaryHealth.d.ts +60 -0
  141. package/dist/core/src/workflow/summaryHealth.js +713 -0
  142. package/dist/core/src/workflow/summaryHealth.js.map +1 -0
  143. package/dist/core/src/workflow/syncOpenWorkflow.d.ts +22 -0
  144. package/dist/core/src/workflow/syncOpenWorkflow.js +235 -0
  145. package/dist/core/src/workflow/syncOpenWorkflow.js.map +1 -0
  146. package/package.json +4 -2
  147. package/references/artifact-authoring-templates.md +14 -12
  148. package/references/artifact-instruction-envelope.md +133 -0
  149. package/references/coder-continuous-growth-loop.md +68 -0
  150. package/references/gh-operation-governance.md +114 -0
  151. package/references/git-automation-governance.md +324 -0
  152. package/references/git-version-control-governance.md +227 -0
  153. package/references/internal-coder-protocol.md +202 -0
  154. package/references/issue-governance.md +115 -0
  155. package/references/planning-artifact-contracts.md +595 -0
  156. package/references/planning-skill-runtime-exposure.md +159 -0
  157. package/references/proto-redesign-artifact-contracts.md +217 -0
  158. package/references/proto2html-artifact-contracts.md +113 -0
  159. package/references/skill-system-lifecycle.md +198 -0
  160. package/references/validation-trust-domains.md +286 -0
  161. package/references/workflow-blueprint-runtime-alignment.md +287 -0
  162. package/schemas/atom-tasks.schema.json +101 -0
  163. package/schemas/candidate-changes.schema.json +323 -0
  164. package/schemas/current-state.schema.json +113 -0
  165. package/schemas/html-prototype.schema.json +288 -0
  166. package/schemas/openworkflow-contract.schema.json +9 -1
  167. package/schemas/proto-prompt-pack.schema.json +1333 -0
  168. package/schemas/prototype-evidence.schema.json +684 -142
  169. package/schemas/selected-change.schema.json +104 -0
  170. package/schemas/validation-target.schema.json +187 -1
  171. package/schemas/validation.schema.json +187 -1
  172. package/schemas/vision-session.schema.json +151 -0
  173. package/skills/analyze-changes/SKILL.md +92 -0
  174. package/skills/analyze-changes/agents/openai.yaml +4 -0
  175. package/skills/analyze-changes/references/analysis-protocol.md +116 -0
  176. package/skills/build-proto-prompt/SKILL.md +125 -0
  177. package/skills/build-proto-prompt/references/output-boundary.md +54 -0
  178. package/skills/build-proto-prompt/references/prompt-pack-compiler-protocol.md +80 -0
  179. package/skills/build-prototype/SKILL.md +162 -38
  180. package/skills/build-prototype/agents/openai.yaml +2 -2
  181. package/skills/build-prototype/references/philosophy-engine.md +61 -0
  182. package/skills/build-prototype/references/strategic-prompt-pack-protocol.md +365 -0
  183. package/skills/build-prototype/references/vision2prompt/01_input_contract.md +84 -0
  184. package/skills/build-prototype/references/vision2prompt/02_vision_decomposition.md +108 -0
  185. package/skills/build-prototype/references/vision2prompt/03_strategy_hypothesis_generation.md +89 -0
  186. package/skills/build-prototype/references/vision2prompt/04_product_system_extraction.md +78 -0
  187. package/skills/build-prototype/references/vision2prompt/05_prototype_prompt_schema.md +189 -0
  188. package/skills/build-prototype/references/vision2prompt/06_output_templates.md +125 -0
  189. package/skills/build-prototype/references/vision2prompt/07_quality_rubric.md +171 -0
  190. package/skills/build-validation/SKILL.md +136 -54
  191. package/skills/build-validation/references/prototype-validation-target-rubric.md +35 -0
  192. package/skills/build-validation/references/return-to-vision-gate.md +32 -0
  193. package/skills/build-vision/SKILL.md +192 -0
  194. package/skills/build-vision/references/proto-readiness-rubric.md +48 -0
  195. package/skills/build-vision/references/vision-interview-protocol.md +48 -0
  196. package/skills/coder/SKILL.md +204 -0
  197. package/skills/decompose-to-changes/SKILL.md +176 -0
  198. package/skills/decompose-to-changes/agents/openai.yaml +4 -0
  199. package/skills/decompose-to-changes/references/decomposition-protocol.md +278 -0
  200. package/skills/prompt2proto/SKILL.md +157 -0
  201. package/skills/prompt2proto/agents/openai.yaml +4 -0
  202. package/skills/prompt2proto/references/00_role_philosophy_engine.md +96 -0
  203. package/skills/prompt2proto/references/01_input_contract.md +53 -0
  204. package/skills/prompt2proto/references/02_prompt_pack_readiness.md +50 -0
  205. package/skills/prompt2proto/references/03_visual_translation_workflow.md +64 -0
  206. package/skills/prompt2proto/references/04_output_contract.md +67 -0
  207. package/skills/prompt2proto/references/05_quality_rubric.md +46 -0
  208. package/skills/proto2html/SKILL.md +136 -0
  209. package/skills/proto2html/agents/openai.yaml +4 -0
  210. package/skills/proto2html/references/proto2html-protocol.md +115 -0
  211. package/skills/run-team/SKILL.md +4 -0
  212. package/skills/select-change/SKILL.md +200 -0
  213. package/skills/select-change/agents/openai.yaml +4 -0
  214. package/skills/select-change/references/selection-protocol.md +281 -0
  215. package/skills/tune-prototype/SKILL.md +121 -0
  216. package/skills/tune-prototype/agents/openai.yaml +4 -0
  217. package/skills/tune-prototype/references/refined-prompt-pack-protocol.md +161 -0
@@ -19,16 +19,23 @@ async function main() {
19
19
  const codexHome = join(tempRoot, "codex-home");
20
20
  const env = { ...process.env, CODEX_HOME: codexHome };
21
21
  await run(["node", CLI, "init", target, "--tools", "codex", "--force"], env);
22
- await run(["node", CLI, "sync", "--root", target, "--tools", "codex"], env);
22
+ await run(["node", CLI, "sync", "--root", target], env);
23
23
  await run(["node", CLI, "doctor", "--root", target, "--tools", "codex"], env);
24
24
  await run(["node", CLI, "validate", "--root", target], env);
25
+ await verifyAgentOnboarding(target, env);
25
26
  const runtime = await loadRuntime(target);
27
+ await verifyCurrentState(runtime);
26
28
  await verifyVisionPhase(runtime);
27
29
  await verifyValidationPhase(runtime);
30
+ await verifyInternalProtoPipelinePhase(runtime);
28
31
  await verifyPrototypePhase(runtime);
29
32
  await verifyTunePhase(runtime);
30
33
  await verifyInternalDecisionPhase(runtime);
34
+ await verifyProductionCommandPhases(runtime);
31
35
  await verifyDisplayLabels(runtime);
36
+ await verifyBriefReadModel(runtime);
37
+ await verifyCommandReadiness(runtime);
38
+ await verifySummaryReadModel(runtime);
32
39
  }
33
40
  finally {
34
41
  await rm(tempRoot, { recursive: true, force: true });
@@ -38,48 +45,100 @@ async function main() {
38
45
  }
39
46
  async function loadRuntime(target) {
40
47
  const commandIndex = await readYaml(join(target, ".openworkflow", "audit", "COMMAND_AUDIT_INDEX.yaml"));
48
+ const currentState = await readYaml(join(target, ".openworkflow", "CURRENT_STATE.yaml"));
41
49
  const contextPackets = await readYaml(join(target, ".openworkflow", "audit", "CONTEXT_PACKETS.yaml"));
50
+ const artifactContracts = await readYaml(join(target, ".openworkflow", "audit", "ARTIFACT_CONTRACTS.yaml"));
42
51
  return {
43
52
  target,
53
+ currentState,
44
54
  commands: records(commandIndex, "commands", "runtime"),
45
55
  packets: records(contextPackets, "packets", "runtime"),
56
+ artifacts: records(artifactContracts, "artifacts", "runtime"),
46
57
  };
47
58
  }
59
+ async function verifyCurrentState(runtime) {
60
+ const phase = "current-state";
61
+ assertPhase(phase, runtime.currentState.contract_id === "workflow:current-state", "current state contract id mismatch");
62
+ assertPhase(phase, runtime.currentState.active_stage === "workflow", "initial active stage should be workflow");
63
+ assertPhase(phase, runtime.currentState.next_command === "/ow:vision", "initial next command should be /ow:vision");
64
+ assertListIncludes(phase, stringList(runtime.currentState, "read_this_first", phase), ".openworkflow/CURRENT_STATE.yaml", "current state does not point to itself");
65
+ const lastDecision = recordField(runtime.currentState, "last_decision", phase);
66
+ assertPhase(phase, "outcome" in lastDecision, "current state last_decision missing outcome");
67
+ }
48
68
  async function verifyVisionPhase(runtime) {
49
69
  const phase = "vision";
50
70
  const source = command("vision", phase);
51
71
  const protocol = protocolFor(source, phase);
52
- assertPhase(phase, protocol.interactionMode === "conversation-first-sustained-grill", "source protocol is not sustained grill");
72
+ assertPhase(phase, protocol.interactionMode === "delayed-compile-product-interrogation", "source protocol is not delayed-compile product interrogation");
53
73
  assertExactList(phase, protocol.handoffCommands, ["/ow:validation"], "vision source handoffs changed");
54
74
  assertListIncludes(phase, protocol.forbiddenOutputs, ".openworkflow/validation/**", "vision can create validation artifacts");
55
75
  assertListIncludes(phase, protocol.forbiddenOutputs, ".openworkflow/prototypes/**", "vision can create prototype artifacts");
56
- assertListIncludes(phase, protocol.auditCheckpoints.before, "Start in conversation mode and ask the next useful vision question before writing artifacts.", "vision does not start conversation-first");
76
+ assertSomeIncludes(phase, protocol.auditCheckpoints.before, "product partner, requirements interrogator, and intent compiler", "vision source missing three-role framing");
77
+ assertSomeIncludes(phase, protocol.auditCheckpoints.before, "do not start by creating or updating durable vision files", "vision does not start conversation-first");
57
78
  assertSomeIncludes(phase, protocol.auditCheckpoints.during, "make each question depend on the previous answer", "vision does not enforce progressive questioning");
58
- assertSomeIncludes(phase, protocol.auditCheckpoints.after, "user confirms readiness", "vision handoff does not require user readiness confirmation");
79
+ assertSomeIncludes(phase, protocol.auditCheckpoints.after, "proto_readiness.status", "vision compile does not require proto-readiness");
80
+ assertSomeIncludes(phase, protocol.auditCheckpoints.after, "user readiness", "vision handoff does not require user readiness confirmation");
59
81
  const generated = commandRecord(runtime, "vision", phase);
60
82
  assertPhase(phase, stringField(generated, "visibility", phase) === "user", "generated vision command is not user visible");
61
83
  assertExactList(phase, stringList(generated, "handoff_commands", phase), ["/ow:validation"], "generated vision handoffs changed");
62
84
  assertListIncludes(phase, stringList(generated, "forbidden_outputs", phase), ".openworkflow/prototypes/**", "generated vision can create prototypes");
63
85
  const packet = packetRecord(runtime, "/ow:vision", phase);
64
- assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "before"], phase), "ask the next useful vision question", "context packet lost conversation-first checkpoint");
65
- assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "during"], phase), "Cover mandatory vision dimensions", "context packet lost mandatory coverage checkpoint");
66
- assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "after"], phase), "user confirms readiness", "context packet lost readiness gate");
86
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "before"], phase), "Ask the next useful vision question", "context packet lost conversation-first checkpoint");
87
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "during"], phase), "Cover mandatory vision and proto-readiness dimensions", "context packet lost mandatory proto-readiness checkpoint");
88
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "after"], phase), "proto-readiness", "context packet lost proto-readiness gate");
67
89
  const skill = await readSkill(runtime, "ow-vision");
90
+ assertIncludes(phase, skill, ".openworkflow/CURRENT_STATE.yaml", "skill missing current state loading guidance");
91
+ assertIncludes(phase, skill, "clear stale current_question", "skill missing stale question closure guidance");
92
+ assertIncludes(phase, skill, "summary_policy", "skill missing summary policy guidance");
93
+ assertIncludes(phase, skill, "<vision_role>", "skill missing vision role block");
94
+ assertIncludes(phase, skill, "Act as product partner", "skill missing product partner role");
95
+ assertIncludes(phase, skill, "Act as requirements interrogator", "skill missing requirements interrogator role");
96
+ assertIncludes(phase, skill, "Act as intent compiler", "skill missing intent compiler role");
97
+ assertIncludes(phase, skill, "<interaction_modes>", "skill missing delayed compile modes");
98
+ assertIncludes(phase, skill, "Interview mode is the default", "skill missing interview mode");
99
+ assertIncludes(phase, skill, "Checkpoint mode writes", "skill missing checkpoint mode");
100
+ assertIncludes(phase, skill, "Compile mode writes", "skill missing compile mode");
101
+ assertIncludes(phase, skill, "<agent_first_consumer>", "skill missing Agent-first consumer guidance");
102
+ assertIncludes(phase, skill, "Treat the next implementing Agent as the first consumer", "skill missing first-consumer framing");
103
+ assertIncludes(phase, skill, "vision_delta must preserve enough handoff intelligence", "skill missing compact handoff intelligence guidance");
68
104
  assertIncludes(phase, skill, "<conversation_first>", "skill missing conversation_first block");
69
105
  assertIncludes(phase, skill, "Ask exactly one question", "skill no longer limits vision to one focused question");
70
106
  assertIncludes(phase, skill, "<mandatory_coverage>", "skill missing mandatory coverage block");
71
- assertIncludes(phase, skill, "Do not hand off to /ow:validation until mandatory coverage is addressed", "skill lost validation handoff gate");
107
+ assertIncludes(phase, skill, "<proto_readiness_gate>", "skill missing proto-readiness gate");
108
+ assertIncludes(phase, skill, "VISION.md is ready only when /ow:proto can derive", "skill missing proto-readiness acceptance");
109
+ assertIncludes(phase, skill, "Do not hand off to /ow:validation until mandatory coverage is addressed, proto-readiness", "skill lost validation handoff gate");
72
110
  assertIncludes(phase, skill, "not on a fixed number of turns", "skill permits fixed-turn readiness");
73
111
  assertIncludes(phase, skill, "<artifact_checkpoint>", "skill missing artifact checkpoint separation");
74
- const template = await readYaml(join(runtime.target, ".openworkflow", "vision", "_templates", "VISION_SESSION.yaml"));
112
+ const template = recordField(artifactRecord(runtime, "vision_session", phase), "template", phase);
113
+ const visionDelta = recordField(template, "vision_delta", phase);
114
+ assertPhase(phase, Object.hasOwn(visionDelta, "problem"), "vision template missing problem field");
115
+ assertPhase(phase, Object.hasOwn(visionDelta, "ai_native_role"), "vision template missing ai_native_role field");
116
+ assertPhase(phase, Object.hasOwn(visionDelta, "success_signals"), "vision template missing success_signals field");
117
+ assertPhase(phase, Object.hasOwn(visionDelta, "failure_signals"), "vision template missing failure_signals field");
118
+ const strategicCore = recordField(template, "strategic_core", phase);
119
+ assertPhase(phase, Object.hasOwn(strategicCore, "target_user"), "vision template missing strategic_core.target_user");
120
+ assertPhase(phase, Object.hasOwn(strategicCore, "core_differentiator"), "vision template missing strategic_core.core_differentiator");
121
+ const productSystemSeed = recordField(template, "product_system_seed", phase);
122
+ assertPhase(phase, Object.hasOwn(productSystemSeed, "primary_loop"), "vision template missing product_system_seed.primary_loop");
123
+ assertPhase(phase, Object.hasOwn(productSystemSeed, "anti_goals"), "vision template missing product_system_seed.anti_goals");
124
+ const protoReadiness = recordField(template, "proto_readiness", phase);
125
+ assertPhase(phase, protoReadiness.status === "missing", "vision proto_readiness should default to missing");
126
+ assertPhase(phase, Object.hasOwn(protoReadiness, "missing_for_proto"), "vision template missing proto_readiness.missing_for_proto");
127
+ assertPhase(phase, Object.hasOwn(protoReadiness, "prototype_direction_seeds"), "vision template missing proto_readiness.prototype_direction_seeds");
128
+ assertPhase(phase, Object.hasOwn(protoReadiness, "validation_target"), "vision template missing proto_readiness.validation_target");
129
+ const coverage = recordField(template, "coverage", phase);
130
+ assertPhase(phase, Object.hasOwn(coverage, "proto_readiness"), "vision template missing coverage.proto_readiness");
75
131
  const handoff = recordField(template, "handoff", phase);
76
132
  assertPhase(phase, handoff.ready === false, "vision template should default to not ready");
77
133
  assertPhase(phase, handoff.next_command === null, "vision template should not default to validation handoff");
134
+ assertPhase(phase, Object.hasOwn(handoff, "blockers"), "vision handoff template missing blockers field");
135
+ assertPhase(phase, Object.hasOwn(handoff, "readiness_notes"), "vision handoff template missing readiness_notes field");
78
136
  }
79
137
  async function verifyValidationPhase(runtime) {
80
138
  const phase = "validation";
81
139
  const source = command("validation", phase);
82
140
  const protocol = protocolFor(source, phase);
141
+ assertPhase(phase, protocol.interactionMode === "prototype-validation-target-compiler", "validation source protocol is not prototype target compiler");
83
142
  assertListIncludes(phase, protocol.requiredContext, ".openworkflow/vision/VISION_CONTRACT.yaml", "validation no longer requires vision contract");
84
143
  assertListIncludes(phase, protocol.forbiddenOutputs, ".openworkflow/prototypes/**", "validation can create prototype artifacts");
85
144
  assertExactList(phase, protocol.handoffCommands, ["/ow:proto", "/ow:vision"], "validation source handoffs changed");
@@ -89,78 +148,185 @@ async function verifyValidationPhase(runtime) {
89
148
  assertListExcludes(phase, handoffs, "/ow:decision", "generated validation exposes manual decision");
90
149
  const packet = packetRecord(runtime, "/ow:validation", phase);
91
150
  assertListIncludes(phase, stringList(packet, "required", phase), ".openworkflow/vision/VISION_CONTRACT.yaml", "context packet does not require vision contract");
92
- assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "during"], phase), "single highest-risk validation question", "validation does not focus the core risk");
93
- assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "after"], phase), "prototype brief", "validation does not produce prototype brief");
94
- const template = await readYaml(join(runtime.target, ".openworkflow", "validation", "_templates", "VALIDATION.yaml"));
151
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "before"], phase), "force /ow:proto to invent product strategy", "validation does not return to vision when strategy is missing");
152
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "during"], phase), "central uncertainty", "validation does not focus the central uncertainty");
153
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "during"], phase), "prototype_experiment", "validation does not define prototype experiment");
154
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "during"], phase), "observable_signals", "validation does not define observable signals");
155
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "after"], phase), "agent_readiness_gate", "validation does not write readiness gate");
156
+ const skill = await readSkill(runtime, "ow-validation");
157
+ assertIncludes(phase, skill, "prototype-validation-target-compiler", "skill missing validation compiler mode");
158
+ assertIncludes(phase, skill, "central_uncertainty", "skill missing central uncertainty artifact guidance");
159
+ assertIncludes(phase, skill, "prototype_experiment", "skill missing prototype experiment artifact guidance");
160
+ assertIncludes(phase, skill, "observable_signals", "skill missing observable signals artifact guidance");
161
+ assertIncludes(phase, skill, "decision_rules", "skill missing decision rules artifact guidance");
162
+ assertIncludes(phase, skill, "Return to /ow:vision", "skill missing return-to-vision gate");
163
+ assertIncludes(phase, skill, "Do not generate prototype prompts", "skill missing no-prototype boundary");
164
+ const template = recordField(artifactRecord(runtime, "validation_target", phase), "template", phase);
95
165
  assertPhase(phase, "prototype_scope" in template, "validation template missing prototype_scope");
166
+ assertPhase(phase, "central_uncertainty" in template, "validation template missing central_uncertainty");
167
+ assertPhase(phase, "target_behavior" in template, "validation template missing target_behavior");
168
+ assertPhase(phase, "prototype_experiment" in template, "validation template missing prototype_experiment");
169
+ assertPhase(phase, "observable_signals" in template, "validation template missing observable_signals");
170
+ assertPhase(phase, "decision_rules" in template, "validation template missing decision_rules");
171
+ assertPhase(phase, "vision_gaps" in template, "validation template missing vision_gaps");
172
+ const agentGate = recordField(template, "agent_readiness_gate", phase);
173
+ const trigger = recordField(template, "trigger", phase);
174
+ assertPhase(phase, trigger.mode === "user_explicit", "validation template should default trigger mode to user_explicit");
175
+ assertPhase(phase, trigger.requested_command === "/ow:validation", "validation template should default trigger requested_command to /ow:validation");
176
+ assertPhase(phase, agentGate.status === "thin_validation", "validation template should default agent readiness to thin_validation");
177
+ assertPhase(phase, agentGate.write_authority === "/ow:validation", "validation template should preserve /ow:validation write authority");
96
178
  assertPhase(phase, stringList(template, "decision_options", phase).includes("needs_more_evidence"), "validation template missing decision options");
97
179
  }
98
180
  async function verifyPrototypePhase(runtime) {
99
181
  const phase = "prototype";
100
182
  const source = command("proto", phase);
101
183
  const protocol = protocolFor(source, phase);
102
- assertPhase(phase, protocol.interactionMode === "classified-prototype-creation", "prototype source protocol is not classified creation");
184
+ assertPhase(phase, protocol.interactionMode === "image-first-strategic-proto-prompt-pack", "prototype source protocol is not image-first prompt pack");
103
185
  assertDiscoveryHandoffs(phase, protocol.handoffCommands, "source prototype handoffs");
104
186
  assertListIncludes(phase, protocol.allowedOutputs, ".openworkflow/decisions/<id>/DECISION.yaml", "prototype cannot write decision audit");
105
- assertSomeIncludes(phase, protocol.auditCheckpoints.before, "Classify prototype mode", "prototype does not classify mode first");
106
- assertSomeIncludes(phase, protocol.auditCheckpoints.before, "Detect reference inputs", "prototype does not detect references");
107
- assertSomeIncludes(phase, protocol.auditCheckpoints.during, "high-fidelity static concept", "prototype does not require static concept");
187
+ assertSomeIncludes(phase, protocol.conditionalOutputs ?? [], ".openworkflow/validation/<id>/VALIDATION.yaml", "prototype cannot auto-write validation artifacts");
188
+ assertSomeIncludes(phase, protocol.auditCheckpoints.before, "auto-run /ow:validation", "prototype does not auto-run validation when missing");
189
+ assertSomeIncludes(phase, protocol.auditCheckpoints.before, "trigger.mode agent_auto", "prototype auto validation does not record provenance");
190
+ assertSomeIncludes(phase, protocol.auditCheckpoints.before, "high-quality prototype prompts", "prototype does not verify vision and validation quality before prompt generation");
191
+ assertSomeIncludes(phase, protocol.auditCheckpoints.before, "askUserQuestion", "prototype does not ask for direction count when missing");
192
+ assertSomeIncludes(phase, protocol.auditCheckpoints.during, "5-8 strategic prototype hypotheses", "prototype does not generate strategic hypotheses");
193
+ assertSomeIncludes(phase, protocol.auditCheckpoints.during, "prompt_text_manifest.status is ready_for_image_generation", "prototype does not gate image generation on prompt text readiness");
194
+ assertSomeIncludes(phase, protocol.auditCheckpoints.during, "post_validate.status is pass", "prototype does not gate image generation on post_validate pass");
195
+ assertSomeIncludes(phase, protocol.auditCheckpoints.during, "post_validate.status is fail", "prototype does not route failed post_validate back to prompt repair");
196
+ assertSomeIncludes(phase, protocol.auditCheckpoints.during, "Batch-generate prototype images", "prototype does not batch-generate images after prompt text");
197
+ assertSomeIncludes(phase, protocol.auditCheckpoints.after, "PROTO_PROMPT_PACK.yaml", "prototype does not write prompt pack");
198
+ assertSomeIncludes(phase, protocol.auditCheckpoints.after, "image_generation collection state", "prototype does not write image generation collection state");
108
199
  assertSomeIncludes(phase, protocol.auditCheckpoints.after, "decision audit record internally", "prototype does not write decision audit internally");
109
200
  assertSomeIncludes(phase, protocol.antiPatterns, "Do not ask the user to manually invoke /ow:decision", "prototype permits manual decision handoff");
201
+ assertSomeIncludes(phase, protocol.antiPatterns, "Do not generate HTML", "prototype permits HTML generation");
110
202
  const generated = commandRecord(runtime, "proto", phase);
111
203
  assertDiscoveryHandoffs(phase, stringList(generated, "handoff_commands", phase), "generated prototype handoffs");
112
204
  assertListIncludes(phase, stringList(generated, "allowed_outputs", phase), ".openworkflow/decisions/<id>/DECISION.yaml", "generated prototype cannot write decision audit");
205
+ assertListIncludes(phase, stringList(generated, "allowed_outputs", phase), ".openworkflow/prototypes/<id>/PROTO_PROMPT_PACK.yaml", "generated prototype missing prompt pack output");
206
+ assertListIncludes(phase, stringList(generated, "conditional_outputs", phase), ".openworkflow/validation/<id>/VALIDATION.yaml", "generated prototype missing conditional validation output");
113
207
  const packet = packetRecord(runtime, "/ow:proto", phase);
114
- assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "during"], phase), "before HTML", "context packet lost visual-first ordering");
208
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "before"], phase), "askUserQuestion", "context packet lost direction-count question behavior");
209
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "during"], phase), "product_experience_model", "context packet lost product experience model behavior");
210
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "during"], phase), "strategic prototype hypotheses", "context packet lost strategic prompt behavior");
211
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "during"], phase), "post_validate.status is pass", "context packet lost post_validate pass gate");
212
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "during"], phase), "Batch-generate prototype images", "context packet lost image generation behavior");
115
213
  assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "after"], phase), "decision audit record internally", "context packet lost internal decision audit");
116
214
  const skill = await readSkill(runtime, "ow-proto");
117
215
  for (const tag of [
118
- "prototype_classification",
119
- "reference_extraction",
120
- "visual_first_path",
121
- "design_seed_protocol",
122
- "verification_protocol",
123
- "self_critique",
216
+ "internal_proto_pipeline",
217
+ "validation_consumption",
218
+ "preflight_quality_gate",
219
+ "direction_count_policy",
220
+ "strategic_prompt_pack",
221
+ "prompt_text_manifest",
222
+ "post_validate_gate",
223
+ "image_generation",
224
+ "image_only_boundary",
225
+ "review_evidence",
124
226
  "internal_decision_audit",
125
227
  ]) {
126
228
  assertIncludes(phase, skill, `<${tag}>`, `skill missing ${tag} block`);
127
229
  }
128
- assertIncludes(phase, skill, "image generation before HTML", "skill lost image generation before HTML rule");
129
- assertIncludes(phase, skill, "reference-pattern extraction", "skill lost reference-pattern extraction wording");
130
- assertIncludes(phase, skill, "repair pass before evidence handoff", "skill lost critique repair gate");
230
+ assertIncludes(phase, skill, "prompt_pack_type: strategic_proto_prompt_pack", "skill lost strategic prompt pack rule");
231
+ assertIncludes(phase, skill, "product_experience_model", "skill lost product experience model rule");
232
+ assertIncludes(phase, skill, "anti_generic_constraints", "skill lost anti-generic constraints rule");
233
+ assertIncludes(phase, skill, "/ow:vision2prompt and /ow:prompt2proto are internal commands", "skill lost internal proto pipeline rule");
234
+ assertIncludes(phase, skill, "trigger.mode: agent_auto", "skill lost auto validation provenance rule");
235
+ assertIncludes(phase, skill, "askUserQuestion", "skill lost direction-count question rule");
236
+ assertIncludes(phase, skill, "resolved_count: 3", "skill lost delegated default direction count");
237
+ assertIncludes(phase, skill, "post_validate.status: pass", "skill lost post_validate pass requirement");
238
+ assertIncludes(phase, skill, "post_validate.status: skipped", "skill lost post_validate skip requirement");
239
+ assertIncludes(phase, skill, "post_validate.status is fail", "skill lost post_validate failure route");
240
+ assertIncludes(phase, skill, "Batch-generate prototype images", "skill lost batch image generation rule");
241
+ assertIncludes(phase, skill, "Do not write HTML, CSS, runnable prototypes", "skill lost image-only boundary");
131
242
  assertIncludes(phase, skill, "decision_record", "skill does not expose decision artifact contract for internal audit");
132
243
  assertNotIncludes(phase, extractBlock(skill, "handoff_commands"), "/ow:decision", "skill exposes decision in prototype handoffs");
133
244
  const artifacts = getDiscoveryArtifactContractsForCommand("/ow:proto").map((artifact) => artifact.artifactType);
134
245
  assertListIncludes(phase, artifacts, "prototype_evidence", "source proto artifacts missing prototype evidence");
135
246
  assertListIncludes(phase, artifacts, "decision_record", "source proto artifacts missing decision record");
136
- const template = await readYaml(join(runtime.target, ".openworkflow", "prototypes", "_templates", "EVIDENCE.yaml"));
137
- assertPhase(phase, "reference_analysis" in template, "prototype template missing reference analysis");
138
- assertPhase(phase, "visual_concept_policy" in template, "prototype template missing visual concept policy");
139
- assertPhase(phase, "concept_evidence" in template, "prototype template missing concept evidence");
140
- assertPhase(phase, "implementation_evidence" in template, "prototype template missing implementation evidence");
141
- assertPhase(phase, "verification" in template, "prototype template missing verification evidence");
142
- assertPhase(phase, "self_critique" in template, "prototype template missing self critique");
143
- const conceptPolicy = recordField(template, "visual_concept_policy", phase);
144
- assertPhase(phase, conceptPolicy.image_generation === "generated|skipped_by_user|not_applicable", "prototype template lost image generation policy");
145
- const critique = recordField(template, "self_critique", phase);
146
- for (const key of ["philosophy", "hierarchy", "execution", "specificity", "restraint", "accessibility", "responsive_behavior", "repairs"]) {
147
- assertPhase(phase, key in critique, `prototype critique missing ${key}`);
148
- }
247
+ const template = recordField(artifactRecord(runtime, "prototype_evidence", phase), "template", phase);
248
+ assertPhase(phase, "prompt_pack_type" in template, "prototype template missing prompt pack type");
249
+ assertPhase(phase, "validation_input" in template, "prototype template missing validation input");
250
+ assertPhase(phase, "internal_pipeline" in template, "prototype template missing internal pipeline");
251
+ assertPhase(phase, "preflight_quality_gate" in template, "prototype template missing preflight quality gate");
252
+ assertPhase(phase, "direction_count_policy" in template, "prototype template missing direction count policy");
253
+ assertPhase(phase, "normalized_input" in template, "prototype template missing normalized input");
254
+ assertPhase(phase, "strategic_core" in template, "prototype template missing strategic core");
255
+ assertPhase(phase, "product_experience_model" in template, "prototype template missing product experience model");
256
+ assertPhase(phase, "prototype_reality_gate" in template, "prototype template missing prototype reality gate");
257
+ assertPhase(phase, "directions" in template, "prototype template missing directions");
258
+ assertPhase(phase, "build_recommendation" in template, "prototype template missing build recommendation");
259
+ assertPhase(phase, "prompt_text_manifest" in template, "prototype template missing prompt text manifest");
260
+ assertPhase(phase, "post_validate" in template, "prototype template missing post-validate gate");
261
+ assertPhase(phase, "image_generation" in template, "prototype template missing image generation state");
262
+ assertPhase(phase, "negative_constraints" in template, "prototype template missing negative constraints");
263
+ assertPhase(phase, "review_plan" in template, "prototype template missing review plan");
264
+ const validationInput = recordField(template, "validation_input", phase);
265
+ assertPhase(phase, !String(validationInput.mode).includes("vision_only"), "prototype template still allows vision_only validation input");
266
+ const directionPolicy = recordField(template, "direction_count_policy", phase);
267
+ assertPhase(phase, directionPolicy.resolved_count === 3, "prototype template should default delegated direction count to 3");
268
+ assertPhase(phase, String(directionPolicy.ask_user_question).includes("strategically different prototype directions"), "prototype template missing direction count askUserQuestion");
149
269
  const handoff = recordField(template, "handoff", phase);
150
270
  assertPhase(phase, handoff.next_command !== "/ow:decision", "prototype template exposes manual decision as next command");
151
271
  assertListIncludes(phase, USER_FACING_DISCOVERY_HANDOFFS, String(handoff.next_command), "prototype template next command is not user-facing");
152
272
  }
273
+ async function verifyInternalProtoPipelinePhase(runtime) {
274
+ const phase = "internal-proto-pipeline";
275
+ const buildProtoPrompt = command("build-proto-prompt", phase);
276
+ const vision2Prompt = command("vision2prompt", phase);
277
+ const prompt2Proto = command("prompt2proto", phase);
278
+ assertPhase(phase, buildProtoPrompt.visibility === "internal", "source build-proto-prompt command is not internal");
279
+ assertPhase(phase, vision2Prompt.visibility === "internal", "source vision2prompt command is not internal");
280
+ assertPhase(phase, prompt2Proto.visibility === "internal", "source prompt2proto command is not internal");
281
+ assertPhase(phase, protocolFor(buildProtoPrompt, phase).interactionMode === "internal-build-proto-prompt-pack-compiler", "build-proto-prompt source protocol mismatch");
282
+ assertPhase(phase, protocolFor(vision2Prompt, phase).interactionMode === "internal-vision-to-strategic-prompt-text", "vision2prompt source protocol mismatch");
283
+ assertPhase(phase, protocolFor(prompt2Proto, phase).interactionMode === "internal-prompt-text-to-prototype-images", "prompt2proto source protocol mismatch");
284
+ const generatedBuildProtoPrompt = commandRecord(runtime, "build-proto-prompt", phase);
285
+ const generatedVision2Prompt = commandRecord(runtime, "vision2prompt", phase);
286
+ const generatedPrompt2Proto = commandRecord(runtime, "prompt2proto", phase);
287
+ assertPhase(phase, stringField(generatedBuildProtoPrompt, "visibility", phase) === "internal", "generated build-proto-prompt command is not internal");
288
+ assertPhase(phase, stringField(generatedVision2Prompt, "visibility", phase) === "internal", "generated vision2prompt command is not internal");
289
+ assertPhase(phase, stringField(generatedPrompt2Proto, "visibility", phase) === "internal", "generated prompt2proto command is not internal");
290
+ const protoHandoffs = stringList(commandRecord(runtime, "proto", phase), "handoff_commands", phase);
291
+ assertListExcludes(phase, protoHandoffs, "/ow:vision2prompt", "proto exposes vision2prompt as user handoff");
292
+ assertListExcludes(phase, protoHandoffs, "/ow:prompt2proto", "proto exposes prompt2proto as user handoff");
293
+ const buildProtoPromptSkill = await readSkill(runtime, "ow-build-proto-prompt");
294
+ const vision2PromptSkill = await readSkill(runtime, "ow-vision2prompt");
295
+ const prompt2ProtoSkill = await readSkill(runtime, "ow-prompt2proto");
296
+ assertIncludes(phase, buildProtoPromptSkill, "<command_visibility>internal</command_visibility>", "build-proto-prompt skill is not internal");
297
+ assertIncludes(phase, buildProtoPromptSkill, "internal-build-proto-prompt-pack-compiler", "build-proto-prompt skill missing compiler mode");
298
+ assertIncludes(phase, buildProtoPromptSkill, "Do not generate images", "build-proto-prompt skill missing image boundary");
299
+ assertIncludes(phase, vision2PromptSkill, "<command_visibility>internal</command_visibility>", "vision2prompt skill is not internal");
300
+ assertIncludes(phase, vision2PromptSkill, "ready_for_image_generation", "vision2prompt skill missing prompt readiness output");
301
+ assertIncludes(phase, vision2PromptSkill, "product_experience_model", "vision2prompt skill missing product experience model output");
302
+ assertIncludes(phase, vision2PromptSkill, "anti_generic_constraints", "vision2prompt skill missing anti-generic constraints output");
303
+ assertIncludes(phase, vision2PromptSkill, "post_validate.status: pass", "vision2prompt skill missing post_validate pass requirement");
304
+ assertIncludes(phase, vision2PromptSkill, "post_validate.status: skipped", "vision2prompt skill missing single-direction skip requirement");
305
+ assertIncludes(phase, vision2PromptSkill, "post_validate.status: fail", "vision2prompt skill missing post_validate failure repair route");
306
+ assertIncludes(phase, prompt2ProtoSkill, "<command_visibility>internal</command_visibility>", "prompt2proto skill is not internal");
307
+ assertIncludes(phase, prompt2ProtoSkill, "post_validate.status pass or skipped", "prompt2proto skill missing post_validate readiness gate");
308
+ assertIncludes(phase, prompt2ProtoSkill, "Every generated image must record image_id", "prompt2proto skill missing image metadata contract");
309
+ }
153
310
  async function verifyTunePhase(runtime) {
154
311
  const phase = "tune";
155
312
  const source = command("tune", phase);
156
313
  const protocol = protocolFor(source, phase);
157
- assertPhase(phase, protocol.interactionMode === "prototype-revision-orchestration", "tune source protocol is not revision orchestration");
314
+ assertPhase(phase, protocol.interactionMode === "screen-bound-prototype-refinement", "tune source protocol is not screen-bound refinement");
158
315
  assertDiscoveryHandoffs(phase, protocol.handoffCommands, "source tune handoffs");
159
316
  assertListIncludes(phase, protocol.allowedOutputs, ".openworkflow/decisions/<id>/DECISION.yaml", "tune cannot write decision audit");
160
317
  assertListExcludes(phase, protocol.requiredContext, ".openworkflow/prototypes/PROTOTYPE_INDEX.yaml", "tune requires prototype index and cannot bootstrap from validation");
161
318
  assertListIncludes(phase, protocol.optionalContext, ".openworkflow/prototypes/PROTOTYPE_INDEX.yaml", "tune optional context missing prototype index");
162
- assertSomeIncludes(phase, protocol.auditCheckpoints.before, "/ow:tune:proto default to the current prototype", "tune does not default to current prototype");
163
- assertSomeIncludes(phase, protocol.auditCheckpoints.before, "no current prototype exists", "tune does not orchestrate proto when prototype is absent");
319
+ assertSomeIncludes(phase, protocol.auditCheckpoints.before, "latest approved", "tune does not resolve latest baseline");
320
+ assertSomeIncludes(phase, protocol.auditCheckpoints.before, "Normalize tune inputs", "tune does not normalize inputs before audit");
321
+ assertSomeIncludes(phase, protocol.auditCheckpoints.before, "baseline_resolution", "tune does not record baseline resolution");
322
+ assertSomeIncludes(phase, protocol.auditCheckpoints.during, "Carry forward locked screens", "tune does not carry forward locked elements");
323
+ assertSomeIncludes(phase, protocol.auditCheckpoints.before, "Assign stable source screen ids", "tune does not assign stable source ids");
324
+ assertSomeIncludes(phase, protocol.auditCheckpoints.during, "Interpret the tune request", "tune does not interpret tune request modes");
325
+ assertSomeIncludes(phase, protocol.auditCheckpoints.during, "Detect conflicts", "tune does not detect tune conflicts");
326
+ assertSomeIncludes(phase, protocol.auditCheckpoints.during, "MUST_INHERIT", "tune does not require delta rules");
327
+ assertSomeIncludes(phase, protocol.auditCheckpoints.during, "screen_delta_matrix", "tune does not require screen delta matrix");
328
+ assertSomeIncludes(phase, protocol.auditCheckpoints.during, "target screen id", "tune does not bind screen prompts");
329
+ assertSomeIncludes(phase, protocol.auditCheckpoints.during, "generation_order", "tune does not require generation order");
164
330
  assertSomeIncludes(phase, protocol.auditCheckpoints.after, "internal decision audit record", "tune does not record decision audit internally");
165
331
  assertSomeIncludes(phase, protocol.antiPatterns, "Do not ask the user to manually invoke /ow:decision", "tune permits manual decision handoff");
166
332
  const generated = commandRecord(runtime, "tune", phase);
@@ -170,10 +336,31 @@ async function verifyTunePhase(runtime) {
170
336
  assertListExcludes(phase, stringList(packet, "required", phase), ".openworkflow/prototypes/PROTOTYPE_INDEX.yaml", "context packet requires prototype index");
171
337
  assertListIncludes(phase, stringList(packet, "optional", phase), ".openworkflow/prototypes/PROTOTYPE_INDEX.yaml", "context packet optional context missing prototype index");
172
338
  const skill = await readSkill(runtime, "ow-tune");
173
- for (const tag of ["target_resolution", "proto_orchestration", "revision_protocol", "internal_decision_audit"]) {
339
+ for (const tag of [
340
+ "target_resolution",
341
+ "multi_round_baseline_inheritance",
342
+ "input_normalization",
343
+ "baseline_screen_audit",
344
+ "product_system_extraction",
345
+ "tune_request_interpretation",
346
+ "inheritance_delta_rules",
347
+ "screen_manifest",
348
+ "refined_prompt_pack_output",
349
+ "internal_decision_audit",
350
+ ]) {
174
351
  assertIncludes(phase, skill, `<${tag}>`, `skill missing ${tag} block`);
175
352
  }
176
- assertIncludes(phase, skill, "/ow:tune resolves to the current prototype by default.", "skill lost default target behavior");
353
+ assertIncludes(phase, skill, "/ow:tune resolves to the latest approved prototype prompt pack", "skill lost default target behavior");
354
+ assertIncludes(phase, skill, "<multi_round_baseline_inheritance>", "skill lost multi-round inheritance block");
355
+ assertIncludes(phase, skill, "baseline_resolution with latest_approved_baseline_group_id", "skill lost baseline resolution rule");
356
+ assertIncludes(phase, skill, "carry_forward with locked_screens", "skill lost carry-forward rule");
357
+ assertIncludes(phase, skill, "Never silently regenerate from stale source screens", "skill permits stale source fallback");
358
+ assertIncludes(phase, skill, "Normalize baseline_source_type", "skill lost input normalization rule");
359
+ assertIncludes(phase, skill, "Extract product thesis", "skill lost product system extraction rule");
360
+ assertIncludes(phase, skill, "Classify the request", "skill lost tune request interpretation rule");
361
+ assertIncludes(phase, skill, "screen_delta_matrix rows", "skill lost screen delta matrix rule");
362
+ assertIncludes(phase, skill, "Every screen prompt must include prompt_id", "skill lost screen manifest rule");
363
+ assertIncludes(phase, skill, "Generation Order, and Acceptance Checklist", "skill lost refined prompt pack output structure");
177
364
  assertIncludes(phase, skill, "Every tune pass must write or update a decision audit record internally.", "skill lost decision audit requirement");
178
365
  assertIncludes(phase, skill, "Do not expose /ow:decision as the next manual user step", "skill exposes decision as user step");
179
366
  assertNotIncludes(phase, extractBlock(skill, "handoff_commands"), "/ow:decision", "skill exposes decision in tune handoffs");
@@ -196,9 +383,90 @@ async function verifyInternalDecisionPhase(runtime) {
196
383
  assertIncludes(phase, skill, "<internal_audit_only>", "decision skill missing internal audit block");
197
384
  assertIncludes(phase, skill, "not as a normal user-facing workflow step", "decision skill exposes normal user workflow");
198
385
  }
386
+ async function verifyProductionCommandPhases(runtime) {
387
+ await verifySpecPhase(runtime);
388
+ await verifyChangePhase(runtime);
389
+ await verifyTeamPhase(runtime);
390
+ }
391
+ async function verifySpecPhase(runtime) {
392
+ const phase = "spec";
393
+ const source = command("spec", phase);
394
+ const protocol = protocolFor(source, phase);
395
+ assertPhase(phase, protocol.interactionMode === "accepted-design-to-production-spec", "spec source protocol is not production spec");
396
+ assertListIncludes(phase, protocol.requiredContext, ".openworkflow/design/DESIGN_INDEX.yaml", "spec does not require design index");
397
+ assertListIncludes(phase, protocol.allowedOutputs, ".openworkflow/specs/<id>/SPEC.yaml", "spec cannot write SPEC.yaml");
398
+ assertListIncludes(phase, protocol.forbiddenOutputs, ".openworkflow/changes/**", "spec can create changes");
399
+ assertExactList(phase, protocol.handoffCommands, ["/ow:change", "/ow:design"], "spec source handoffs changed");
400
+ assertSomeIncludes(phase, protocol.auditCheckpoints.before, "Lazy-create", "spec missing lazy-create checkpoint");
401
+ const generated = commandRecord(runtime, "spec", phase);
402
+ assertPhase(phase, stringField(generated, "depth", phase) === "deep", "generated spec is not deep");
403
+ assertListIncludes(phase, stringList(generated, "allowed_outputs", phase), ".openworkflow/specs/<id>/SPEC.yaml", "generated spec cannot write SPEC.yaml");
404
+ const packet = packetRecord(runtime, "/ow:spec", phase);
405
+ assertListIncludes(phase, stringList(packet, "required", phase), ".openworkflow/design/DESIGN_INDEX.yaml", "spec packet does not require design index");
406
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "before"], phase), "Lazy-create", "spec packet lost lazy-create checkpoint");
407
+ const skill = await readSkill(runtime, "ow-spec");
408
+ for (const required of ["<lazy_create>", "<spec_quality_bar>", "<readiness_gate>", "A production spec must be enough for an implementation agent"]) {
409
+ assertIncludes(phase, skill, required, `spec skill missing ${required}`);
410
+ }
411
+ const artifact = artifactRecord(runtime, "production_spec", phase);
412
+ assertPhase(phase, artifact.command === "/ow:spec", "production_spec command mismatch");
413
+ assertPhase(phase, artifact.lazy_create === true, "production_spec is not marked lazy_create");
414
+ assertPhase(phase, "summary_policy" in artifact, "production_spec missing summary policy");
415
+ const template = recordField(artifact, "template", phase);
416
+ assertPhase(phase, "change_readiness" in template, "production_spec template missing change_readiness");
417
+ }
418
+ async function verifyChangePhase(runtime) {
419
+ const phase = "change";
420
+ const source = command("change", phase);
421
+ const protocol = protocolFor(source, phase);
422
+ assertPhase(phase, protocol.interactionMode === "production-change-planning", "change source protocol is not production planning");
423
+ assertListIncludes(phase, protocol.requiredContext, ".openworkflow/specs/SPEC_INDEX.yaml", "change does not require spec index");
424
+ assertListIncludes(phase, protocol.allowedOutputs, ".openworkflow/changes/<id>/WORK_ITEMS.yaml", "change cannot write work items");
425
+ assertListIncludes(phase, protocol.forbiddenOutputs, ".openworkflow/runtime/**", "change can create runtime");
426
+ assertExactList(phase, protocol.handoffCommands, ["/ow:team", "/ow:spec"], "change source handoffs changed");
427
+ const generated = commandRecord(runtime, "change", phase);
428
+ assertPhase(phase, stringField(generated, "depth", phase) === "deep", "generated change is not deep");
429
+ const packet = packetRecord(runtime, "/ow:change", phase);
430
+ assertListIncludes(phase, stringList(packet, "required", phase), ".openworkflow/specs/SPEC_INDEX.yaml", "change packet does not require spec index");
431
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "during"], phase), "owned paths", "change packet lost work-item planning guidance");
432
+ const skill = await readSkill(runtime, "ow-change");
433
+ for (const required of ["<lazy_create>", "<planning_quality_bar>", "<readiness_gate>", "owned_paths"]) {
434
+ assertIncludes(phase, skill, required, `change skill missing ${required}`);
435
+ }
436
+ const artifact = artifactRecord(runtime, "production_change", phase);
437
+ assertPhase(phase, artifact.command === "/ow:change", "production_change command mismatch");
438
+ assertPhase(phase, artifact.lazy_create === true, "production_change is not marked lazy_create");
439
+ assertPhase(phase, "summary_policy" in artifact, "production_change missing summary policy");
440
+ const template = recordField(artifact, "template", phase);
441
+ assertPhase(phase, "runtime_readiness" in template, "production_change template missing runtime_readiness");
442
+ }
443
+ async function verifyTeamPhase(runtime) {
444
+ const phase = "team";
445
+ const source = command("team", phase);
446
+ const protocol = protocolFor(source, phase);
447
+ assertPhase(phase, protocol.interactionMode === "approved-change-team-execution", "team source protocol is not approved execution");
448
+ assertListIncludes(phase, protocol.requiredContext, ".openworkflow/changes/CHANGE_INDEX.yaml", "team does not require change index");
449
+ assertListIncludes(phase, protocol.allowedOutputs, ".openworkflow/runtime/<id>/STATE.yaml", "team cannot write runtime state");
450
+ assertExactList(phase, protocol.handoffCommands, ["/ow:change"], "team source handoffs changed");
451
+ const generated = commandRecord(runtime, "team", phase);
452
+ assertPhase(phase, stringField(generated, "depth", phase) === "deep", "generated team is not deep");
453
+ const packet = packetRecord(runtime, "/ow:team", phase);
454
+ assertListIncludes(phase, stringList(packet, "required", phase), ".openworkflow/changes/CHANGE_INDEX.yaml", "team packet does not require change index");
455
+ assertSomeIncludes(phase, nestedStringList(packet, ["audit_checkpoints", "before"], phase), "Lazy-create runtime state", "team packet lost lazy-create checkpoint");
456
+ const skill = await readSkill(runtime, "ow-team");
457
+ for (const required of ["<lazy_create>", "<execution_quality_bar>", "<handoff_gate>", "Track active change, active work item"]) {
458
+ assertIncludes(phase, skill, required, `team skill missing ${required}`);
459
+ }
460
+ const artifact = artifactRecord(runtime, "team_runtime", phase);
461
+ assertPhase(phase, artifact.command === "/ow:team", "team_runtime command mismatch");
462
+ assertPhase(phase, artifact.lazy_create === true, "team_runtime is not marked lazy_create");
463
+ assertPhase(phase, "summary_policy" in artifact, "team_runtime missing summary policy");
464
+ const template = recordField(artifact, "template", phase);
465
+ assertPhase(phase, "handoff" in template, "team_runtime template missing handoff");
466
+ }
199
467
  async function verifyDisplayLabels(runtime) {
200
468
  const phase = "display-labels";
201
- for (const id of ["vision", "validation", "proto", "tune", "decision", "design", "spec"]) {
469
+ for (const id of ["vision", "validation", "proto", "tune", "decision", "design", "spec", "change", "team"]) {
202
470
  const skillName = `ow-${id}`;
203
471
  const semanticCommand = `/ow:${id}`;
204
472
  const displayName = `ow:${id}`;
@@ -210,6 +478,142 @@ async function verifyDisplayLabels(runtime) {
210
478
  assertIncludes(phase, interfaceContent, `Use ${semanticCommand}`, `${skillName} default prompt lost semantic command`);
211
479
  }
212
480
  }
481
+ async function verifyBriefReadModel(runtime) {
482
+ const phase = "brief-read-model";
483
+ const report = JSON.parse(await runCapture(["node", CLI, "brief", "--root", runtime.target, "--json"], { ...process.env }));
484
+ assertPhase(phase, report.command === "brief", "brief report command mismatch");
485
+ assertPhase(phase, report.ok === true, "brief report should be ok");
486
+ const json = recordField(report, "data", phase);
487
+ for (const key of ["project", "workflow", "read_this_first", "active_pointers", "health", "git", "agent_guidance"]) {
488
+ assertPhase(phase, key in json, `brief missing key ${key}`);
489
+ }
490
+ const workflow = recordField(json, "workflow", phase);
491
+ assertPhase(phase, workflow.active_stage === "workflow", "brief active_stage mismatch");
492
+ assertPhase(phase, workflow.next_command === "/ow:vision", "brief next_command mismatch");
493
+ const readThisFirst = json.read_this_first;
494
+ assertPhase(phase, Array.isArray(readThisFirst), "brief read_this_first must be a list");
495
+ assertPhase(phase, readThisFirst.includes(".openworkflow/CURRENT_STATE.yaml"), "brief does not include current state read pointer");
496
+ const guidance = recordField(json, "agent_guidance", phase);
497
+ assertPhase(phase, String(guidance.recommended_next_action).includes("/ow:vision"), "brief guidance does not point to next command");
498
+ }
499
+ async function verifyCommandReadiness(runtime) {
500
+ const phase = "command-readiness";
501
+ const report = JSON.parse(await runCapture(["node", CLI, "check", "/ow:vision", "--root", runtime.target, "--json"], { ...process.env }));
502
+ assertPhase(phase, report.command === "check", "check report command mismatch");
503
+ assertPhase(phase, report.ok === true, "vision check should be ok");
504
+ const data = recordField(report, "data", phase);
505
+ assertPhase(phase, data.ready === true, "vision should be ready");
506
+ assertPhase(phase, Array.isArray(data.required_context), "check missing required_context");
507
+ assertPhase(phase, Array.isArray(data.forbidden_context), "check missing forbidden_context");
508
+ assertPhase(phase, Array.isArray(data.allowed_outputs), "check missing allowed_outputs");
509
+ assertPhase(phase, Array.isArray(data.handoff_commands), "check missing handoff_commands");
510
+ const spec = await runCaptureStatus(["node", CLI, "check", "ow-spec", "--root", runtime.target, "--json"], { ...process.env });
511
+ assertPhase(phase, spec.code !== 0, "spec check should fail without product design");
512
+ const specReport = JSON.parse(spec.output);
513
+ const specData = recordField(specReport, "data", phase);
514
+ assertPhase(phase, Array.isArray(specData.blockers), "spec check missing blockers");
515
+ }
516
+ async function verifySummaryReadModel(runtime) {
517
+ const phase = "summary-read-model";
518
+ const report = JSON.parse(await runCapture(["node", CLI, "summaries", "--root", runtime.target, "--json"], { ...process.env }));
519
+ assertPhase(phase, report.command === "summaries", "summaries report command mismatch");
520
+ assertPhase(phase, report.ok === true, "fresh summaries report should be ok");
521
+ const data = recordField(report, "data", phase);
522
+ const counts = recordField(data, "counts", phase);
523
+ assertPhase(phase, Number(counts.not_instantiated) > 0, "fresh summaries should report not_instantiated artifacts");
524
+ assertPhase(phase, Array.isArray(data.entries), "summaries missing entries");
525
+ const brief = JSON.parse(await runCapture(["node", CLI, "brief", "--root", runtime.target, "--json"], { ...process.env }));
526
+ const briefData = recordField(brief, "data", phase);
527
+ const health = recordField(briefData, "health", phase);
528
+ assertPhase(phase, "summaries" in health, "brief health missing summaries");
529
+ const check = JSON.parse(await runCapture(["node", CLI, "check", "/ow:vision", "--root", runtime.target, "--json"], { ...process.env }));
530
+ const checkData = recordField(check, "data", phase);
531
+ assertPhase(phase, Array.isArray(checkData.summary_guidance), "check missing summary_guidance");
532
+ const inspect = JSON.parse(await runCapture(["node", CLI, "inspect", "--root", runtime.target, "--json"], { ...process.env }));
533
+ assertPhase(phase, inspect.command === "inspect", "inspect report command mismatch");
534
+ assertPhase(phase, inspect.ok === true, "fresh inspect report should be ok");
535
+ const inspectData = recordField(inspect, "data", phase);
536
+ for (const key of ["project", "workflow", "health", "summaries", "next_command_check", "read_order", "recommended_next_actions"]) {
537
+ assertPhase(phase, key in inspectData, `inspect missing key ${key}`);
538
+ }
539
+ const readOrder = recordField(inspectData, "read_order", phase);
540
+ assertPhase(phase, Array.isArray(readOrder.must_read), "inspect read_order missing must_read");
541
+ assertPhase(phase, readOrder.must_read.includes(".openworkflow/CURRENT_STATE.yaml"), "inspect must_read missing current state");
542
+ const context = JSON.parse(await runCapture(["node", CLI, "context", "--root", runtime.target, "--json"], { ...process.env }));
543
+ assertPhase(phase, context.command === "context", "context report command mismatch");
544
+ assertPhase(phase, context.ok === true, "fresh context report should be ok");
545
+ const contextData = recordField(context, "data", phase);
546
+ for (const key of ["normalized_command", "packet_id", "budget", "readiness", "inspect", "context_packet", "included", "omitted", "recommended_next_actions"]) {
547
+ assertPhase(phase, key in contextData, `context missing key ${key}`);
548
+ }
549
+ assertPhase(phase, contextData.normalized_command === "/ow:vision", "context should default to CURRENT_STATE.next_command");
550
+ assertPhase(phase, contextData.packet_id === "context:vision", "context missing packet id");
551
+ assertPhase(phase, Array.isArray(contextData.included), "context included must be array");
552
+ assertPhase(phase, Array.isArray(contextData.omitted), "context omitted must be array");
553
+ }
554
+ async function runCaptureStatus(command, env) {
555
+ return new Promise((resolvePromise, reject) => {
556
+ let output = "";
557
+ const child = spawn(command[0] ?? "", command.slice(1), {
558
+ cwd: REPO_ROOT,
559
+ env,
560
+ stdio: ["ignore", "pipe", "pipe"],
561
+ });
562
+ child.stdout.on("data", (chunk) => {
563
+ output += chunk.toString("utf8");
564
+ });
565
+ child.stderr.on("data", (chunk) => {
566
+ output += chunk.toString("utf8");
567
+ });
568
+ child.on("error", reject);
569
+ child.on("close", (code) => {
570
+ resolvePromise({ code, output });
571
+ });
572
+ });
573
+ }
574
+ async function verifyAgentOnboarding(target, env) {
575
+ const phase = "agent-onboarding";
576
+ const guide = await read(join(target, "AGENTS.md"));
577
+ assertIncludes(phase, guide, "openworkflow --help", "AGENTS.md does not point agents to CLI help");
578
+ assertIncludes(phase, guide, "Prefer `--json`", "AGENTS.md does not mention JSON report mode");
579
+ assertIncludes(phase, guide, "openworkflow inspect --root . --json", "AGENTS.md does not mention inspect command");
580
+ assertIncludes(phase, guide, "openworkflow context --root . --json", "AGENTS.md does not mention context command");
581
+ assertIncludes(phase, guide, "command_audit", "AGENTS.md does not mention compact command audit slice");
582
+ assertIncludes(phase, guide, "--max-bytes <n>", "AGENTS.md does not mention context budget");
583
+ assertIncludes(phase, guide, "--mode full", "AGENTS.md does not mention full context mode");
584
+ assertIncludes(phase, guide, "openworkflow draft --root . --artifact <type> --id <id> --json", "AGENTS.md does not mention draft command");
585
+ assertIncludes(phase, guide, "openworkflow register --root . --artifact <path> --json", "AGENTS.md does not mention register command");
586
+ assertIncludes(phase, guide, ".openworkflow/CURRENT_STATE.yaml", "AGENTS.md does not point agents to current state");
587
+ assertIncludes(phase, guide, "CLI commands maintain and summarize the repo-local workflow surface", "AGENTS.md does not distinguish CLI maintenance commands");
588
+ assertIncludes(phase, guide, "openworkflow brief --root .", "AGENTS.md does not mention brief command");
589
+ assertIncludes(phase, guide, "openworkflow status --root .", "AGENTS.md does not mention status command");
590
+ assertIncludes(phase, guide, "openworkflow check /ow:<command> --root . --json", "AGENTS.md does not mention check command");
591
+ assertIncludes(phase, guide, "openworkflow summaries --root . --json", "AGENTS.md does not mention summaries command");
592
+ assertIncludes(phase, guide, "openworkflow summarize --root . --artifact <path> --json", "AGENTS.md does not mention summarize command");
593
+ assertIncludes(phase, guide, "SUMMARY.yaml trust is checked by `summaries`, not by `validate`", "AGENTS.md does not explain validate/summaries boundary");
594
+ assertIncludes(phase, guide, "Repo-local workflow commands are delivered as Agent skills", "AGENTS.md does not distinguish workflow skill commands");
595
+ assertIncludes(phase, guide, "Respect lazy creation", "AGENTS.md does not preserve lazy artifact creation boundary");
596
+ const help = await runCapture(["node", CLI, "--help"], env);
597
+ assertIncludes(phase, help, "Agent quick start", "help missing Agent quick start");
598
+ assertIncludes(phase, help, "Two command surfaces", "help missing command surface distinction");
599
+ assertIncludes(phase, help, "Repo-local workflow commands are Agent skills", "help missing workflow skill explanation");
600
+ assertIncludes(phase, help, "Lazy creation boundary", "help missing lazy creation boundary");
601
+ assertIncludes(phase, help, "Every command supports --json", "help missing JSON report mode");
602
+ assertIncludes(phase, help, "inspect", "help missing inspect command");
603
+ assertIncludes(phase, help, "context", "help missing context command");
604
+ assertIncludes(phase, help, "command_audit", "help missing compact command audit slice");
605
+ assertIncludes(phase, help, "--max-bytes", "help missing context budget");
606
+ assertIncludes(phase, help, "--mode full", "help missing full context mode");
607
+ assertIncludes(phase, help, "draft", "help missing draft command");
608
+ assertIncludes(phase, help, "contract-shaped source artifact", "help missing draft boundary");
609
+ assertIncludes(phase, help, "register", "help missing register command");
610
+ assertIncludes(phase, help, "index registration", "help missing register boundary");
611
+ assertIncludes(phase, help, "check", "help missing check command");
612
+ assertIncludes(phase, help, "summaries", "help missing summaries command");
613
+ assertIncludes(phase, help, "summarize", "help missing summarize command");
614
+ assertIncludes(phase, help, "pass --write to update summary files", "help missing summarize write boundary");
615
+ assertIncludes(phase, help, "SUMMARY.yaml freshness is checked by summaries", "help missing validate/summaries boundary");
616
+ }
213
617
  async function run(command, env) {
214
618
  await new Promise((resolvePromise, reject) => {
215
619
  const child = spawn(command[0] ?? "", command.slice(1), {
@@ -228,6 +632,31 @@ async function run(command, env) {
228
632
  });
229
633
  });
230
634
  }
635
+ async function runCapture(command, env) {
636
+ return new Promise((resolvePromise, reject) => {
637
+ let output = "";
638
+ const child = spawn(command[0] ?? "", command.slice(1), {
639
+ cwd: REPO_ROOT,
640
+ env,
641
+ stdio: ["ignore", "pipe", "pipe"],
642
+ });
643
+ child.stdout.on("data", (chunk) => {
644
+ output += chunk.toString("utf8");
645
+ });
646
+ child.stderr.on("data", (chunk) => {
647
+ output += chunk.toString("utf8");
648
+ });
649
+ child.on("error", reject);
650
+ child.on("close", (code) => {
651
+ if (code === 0) {
652
+ resolvePromise(output);
653
+ }
654
+ else {
655
+ reject(new Error(`command failed (${code ?? "signal"}): ${command.join(" ")}\n${output}`));
656
+ }
657
+ });
658
+ });
659
+ }
231
660
  function command(id, phase) {
232
661
  const found = getWorkflowCommands().find((item) => item.id === id);
233
662
  if (!found) {
@@ -247,6 +676,9 @@ function commandRecord(runtime, id, phase) {
247
676
  function packetRecord(runtime, command, phase) {
248
677
  return findRecord(runtime.packets, "command", command, phase, `context packet missing ${command}`);
249
678
  }
679
+ function artifactRecord(runtime, artifactType, phase) {
680
+ return findRecord(runtime.artifacts, "artifact_type", artifactType, phase, `artifact contract missing ${artifactType}`);
681
+ }
250
682
  function findRecord(records, key, value, phase, message) {
251
683
  const found = records.find((record) => record[key] === value);
252
684
  if (!found) {