create-agdf 0.30.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/generated/.agdf/control/README.md +5 -0
  2. package/generated/.agdf/control/templates/BRAND.md +28 -0
  3. package/generated/.agdf/control/templates/artefacts/PRD.md +11 -0
  4. package/generated/.agdf/control/templates/artefacts/SD.md +4 -0
  5. package/generated/.agdf/control/templates/artefacts/UX_INTENT_DEFINITION.md +1 -1
  6. package/generated/.opencode/agdf-interaction-locales.json +292 -2
  7. package/generated/.opencode/contracts/gate-artifact-preparation.md +9 -3
  8. package/generated/.opencode/contracts/interaction.md +10 -4
  9. package/generated/.opencode/contracts/modes.md +5 -0
  10. package/generated/.opencode/contracts/prd-definition.md +40 -1
  11. package/generated/.opencode/contracts/quality.md +35 -0
  12. package/generated/.opencode/contracts/request-activation.md +6 -3
  13. package/generated/.opencode/contracts/sd-definition.md +46 -0
  14. package/generated/.opencode/contracts/tp-definition.md +8 -0
  15. package/generated/.opencode/contracts/ur-definition.md +9 -5
  16. package/generated/.opencode/skills/agdf-ux-intent-definition/SKILL.md +31 -2
  17. package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
  18. package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
  19. package/generated/plugins/agdf/companion/events.mjs +117 -0
  20. package/generated/plugins/agdf/companion/figure.mjs +6 -6
  21. package/generated/plugins/agdf/companion/gate-bar.mjs +10 -28
  22. package/generated/plugins/agdf/companion/localization.mjs +10 -32
  23. package/generated/plugins/agdf/companion/notice.mjs +10 -8
  24. package/generated/plugins/agdf/companion/register.mjs +229 -43
  25. package/generated/plugins/agdf/companion/run-state.mjs +77 -90
  26. package/generated/plugins/agdf/companion/state.mjs +6 -32
  27. package/generated/plugins/agdf/companion/view.mjs +57 -12
  28. package/generated/plugins/agdf/control/README.md +5 -0
  29. package/generated/plugins/agdf/control/templates/BRAND.md +28 -0
  30. package/generated/plugins/agdf/control/templates/artefacts/PRD.md +11 -0
  31. package/generated/plugins/agdf/control/templates/artefacts/SD.md +4 -0
  32. package/generated/plugins/agdf/control/templates/artefacts/UX_INTENT_DEFINITION.md +1 -1
  33. package/generated/plugins/agdf/mcp/server/README.md +26 -2
  34. package/generated/plugins/agdf/mcp/server/package.json +3 -3
  35. package/generated/plugins/agdf/mcp/server/src/generation-runtime.js +11 -2
  36. package/generated/plugins/agdf/mcp/server/src/main.js +1 -0
  37. package/generated/plugins/agdf/mcp/server/src/next-action.js +227 -0
  38. package/generated/plugins/agdf/mcp/server/src/prepared-call-transport.js +306 -0
  39. package/generated/plugins/agdf/mcp/server/src/safe-read-continuation.js +62 -0
  40. package/generated/plugins/agdf/mcp/server/src/server.js +139 -35
  41. package/generated/plugins/agdf/mcp/server/ui/cockpit.html +2 -2
  42. package/generated/plugins/agdf/mcp/server/ui/decision-manifest.json +3 -3
  43. package/generated/plugins/agdf/mcp/server/ui/decision.html +2 -2
  44. package/generated/plugins/agdf/mcp/server/ui/manifest.json +3 -3
  45. package/generated/plugins/agdf/meta/agdf-interaction-locales.json +292 -2
  46. package/generated/plugins/agdf/meta/agdf-mcp-capability.json +1 -1
  47. package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  48. package/generated/plugins/agdf/meta/contracts/gate-artifact-preparation.md +9 -3
  49. package/generated/plugins/agdf/meta/contracts/interaction.md +10 -4
  50. package/generated/plugins/agdf/meta/contracts/modes.md +5 -0
  51. package/generated/plugins/agdf/meta/contracts/prd-definition.md +40 -1
  52. package/generated/plugins/agdf/meta/contracts/quality.md +35 -0
  53. package/generated/plugins/agdf/meta/contracts/request-activation.md +6 -3
  54. package/generated/plugins/agdf/meta/contracts/sd-definition.md +46 -0
  55. package/generated/plugins/agdf/meta/contracts/tp-definition.md +8 -0
  56. package/generated/plugins/agdf/meta/contracts/ur-definition.md +9 -5
  57. package/generated/plugins/agdf/meta/copilot-payload-baseline.json +3 -3
  58. package/generated/plugins/agdf/meta/distribution-profile-history.json +7 -0
  59. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +292 -2
  60. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  61. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/gate-artifact-preparation.md +9 -3
  62. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/interaction.md +10 -4
  63. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/modes.md +5 -0
  64. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/prd-definition.md +40 -1
  65. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/quality.md +35 -0
  66. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/request-activation.md +6 -3
  67. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/sd-definition.md +46 -0
  68. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/tp-definition.md +8 -0
  69. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/ur-definition.md +9 -5
  70. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/command-registry.js +2 -1
  71. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/parse-args.js +7 -4
  72. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/project-read-command.js +29 -0
  73. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/validation-handlers.js +2 -1
  74. package/generated/plugins/agdf/runtime/create-agdf/lib/mcp-dispatch-runtime.js +32 -6
  75. package/generated/plugins/agdf/runtime/create-agdf/lib/mcp-lifecycle/package.js +7 -0
  76. package/generated/plugins/agdf/runtime/create-agdf/lib/mcp-lifecycle/plugin-runtime.js +34 -6
  77. package/generated/plugins/agdf/runtime/create-agdf/lib/mcp-lifecycle/prepared-call-journal.js +269 -0
  78. package/generated/plugins/agdf/runtime/create-agdf/lib/mcp-worker-runtime.js +86 -83
  79. package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/validator-application.js +2 -2
  80. package/generated/plugins/agdf/runtime/create-agdf/package.json +1 -1
  81. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/events.js +154 -0
  82. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/figure-state.js +36 -0
  83. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/journal.js +180 -0
  84. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/run-view.js +184 -0
  85. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/acceptance-criteria.js +2 -0
  86. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/closeout-readiness.js +24 -6
  87. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-check.js +44 -6
  88. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-policy-inputs.js +2 -0
  89. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-policy-pure.js +7 -3
  90. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/prd-readiness.js +7 -2
  91. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/report-decision.js +9 -0
  92. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/traceability-readiness.js +57 -8
  93. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/ur-readiness.js +15 -2
  94. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/artifact-readiness.js +5 -3
  95. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/authoring-inputs.js +107 -22
  96. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/cockpit.js +4 -20
  97. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/contract.js +64 -24
  98. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/material-recording-calls.js +54 -0
  99. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/project-state.js +2 -1
  100. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/run-classification.js +31 -0
  101. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/selection.js +6 -0
  102. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/service.js +32 -5
  103. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/authoring-text-handoff.js +3 -0
  104. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/input-resolution.js +7 -5
  105. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/material-limits.js +4 -0
  106. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/prepared-control-calls.js +137 -0
  107. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/presentation-delivery.js +4 -1
  108. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-command-contract.js +10 -4
  109. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-command-schema.js +9 -3
  110. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-command-service.js +35 -11
  111. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-identity.js +2 -0
  112. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-inventory-reader.js +5 -3
  113. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-material.js +25 -7
  114. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-presentation-render.js +84 -28
  115. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-revision.js +38 -20
  116. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-source-revisions.js +37 -17
  117. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-state-reader.js +10 -3
  118. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-steps.js +8 -5
  119. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/index.js +3 -1
  120. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/interaction-catalog-validation.js +6 -1
  121. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/interaction-catalog.js +8 -0
  122. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/interaction-presentation.js +26 -1
  123. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/mcp-input-hints.js +30 -1
  124. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/mcp-tool-errors.js +5 -3
  125. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/resources/canonical-init-plan.js +1 -1
  126. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/runtime/plugin-provenance.js +9 -2
  127. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/contract.js +6 -4
  128. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/delivery-intake.js +3 -1
  129. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/mcp-continuation.js +25 -2
  130. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/prd-definition.js +3 -2
  131. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/readiness-findings.js +7 -0
  132. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/sd-definition.js +4 -2
  133. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/service.js +17 -4
  134. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/tp-definition.js +4 -2
  135. package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
  136. package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +4 -3
  137. package/generated/plugins/agdf/skills/ux-intent-definition/SKILL.md +31 -2
  138. package/generated/plugins/agdf/skills/ux-intent-definition/help.md +8 -1
  139. package/generated/plugins/agdf/submission/openai/availability.md +1 -1
  140. package/generated/plugins/agdf/submission/openai/capability-matrix.json +1 -1
  141. package/generated/plugins/agdf/submission/openai/release-notes.md +8 -8
  142. package/generated/plugins/agdf/submission/openai/reviewer-cases.json +1 -1
  143. package/generated/plugins/agdf-planning/.codex-plugin/plugin.json +1 -1
  144. package/generated/plugins/agdf-planning/plugin.json +1 -1
  145. package/generated/plugins/copilot/agdf/.agdf-payload-inventory.json +332 -195
  146. package/generated/plugins/copilot/agdf/control/README.md +5 -0
  147. package/generated/plugins/copilot/agdf/control/templates/BRAND.md +28 -0
  148. package/generated/plugins/copilot/agdf/control/templates/artefacts/PRD.md +11 -0
  149. package/generated/plugins/copilot/agdf/control/templates/artefacts/SD.md +4 -0
  150. package/generated/plugins/copilot/agdf/control/templates/artefacts/UX_INTENT_DEFINITION.md +1 -1
  151. package/generated/plugins/copilot/agdf/mcp/server/README.md +26 -2
  152. package/generated/plugins/copilot/agdf/mcp/server/package.json +3 -3
  153. package/generated/plugins/copilot/agdf/mcp/server/src/generation-runtime.js +11 -2
  154. package/generated/plugins/copilot/agdf/mcp/server/src/main.js +1 -0
  155. package/generated/plugins/copilot/agdf/mcp/server/src/next-action.js +227 -0
  156. package/generated/plugins/copilot/agdf/mcp/server/src/prepared-call-transport.js +306 -0
  157. package/generated/plugins/copilot/agdf/mcp/server/src/safe-read-continuation.js +62 -0
  158. package/generated/plugins/copilot/agdf/mcp/server/src/server.js +139 -35
  159. package/generated/plugins/copilot/agdf/meta/agdf-plugin.definition.json +1 -1
  160. package/generated/plugins/copilot/agdf/meta/distribution-profile-history.json +7 -0
  161. package/generated/plugins/copilot/agdf/plugin.json +1 -1
  162. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +292 -2
  163. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  164. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/gate-artifact-preparation.md +9 -3
  165. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/interaction.md +10 -4
  166. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/modes.md +5 -0
  167. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/prd-definition.md +40 -1
  168. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/quality.md +35 -0
  169. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/request-activation.md +6 -3
  170. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/sd-definition.md +46 -0
  171. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/tp-definition.md +8 -0
  172. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/ur-definition.md +9 -5
  173. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/command-registry.js +2 -1
  174. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/parse-args.js +7 -4
  175. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/project-read-command.js +29 -0
  176. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/validation-handlers.js +2 -1
  177. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/mcp-dispatch-runtime.js +32 -6
  178. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/mcp-lifecycle/package.js +7 -0
  179. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/mcp-lifecycle/plugin-runtime.js +34 -6
  180. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/mcp-lifecycle/prepared-call-journal.js +269 -0
  181. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/mcp-worker-runtime.js +81 -78
  182. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime/validator-application.js +2 -2
  183. package/generated/plugins/copilot/agdf/runtime/create-agdf/package.json +1 -1
  184. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/events.js +154 -0
  185. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/figure-state.js +36 -0
  186. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/journal.js +180 -0
  187. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/run-view.js +184 -0
  188. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/acceptance-criteria.js +2 -0
  189. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/closeout-readiness.js +24 -6
  190. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-check.js +44 -6
  191. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-policy-inputs.js +2 -0
  192. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-policy-pure.js +7 -3
  193. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/prd-readiness.js +7 -2
  194. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/report-decision.js +9 -0
  195. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/traceability-readiness.js +57 -8
  196. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/ur-readiness.js +15 -2
  197. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/artifact-readiness.js +5 -3
  198. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/authoring-inputs.js +107 -22
  199. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/contract.js +64 -24
  200. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/material-recording-calls.js +54 -0
  201. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/project-state.js +2 -1
  202. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/run-classification.js +31 -0
  203. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/selection.js +6 -0
  204. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/service.js +32 -5
  205. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/authoring-text-handoff.js +3 -0
  206. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/input-resolution.js +7 -5
  207. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/material-limits.js +4 -0
  208. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/prepared-control-calls.js +137 -0
  209. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/presentation-delivery.js +4 -1
  210. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-command-contract.js +10 -4
  211. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-command-schema.js +9 -3
  212. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-command-service.js +35 -11
  213. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-identity.js +2 -0
  214. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-inventory-reader.js +5 -3
  215. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-material.js +25 -7
  216. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-presentation-render.js +84 -28
  217. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-revision.js +38 -20
  218. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-source-revisions.js +37 -17
  219. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-state-reader.js +10 -3
  220. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-steps.js +8 -5
  221. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/index.js +3 -1
  222. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/interaction-catalog-validation.js +6 -1
  223. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/interaction-catalog.js +8 -0
  224. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/interaction-presentation.js +26 -1
  225. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/mcp-input-hints.js +30 -1
  226. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/mcp-tool-errors.js +5 -3
  227. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/resources/canonical-init-plan.js +1 -1
  228. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/runtime/plugin-provenance.js +9 -2
  229. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/contract.js +6 -4
  230. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/delivery-intake.js +3 -1
  231. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/mcp-continuation.js +25 -2
  232. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/prd-definition.js +3 -2
  233. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/readiness-findings.js +7 -0
  234. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/sd-definition.js +4 -2
  235. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/service.js +17 -4
  236. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/tp-definition.js +4 -2
  237. package/generated/plugins/copilot/agdf/runtime/runtime-manifest.json +2 -2
  238. package/generated/plugins/copilot/agdf/skills/agdf-interaction-locales.json +1 -1
  239. package/generated/plugins/copilot/agdf/skills/agdf-ux-intent-definition/SKILL.md +31 -2
  240. package/generated/plugins/copilot/agdf/skills/contracts/gate-artifact-preparation.md +9 -3
  241. package/generated/plugins/copilot/agdf/skills/contracts/interaction.md +10 -4
  242. package/generated/plugins/copilot/agdf/skills/contracts/modes.md +5 -0
  243. package/generated/plugins/copilot/agdf/skills/contracts/prd-definition.md +40 -1
  244. package/generated/plugins/copilot/agdf/skills/contracts/quality.md +35 -0
  245. package/generated/plugins/copilot/agdf/skills/contracts/request-activation.md +6 -3
  246. package/generated/plugins/copilot/agdf/skills/contracts/sd-definition.md +46 -0
  247. package/generated/plugins/copilot/agdf/skills/contracts/tp-definition.md +8 -0
  248. package/generated/plugins/copilot/agdf/skills/contracts/ur-definition.md +9 -5
  249. package/generated/submissions/openai/agdf/.codex-plugin/plugin.json +1 -1
  250. package/generated/submissions/openai/agdf/meta/agdf-interaction-locales.json +292 -2
  251. package/generated/submissions/openai/agdf/meta/agdf-plugin.definition.json +1 -1
  252. package/generated/submissions/openai/agdf/meta/contracts/gate-artifact-preparation.md +9 -3
  253. package/generated/submissions/openai/agdf/meta/contracts/interaction.md +10 -4
  254. package/generated/submissions/openai/agdf/meta/contracts/modes.md +5 -0
  255. package/generated/submissions/openai/agdf/meta/contracts/prd-definition.md +40 -1
  256. package/generated/submissions/openai/agdf/meta/contracts/quality.md +35 -0
  257. package/generated/submissions/openai/agdf/meta/contracts/request-activation.md +6 -3
  258. package/generated/submissions/openai/agdf/meta/contracts/sd-definition.md +46 -0
  259. package/generated/submissions/openai/agdf/meta/contracts/tp-definition.md +8 -0
  260. package/generated/submissions/openai/agdf/meta/contracts/ur-definition.md +9 -5
  261. package/generated/submissions/openai/agdf/meta/copilot-payload-baseline.json +3 -3
  262. package/generated/submissions/openai/agdf/meta/distribution-profile-history.json +7 -0
  263. package/generated/submissions/openai/agdf/plugin.json +1 -1
  264. package/generated/submissions/openai/agdf/skills/ux-intent-definition/SKILL.md +31 -2
  265. package/generated/submissions/openai/agdf/skills/ux-intent-definition/help.md +8 -1
  266. package/generated/submissions/openai/agdf/submission/openai/availability.md +1 -1
  267. package/generated/submissions/openai/agdf/submission/openai/capability-matrix.json +1 -1
  268. package/generated/submissions/openai/agdf/submission/openai/inventory.json +24 -24
  269. package/generated/submissions/openai/agdf/submission/openai/listing.json +1 -1
  270. package/generated/submissions/openai/agdf/submission/openai/readiness.json +2 -2
  271. package/generated/submissions/openai/agdf/submission/openai/readiness.md +2 -2
  272. package/generated/submissions/openai/agdf/submission/openai/release-notes.md +8 -8
  273. package/generated/submissions/openai/agdf/submission/openai/reviewer-cases.json +1 -1
  274. package/lib/cli/command-registry.js +2 -1
  275. package/lib/cli/parse-args.js +7 -4
  276. package/lib/cli/project-read-command.js +29 -0
  277. package/lib/cli/validation-handlers.js +2 -1
  278. package/lib/mcp-dispatch-runtime.js +32 -6
  279. package/lib/mcp-lifecycle/package.js +7 -0
  280. package/lib/mcp-lifecycle/plugin-runtime.js +34 -6
  281. package/lib/mcp-lifecycle/prepared-call-journal.js +269 -0
  282. package/lib/mcp-worker-runtime.js +86 -83
  283. package/lib/runtime/validator-application.js +2 -2
  284. package/lib/scaffold/plan.js +2 -0
  285. package/package.json +1 -1
  286. package/runtime/core/lib/companion/events.js +154 -0
  287. package/runtime/core/lib/companion/figure-state.js +36 -0
  288. package/runtime/core/lib/companion/journal.js +180 -0
  289. package/runtime/core/lib/companion/run-view.js +184 -0
  290. package/runtime/core/lib/control-evaluation/acceptance-criteria.js +2 -0
  291. package/runtime/core/lib/control-evaluation/closeout-readiness.js +24 -6
  292. package/runtime/core/lib/control-evaluation/gate-check.js +44 -6
  293. package/runtime/core/lib/control-evaluation/gate-policy-inputs.js +2 -0
  294. package/runtime/core/lib/control-evaluation/gate-policy-pure.js +7 -3
  295. package/runtime/core/lib/control-evaluation/prd-readiness.js +7 -2
  296. package/runtime/core/lib/control-evaluation/report-decision.js +9 -0
  297. package/runtime/core/lib/control-evaluation/traceability-readiness.js +57 -8
  298. package/runtime/core/lib/control-evaluation/ur-readiness.js +15 -2
  299. package/runtime/core/lib/control-inspect/artifact-readiness.js +5 -3
  300. package/runtime/core/lib/control-inspect/authoring-inputs.js +107 -22
  301. package/runtime/core/lib/control-inspect/cockpit.js +4 -20
  302. package/runtime/core/lib/control-inspect/contract.js +64 -24
  303. package/runtime/core/lib/control-inspect/material-recording-calls.js +54 -0
  304. package/runtime/core/lib/control-inspect/project-state.js +2 -1
  305. package/runtime/core/lib/control-inspect/run-classification.js +31 -0
  306. package/runtime/core/lib/control-inspect/selection.js +6 -0
  307. package/runtime/core/lib/control-inspect/service.js +32 -5
  308. package/runtime/core/lib/control-state/authoring-text-handoff.js +3 -0
  309. package/runtime/core/lib/control-state/input-resolution.js +7 -5
  310. package/runtime/core/lib/control-state/material-limits.js +4 -0
  311. package/runtime/core/lib/control-state/prepared-control-calls.js +137 -0
  312. package/runtime/core/lib/control-state/presentation-delivery.js +4 -1
  313. package/runtime/core/lib/control-state/run-command-contract.js +10 -4
  314. package/runtime/core/lib/control-state/run-command-schema.js +9 -3
  315. package/runtime/core/lib/control-state/run-command-service.js +35 -11
  316. package/runtime/core/lib/control-state/run-identity.js +2 -0
  317. package/runtime/core/lib/control-state/run-inventory-reader.js +5 -3
  318. package/runtime/core/lib/control-state/run-material.js +25 -7
  319. package/runtime/core/lib/control-state/run-presentation-render.js +84 -28
  320. package/runtime/core/lib/control-state/run-revision.js +38 -20
  321. package/runtime/core/lib/control-state/run-source-revisions.js +37 -17
  322. package/runtime/core/lib/control-state/run-state-reader.js +10 -3
  323. package/runtime/core/lib/control-state/run-steps.js +8 -5
  324. package/runtime/core/lib/index.js +3 -1
  325. package/runtime/core/lib/interaction-catalog-validation.js +6 -1
  326. package/runtime/core/lib/interaction-catalog.js +8 -0
  327. package/runtime/core/lib/interaction-presentation.js +26 -1
  328. package/runtime/core/lib/mcp-input-hints.js +30 -1
  329. package/runtime/core/lib/mcp-tool-errors.js +5 -3
  330. package/runtime/core/lib/resources/canonical-init-plan.js +1 -1
  331. package/runtime/core/lib/runtime/plugin-provenance.js +9 -2
  332. package/runtime/core/lib/skill-dispatch/contract.js +6 -4
  333. package/runtime/core/lib/skill-dispatch/delivery-intake.js +3 -1
  334. package/runtime/core/lib/skill-dispatch/mcp-continuation.js +25 -2
  335. package/runtime/core/lib/skill-dispatch/prd-definition.js +3 -2
  336. package/runtime/core/lib/skill-dispatch/readiness-findings.js +7 -0
  337. package/runtime/core/lib/skill-dispatch/sd-definition.js +4 -2
  338. package/runtime/core/lib/skill-dispatch/service.js +17 -4
  339. package/runtime/core/lib/skill-dispatch/tp-definition.js +4 -2
  340. package/generated/plugins/agdf/companion/core.mjs +0 -16
  341. package/generated/plugins/agdf/companion/locales.json +0 -200
@@ -15,8 +15,8 @@ import { containedRegularFile, hasSymlinkComponent } from './contained-file.js';
15
15
  import { readPendingSourceRevisionJournal } from './run-step-transaction.js';
16
16
  import { inspectPlannedOperation, inspectPlannedSelection } from './planned-entry.js';
17
17
 
18
- export const MATERIAL_LIMITS = Object.freeze({ part: 16384, file: 131072, set: 524288, sets: 4, total: 2097152,
19
- lifetime: 600000, idle: 120000 });
18
+ import { MATERIAL_LIMITS } from './material-limits.js';
19
+ export { MATERIAL_LIMITS } from './material-limits.js';
20
20
 
21
21
  // A rejected transfer step names its concrete cause; the reason code stays stable for clients.
22
22
  const MATERIAL_RECOVERY = Object.freeze({
@@ -108,23 +108,34 @@ export function createRunMaterialStore({ now = Date.now } = {}) {
108
108
  const state = current(input), p = input.command.payload;
109
109
  expire();
110
110
  let row;
111
- if (p.action === 'begin') {
111
+ if (p.action === 'begin' || p.action === 'put') {
112
+ // The small-text path bundles only private transfer work. Validate the
113
+ // whole set before allocating; canonical recording still uses its owner.
114
+ const files = p.action === 'put' ? p.files.map(({ slot, content }) => {
115
+ const bytes = Buffer.from(content, 'utf8');
116
+ if (bytes.length > MATERIAL_LIMITS.inline || new TextDecoder('utf-8', { fatal: true }).decode(bytes) !== content)
117
+ throw Error('material_text_invalid');
118
+ return { slot, bytes: bytes.length, content, data: bytes };
119
+ }) : p.files;
112
120
  const request = digest(canonicalJson(input));
113
121
  row = sets.get(input.operation_id);
114
122
  if (row) {
115
123
  if (row.request !== request || row.binding !== binding(input)) throw Error('operation_payload_conflict');
116
124
  } else {
117
- const size = p.files.reduce((n, f) => n + f.bytes, 0);
125
+ const size = files.reduce((n, f) => n + f.bytes, 0);
118
126
  const resolutionBytes = currentInputResolution() ? Buffer.byteLength(canonicalJson(currentInputResolution())) : 0;
119
- if (!p.files.length || p.files.length > 4 || p.files.some(f => !Number.isSafeInteger(f.bytes) || f.bytes < 0 || f.bytes > MATERIAL_LIMITS.file)
127
+ if (!files.length || files.length > 4 || files.some(f => !Number.isSafeInteger(f.bytes) || f.bytes < 0 || f.bytes > MATERIAL_LIMITS.file)
120
128
  || size + resolutionBytes > MATERIAL_LIMITS.set || sets.size >= MATERIAL_LIMITS.sets
121
129
  || [...sets.values()].reduce((n, r) => n + r.size + r.resolutionBytes, size + resolutionBytes) > MATERIAL_LIMITS.total) throw Error('material_resource_limit');
122
130
  row = { request, binding: binding(input), size, created: now(), touched: now(), resolutions: new Map(), resolutionBytes: 0,
123
- files: p.files.map(f => ({ ...f, parts: [], received: 0, material_id: randomUUID(), content: null, digest: null })) };
131
+ files: files.map(f => ({ slot: f.slot, bytes: f.bytes, parts: f.data ? [f.data] : [],
132
+ received: f.data ? f.bytes : 0, material_id: randomUUID(), content: f.content ?? null,
133
+ digest: f.data ? digest(canonicalRunText(f.content)) : null })) };
124
134
  sets.set(input.operation_id, row);
125
135
  }
126
136
  rememberResolution(row, input);
127
- return envelope('accepted', null, { transfer_id: input.operation_id, files: row.files.map(({ slot, bytes, received }) => ({ slot, bytes, received })), limits: MATERIAL_LIMITS }, state);
137
+ return envelope('accepted', null, { transfer_id: input.operation_id, files: row.files.map(({ slot, bytes, received, content, material_id, digest }) => ({ slot, bytes, received,
138
+ ...(content !== null ? { sealed: true, reference: { material_id, digest } } : {}) })), limits: MATERIAL_LIMITS }, state);
128
139
  }
129
140
  row = rowFor(input, p.transfer_id);
130
141
  rememberResolution(row, input);
@@ -195,6 +206,13 @@ export function createRunMaterialStore({ now = Date.now } = {}) {
195
206
  // parts in that same set cannot be used for another current write.
196
207
  for (const [id, row] of sets) if (row.files.some(file => ids.has(file.material_id))) sets.delete(id);
197
208
  },
209
+ // Review facts (companion core SDD-008): the sealed text of one transfer for the runtime's bounded
210
+ // preview, or null when it is unknown or not sealed. It reads only; it is never a material grant.
211
+ sealedFiles(transferId) {
212
+ const row = sets.get(transferId);
213
+ if (closed || !row || row.files.some(file => file.content === null)) return null;
214
+ return row.files.map(({ slot, content, digest: hash }) => Object.freeze({ slot, content, digest: hash }));
215
+ },
198
216
  close() { closed = true; sets.clear(); },
199
217
  inspectOwnership() { expire(); return { sets: sets.size, bytes: [...sets.values()].reduce((n, r) => n + r.size, 0) }; },
200
218
  });
@@ -1,4 +1,4 @@
1
- import { readAcceptanceCriteria } from "../control-evaluation/traceability-readiness.js";
1
+ import { readAcceptanceCriteria, ACCEPTANCE_CRITERIA_FORMAT, TRACEABILITY_CONTRACT } from "../control-evaluation/traceability-readiness.js";
2
2
  import { createHash } from "node:crypto";
3
3
  import { readFileSync } from "../control-read/fs.js";
4
4
  import { basename } from "node:path";
@@ -43,6 +43,14 @@ export function presentationVersionComparison(root, state, gate, currentDigest)
43
43
  const MAX_APPROVAL_ARTEFACT_BYTES = 131072;
44
44
  const MAX_LOCALIZED_SUMMARY_ITEM_CHARS = 1200;
45
45
  const MAX_LOCALIZED_SUMMARY_CHARS = 24000;
46
+ const PRD_LOCALIZED_SUMMARY_FIELDS = Object.freeze([
47
+ { pattern: /^(?:[-*]\s*)?(?:Nutzerziel|User intent|Ziel|Goal)\s*:/iu,
48
+ de: '- Ziel:', en: '- Goal:', reason: 'approval_summary_user_goal_missing' },
49
+ { pattern: /^(?:[-*]\s*)?(?:Umfang|Scope|Produktumfang|Product scope)\s*:/iu,
50
+ de: '- Umfang:', en: '- Scope:', reason: 'approval_summary_scope_missing' },
51
+ { pattern: /^(?:[-*]\s*)?(?:Entscheidungen|Decisions|Geklärte Entscheidungen|Resolved decisions|Offene Entscheidungen|Open decisions)\s*:/iu,
52
+ de: '- Entscheidungen:', en: '- Decisions:', reason: 'approval_summary_decisions_missing', conditional: true },
53
+ ]);
46
54
  const SUMMARY_SECTIONS = Object.freeze({
47
55
  UR: [
48
56
  ["Problem", /problem|need|anlass/iu], ["Ziel", /goal|objective|ziel/iu],
@@ -83,8 +91,13 @@ const SUMMARY_LABELS_EN = Object.freeze({
83
91
  export const hash = (value) => `sha256:${createHash("sha256").update(value).digest("hex")}`;
84
92
 
85
93
  function compactSummaryText(value) {
86
- return value.replace(/\[([^\]]+)\]\([^)]*\)/gu, "$1").replace(/`([^`]+)`/gu, "$1")
87
- .replace(/<[^>]+>/gu, "").replace(/\s+/gu, " ").trim();
94
+ // Markup outside code spans is dropped. A code span naming syntax such as
95
+ // `node app.js <file>` keeps its backticks: unwrapped, Markdown would treat
96
+ // <file> as an HTML tag and the reader would lose it.
97
+ return value.replace(/\[([^\]]+)\]\([^)]*\)/gu, "$1")
98
+ .split(/(`[^`]+`)/u)
99
+ .map(part => /^`[^`]+`$/u.test(part) ? (part.includes("<") ? part : part.slice(1, -1)) : part.replace(/<[^>]+>/gu, ""))
100
+ .join("").replace(/\s+/gu, " ").trim();
88
101
  }
89
102
 
90
103
  function userFacingSummaryText(value) {
@@ -111,14 +124,14 @@ function substantiveMarkdown(markdown) {
111
124
  return markdown.replace(/^## AGDF Approval Summary \([^\r\n]*\)\s*\r?\n[\s\S]*?(?=^#{1,2}\s|(?![\s\S]))/gmu, "");
112
125
  }
113
126
 
114
- function criterionEntries(markdown) {
115
- const parsed = readAcceptanceCriteria(substantiveMarkdown(markdown));
127
+ function criterionEntries(markdown, source) {
128
+ const parsed = readAcceptanceCriteria(substantiveMarkdown(markdown), { source });
116
129
  if (parsed.diagnostics.length) throw Object.assign(new Error("approval_summary_source_criteria_invalid"), { diagnostics: parsed.diagnostics });
117
130
  return parsed.entries;
118
131
  }
119
132
 
120
133
  function criterionDescription(entry) {
121
- for (const field of ["observable_success", "visible_feedback", "expected_effective_state"]) {
134
+ for (const field of ACCEPTANCE_CRITERIA_FORMAT.description_fields) {
122
135
  const value = String(entry.fields[field] ?? "").trim();
123
136
  if (value) return capitalizeSummary(compactSummaryText(value));
124
137
  }
@@ -158,16 +171,35 @@ function validateLocalizedSummaryBlock(gate, block, sourceMarkdown, sourceLangua
158
171
  if (summaryIds.some((id) => !sourceSet.has(id))) throw new Error("approval_summary_criteria_unknown");
159
172
  if (summaryIds.length !== sourceIds.length || sourceIds.some((id) => !summaryIds.includes(id)))
160
173
  throw new Error("approval_summary_criteria_incomplete");
161
- if (!lines.some((line) => /^(?:[-*]\s*)?(?:Nutzerziel|User intent|Ziel|Goal)\s*:/iu.test(line)))
162
- throw new Error("approval_summary_user_goal_missing");
163
- if (!lines.some((line) => /^(?:[-*]\s*)?(?:Umfang|Scope|Produktumfang|Product scope)\s*:/iu.test(line)))
164
- throw new Error("approval_summary_scope_missing");
165
174
  const decisions = approvalDecisionEntries(sourceMarkdown);
166
- if (decisions.length && !lines.some((line) => /^(?:[-*]\s*)?(?:Entscheidungen|Decisions|Geklärte Entscheidungen|Resolved decisions|Offene Entscheidungen|Open decisions)\s*:/iu.test(line)))
167
- throw new Error("approval_summary_decisions_missing");
175
+ for (const field of PRD_LOCALIZED_SUMMARY_FIELDS)
176
+ if ((!field.conditional || decisions.length) && !lines.some(line => field.pattern.test(line))) throw new Error(field.reason);
168
177
  }
169
178
  }
170
179
 
180
+ // Syntax guidance comes from the presentation owner, never inferred product
181
+ // content. An incomplete source remains readable through its authoring route.
182
+ export function approvalSummaryAuthoringFormat(gate, markdown, languages) {
183
+ if (!languages.approval_summary_required || !['UR', 'PRD', 'SD', 'TP', 'QA'].includes(gate)) return null;
184
+ const locale = languages.presentation_language.toLowerCase().startsWith('de') ? 'de' : 'en';
185
+ const format = { heading: languages.approval_summary_heading, body_required: true,
186
+ required_fields: localizedDecisionFields(gate, '', { locale, sourceLanguage: languages.artifact_language }).gaps.map(gap => gap.expected),
187
+ instruction: 'Use the exact heading and a nonempty authored summary in the presentation language. Required fields are separate list declarations with concrete content; a heading or generic criteria paragraph does not replace them. This is syntax guidance, never supplied product content, review or approval.' };
188
+ if (gate === 'PRD') {
189
+ const decisions = approvalDecisionEntries(markdown);
190
+ format.required_fields = PRD_LOCALIZED_SUMMARY_FIELDS.filter(field => !field.conditional || decisions.length).map(field => field[locale]);
191
+ format.conditional_fields = [{ field: PRD_LOCALIZED_SUMMARY_FIELDS.find(field => field.conditional)[locale],
192
+ when: 'Required when the source PRD contains Approval Decisions rows.' }];
193
+ try {
194
+ const entries = criterionEntries(markdown);
195
+ format.criterion_fields = entries.map(entry => `- ${entry.id}:`);
196
+ format.criteria_state = entries.length ? 'available' : 'not_declared';
197
+ } catch { format.criterion_fields = []; format.criteria_state = 'source_invalid'; }
198
+ format.criteria_instruction = 'For every exact criterion_id in the authored PRD, write one separate list item beginning with that ID followed by a colon, exactly once. Retain the source IDs; do not combine them in a generic criteria item. If IDs are not yet available, complete the source criteria first.';
199
+ }
200
+ return format;
201
+ }
202
+
171
203
  function requiredLocalizedSummary(gate, markdown, languages, { path } = {}) {
172
204
  if (!languages.approval_summary_required) return null;
173
205
  const blocks = localizedSummaryBlocks(markdown).filter(block => block.locale === languages.presentation_language);
@@ -186,6 +218,11 @@ function requiredLocalizedSummary(gate, markdown, languages, { path } = {}) {
186
218
  export function evaluateApprovalSummaryReadiness(gate, markdown, languages, { path } = {}) {
187
219
  try {
188
220
  requiredLocalizedSummary(gate, markdown, languages, { path });
221
+ if (gate === "PRD" && markdown.includes(TRACEABILITY_CONTRACT)
222
+ && languages.artifact_language === languages.presentation_language.split("-")[0]) {
223
+ artifactSummary(gate, markdown, { language: languages.presentation_language,
224
+ sourceLanguage: languages.artifact_language, artefactPath: path });
225
+ }
189
226
  return Object.freeze({ ready: true, required_heading: languages.approval_summary_heading, reason: null });
190
227
  } catch (error) {
191
228
  if (!String(error.message).startsWith("approval_summary_")) throw error;
@@ -197,12 +234,25 @@ export function evaluateApprovalSummaryReadiness(gate, markdown, languages, { pa
197
234
  export function renderApprovalSummaryIssue(summary, { path, presentationLanguage }) {
198
235
  const locale = resolvePresentationLocale(interactionLocales, presentationLanguage);
199
236
  const pack = localePack(interactionLocales, locale), german = locale.split("-")[0] === "de";
200
- const expected = {
201
- approval_summary_user_goal_missing: german ? "- Ziel:" : "- Goal:",
202
- approval_summary_scope_missing: german ? "- Umfang:" : "- Scope:",
203
- approval_summary_decisions_missing: german ? "- Entscheidungen:" : "- Decisions:",
204
- approval_summary_field_unreadable: summary.diagnostics?.map(row => row.expected).join("` · `") || undefined,
205
- }[summary.reason] ?? summary.required_heading;
237
+ if (summary.reason === "approval_summary_criterion_content_missing") {
238
+ const fields = ACCEPTANCE_CRITERIA_FORMAT.description_fields.map(field => `\`${field}\``).join(" / ");
239
+ const locations = (summary.diagnostics ?? []).map(row =>
240
+ `- ${row.criterion_id}${row.line ? ` (${german ? 'Zeile' : 'line'} ${row.line})` : ''}`).join("\n");
241
+ return [
242
+ `${german ? 'PRD-Quellkriterien korrigieren' : 'Correct PRD source criteria'}: \`${path}\``,
243
+ `${pack.statusCard.summaryReason}: \`${summary.reason}\``,
244
+ locations,
245
+ german
246
+ ? `Je betroffenem Kriterium mindestens eines dieser Felder einzeln und mit Inhalt angeben: ${fields}. Zusammengesetzte Feldnamen werden nicht aufgeteilt.`
247
+ : `For each affected criterion, provide at least one separate nonempty field: ${fields}. Combined field names are not split.`,
248
+ german
249
+ ? 'Eine zusätzliche AGDF Approval Summary ersetzt diese Quellfelder nicht. Die Kriterien-IDs und den fachlichen Inhalt erhalten; über den bestehenden Autorpfad korrigieren und erneut prüfen.'
250
+ : 'An additional AGDF Approval Summary does not replace these source fields. Preserve criterion IDs and product intent; correct through the existing authoring route and recheck.',
251
+ ].filter(Boolean).join("\n");
252
+ }
253
+ const expected = PRD_LOCALIZED_SUMMARY_FIELDS.find(field => field.reason === summary.reason)?.[german ? 'de' : 'en']
254
+ ?? (summary.reason === 'approval_summary_field_unreadable' ? summary.diagnostics?.map(row => row.expected).join("` · `") || undefined : undefined)
255
+ ?? summary.required_heading;
206
256
  return [
207
257
  `${pack.statusCard.summaryRecovery}: \`${path}\``,
208
258
  `${pack.statusCard.summaryReason}: \`${summary.reason}\``,
@@ -210,7 +260,8 @@ export function renderApprovalSummaryIssue(summary, { path, presentationLanguage
210
260
  ].join("\n");
211
261
  }
212
262
 
213
- function sectionBody(markdown, pattern, contentPattern = null, maxItems = 3) {
263
+ // Pure summary extraction; exported so its text rules are tested directly.
264
+ export function sectionBody(markdown, pattern, contentPattern = null, maxItems = 3) {
214
265
  const lines = markdown.replace(/\r\n?/gu, "\n").split("\n");
215
266
  for (let index = 0; index < lines.length; index += 1) {
216
267
  const heading = lines[index].match(/^(#{2,6})\s+(?:\d+\.\s*)?(.+?)\s*#*\s*$/u);
@@ -233,6 +284,9 @@ function sectionBody(markdown, pattern, contentPattern = null, maxItems = 3) {
233
284
  }
234
285
  if (contentPattern && !contentPattern.test(line)) continue;
235
286
  const cleaned = userFacingSummaryText(line);
287
+ // A lead-in such as "covers exactly these points:" says nothing on its own;
288
+ // the items that follow it carry the content.
289
+ if (/:$/u.test(cleaned)) continue;
236
290
  if (!cleaned || /^(?:what|which|how|who|every field|specify|decision:|status:|gate:|date:|owner:)/iu.test(cleaned)
237
291
  || /^(?:ui_ux_impact|ux_intent_definition|primary_user_intent|success_signal|primary_decision_or_action):/iu.test(cleaned)) continue;
238
292
  body.push(cleaned);
@@ -275,14 +329,15 @@ function artifactSummary(gate, markdown, { runId, revisionId, language, sourceLa
275
329
  if (decision) items.unshift(`- ${german ? 'Entscheidung' : 'Decision'}: ${decision}`);
276
330
  }
277
331
  if (gate === "PRD") {
278
- const criteria = criterionEntries(source);
332
+ const criteria = criterionEntries(source, artefactPath);
279
333
  if (!criteria.length) throw new Error("approval_summary_criteria_missing");
280
334
  if (new Set(criteria.map((entry) => entry.id)).size !== criteria.length) throw new Error("approval_summary_source_criteria_duplicate");
281
- const criterionRows = criteria.map((entry) => {
282
- const description = criterionDescription(entry);
283
- if (!description) throw new Error("approval_summary_criterion_content_missing");
284
- return ` - ${entry.id}: ${description}`;
335
+ const missing = criteria.filter(entry => !criterionDescription(entry));
336
+ if (missing.length) throw Object.assign(new Error("approval_summary_criterion_content_missing"), {
337
+ diagnostics: missing.map(entry => ({ code: 'criterion_description_missing', source: entry.source,
338
+ line: entry.line, criterion_id: entry.id, expected: ACCEPTANCE_CRITERIA_FORMAT.description_fields })),
285
339
  });
340
+ const criterionRows = criteria.map(entry => ` - ${entry.id}: ${criterionDescription(entry)}`);
286
341
  items.push(`- ${german ? "Abnahmekriterien" : "Acceptance criteria"}:\n${criterionRows.join("\n")}`);
287
342
  const decisions = approvalDecisionEntries(source);
288
343
  if (decisions.length) {
@@ -388,16 +443,17 @@ export function renderReviewableApproval(root, report, { runId, gate, revisionId
388
443
  const languageName = pack.statusCard[locale.split("-")[0] === "de" ? "languageGerman" : "languageEnglish"];
389
444
  const artifactLabel = german ? "Artefakt" : "Artefact";
390
445
  const requiredHeading = languages.approval_summary_heading ?? `AGDF Approval Summary (${locale}; source=${sourceLanguage})`;
446
+ const sourceIssue = error.message === 'approval_summary_criterion_content_missing';
391
447
  error.recovery = [
392
- `## ${pack.statusCard.summaryRecovery}`,
448
+ `## ${sourceIssue ? (german ? 'PRD-Quellkriterien korrigieren' : 'Correct PRD source criteria') : pack.statusCard.summaryRecovery}`,
393
449
  `Run: \`${runId}\` · Gate: \`${gate}\` · Revision: \`${revisionId}\``,
394
450
  renderApprovalSummaryIssue({ reason: error.message, required_heading: requiredHeading, diagnostics: error.diagnostics }, {
395
451
  path, presentationLanguage: p.presentation_language,
396
452
  }),
397
- pack.operationalValues.approvalSummaryRecovery.replace("{language}", languageName),
398
- `${german ? "Erforderlicher Abschnitt" : "Required section"}: \`${requiredHeading}\``,
453
+ ...(!sourceIssue ? [pack.operationalValues.approvalSummaryRecovery.replace("{language}", languageName),
454
+ `${german ? "Erforderlicher Abschnitt" : "Required section"}: \`${requiredHeading}\``] : []),
399
455
  `${artifactLabel}: [${basename(path)}](<${path}>)`,
400
- pack.operationalValues.completeMissingApprovalSummary,
456
+ ...(!sourceIssue ? [pack.operationalValues.completeMissingApprovalSummary] : []),
401
457
  ].join("\n\n");
402
458
  }
403
459
  throw error;
@@ -22,7 +22,7 @@ import { join } from "node:path";
22
22
  import { runPath } from "./run-state-reader.js";
23
23
  import { readArtefactBindings } from "./artefact-bindings.js";
24
24
  import { exactApprovedArtefacts, readContainedJson, validateBindingProof } from "./artefact-binding-proof.js";
25
- import { SOURCE_GATES, appendSourceRevision, readSourceRevisions, revisionHistoryPrefix, sourceRevisionImpact, sourceRevisionConfirmation, validExpandedRevisionProof } from "./run-source-revisions.js";
25
+ import { SOURCE_GATES, appendSourceRevision, readSourceRevisions, revisionHistoryPrefix, sourceRevisionImpact, sourceRevisionConfirmation, revisionProofProblems } from "./run-source-revisions.js";
26
26
  import { collectRevisionHistory, byteDigest, inspectRevisionHistory, readHistoryBytes } from "./run-revision-history.js";
27
27
  import { commitRunStepLocked, recoverPendingRunStepLocked, pendingRunStepConflictIds, readPendingRunStepJournal, readPendingSourceRevisionJournal } from "./run-step-transaction.js";
28
28
  import { pendingRunStepPath } from "./run-step-pending.js";
@@ -139,27 +139,45 @@ function prepareSourceRevision(root, input) {
139
139
  || boundary.approved.includes(row.type) && state.approvals.get(row.type)?.status !== "approved")) throw revisionError("approved_source_invalid");
140
140
  if (input.commandContext && input.proposalBinding?.path !== evidence) throw revisionError('revision_source_binding_invalid');
141
141
  const boundProposal = input.proposalMaterial ? { validate: () => {} } : input.proposalBinding ? readOperationSource(root, runId, input.proposalBinding) : null;
142
- if (!exactObject(proposal, REVISION_PROPOSAL_FIELDS)
143
- || proposal.target_id !== resolveControlCommandTarget(root).target_id
144
- || proposal.run_id !== runId || proposal.expected_revision_id !== revisionId || proposal.operation_id !== operationId
145
- || proposal.source_gate !== sourceGate || !nonempty(proposal.reason) || !nonempty(proposal.intended_change)
146
- || canonicalJson(proposal.sources) !== canonicalJson(sources)) throw revisionError("revision_proposal_invalid");
142
+ // Name each violated field with its expected binding: these are facts the caller
143
+ // may read anyway, and the proposal can only be corrected when they are known.
144
+ const expected = { target_id: resolveControlCommandTarget(root).target_id, run_id: runId, expected_revision_id: revisionId,
145
+ operation_id: operationId, source_gate: sourceGate, sources };
146
+ const problems = [
147
+ ...(exactObject(proposal, REVISION_PROPOSAL_FIELDS) ? [] : [`fields must be exactly ${REVISION_PROPOSAL_FIELDS.join(", ")}`]),
148
+ ...Object.entries(expected).filter(([key, value]) => canonicalJson(proposal[key] ?? null) !== canonicalJson(value))
149
+ .map(([key, value]) => `${key} must be ${canonicalJson(value)}`),
150
+ ...["reason", "intended_change"].filter(key => !nonempty(proposal[key])).map(key => `${key} must be nonempty text`),
151
+ ];
152
+ if (problems.length) throw revisionError("revision_proposal_invalid",
153
+ `Correct the staged proposal and stage it again; operation_id is the UUID of this source_revision command. ${problems.join("; ")}.`);
147
154
  const assessment = proposal.impact_assessment, index = SOURCE_GATES.indexOf(sourceGate);
148
- if (!exactObject(assessment, REVISION_IMPACT_FIELDS)
149
- || !nonempty(assessment.reviewer) || assessment.earliest_source !== sourceGate || !nonempty(assessment.implementation_evidence)
150
- || !Array.isArray(assessment.unresolved) || assessment.unresolved.length
151
- || !Array.isArray(assessment.upstream) || assessment.upstream.length !== index
152
- || !assessment.upstream.every((row, i) => exactObject(row, REVISION_UPSTREAM_FIELDS)
153
- && nonempty(row.rationale) && canonicalJson({ type: row.type, path: row.path, digest: row.digest }) === canonicalJson(sources[i]))) throw revisionError("source_impact_unresolved", "Resolve the earliest changed source and unchanged upstream rationale with its owner; then regenerate the proposal.");
154
- const analyticalTypes = ["Brownfield Review", "UX Intent Definition"].filter(type => state.artefacts.get(type)?.path);
155
- if (!Array.isArray(assessment.analyses) || assessment.analyses.length !== analyticalTypes.length
156
- || !assessment.analyses.every((row, i) => exactObject(row, REVISION_ANALYSIS_FIELDS)
157
- && row.type === analyticalTypes[i] && row.path === state.artefacts.get(row.type).path
158
- && row.digest === artefactFileDigest(root, row.path) && nonempty(row.reason)
159
- && ["retain", "reassess"].includes(row.disposition) && (sourceGate !== "UR" || row.disposition === "reassess"))) throw revisionError("analysis_applicability_unresolved");
155
+ // Like the binding check above, each rejection names the expected entries.
156
+ const identity = row => canonicalJson({ type: row?.type, path: row?.path, digest: row?.digest });
157
+ const upstream = sources.slice(0, index);
158
+ const impactProblems = [
159
+ ...(exactObject(assessment, REVISION_IMPACT_FIELDS) ? [] : [`impact_assessment fields must be exactly ${REVISION_IMPACT_FIELDS.join(", ")}`]),
160
+ ...(nonempty(assessment?.reviewer) ? [] : ["impact_assessment.reviewer must be nonempty text"]),
161
+ ...(assessment?.earliest_source === sourceGate ? [] : [`impact_assessment.earliest_source must be "${sourceGate}"`]),
162
+ ...(nonempty(assessment?.implementation_evidence) ? [] : ["impact_assessment.implementation_evidence must be nonempty text"]),
163
+ ...(Array.isArray(assessment?.unresolved) && !assessment.unresolved.length ? [] : ["impact_assessment.unresolved must be []"]),
164
+ ...(Array.isArray(assessment?.upstream) && assessment.upstream.length === upstream.length
165
+ && assessment.upstream.every((row, i) => exactObject(row, REVISION_UPSTREAM_FIELDS) && nonempty(row.rationale) && identity(row) === identity(upstream[i]))
166
+ ? [] : [`impact_assessment.upstream must list exactly ${canonicalJson(upstream)} in order, each with a nonempty rationale`]),
167
+ ];
168
+ if (impactProblems.length) throw revisionError("source_impact_unresolved",
169
+ `Resolve the earliest changed source and unchanged upstream rationale with its owner; then regenerate the proposal. ${impactProblems.join("; ")}.`);
170
+ const analyses = ["Brownfield Review", "UX Intent Definition"].filter(type => state.artefacts.get(type)?.path)
171
+ .map(type => ({ type, path: state.artefacts.get(type).path, digest: artefactFileDigest(root, state.artefacts.get(type).path) }));
172
+ const dispositions = sourceGate === "UR" ? ["reassess"] : ["retain", "reassess"];
173
+ if (!Array.isArray(assessment.analyses) || assessment.analyses.length !== analyses.length
174
+ || !assessment.analyses.every((row, i) => exactObject(row, REVISION_ANALYSIS_FIELDS) && identity(row) === identity(analyses[i])
175
+ && nonempty(row.reason) && dispositions.includes(row.disposition)))
176
+ throw revisionError("analysis_applicability_unresolved", `Set impact_assessment.analyses to exactly the registered analyses ${canonicalJson(analyses)} in order, each with a nonempty reason and disposition ${dispositions.join(" or ")}; unregistered analyses are not listed.`);
160
177
  const readArtefact = path => { try { return readHistoryBytes(root, path).toString('utf8'); } catch { return undefined; } };
161
- if (!validExpandedRevisionProof(proposal, state, readArtefact, boundary.initiating_phase))
162
- throw revisionError('revision_impact_proof_invalid', 'Bind concrete changed statements, earliest source, actual phase and every exact quality record; unknown or affected associations require reassessment.');
178
+ const proofProblems = revisionProofProblems(proposal, state, readArtefact, boundary.initiating_phase);
179
+ if (proofProblems.length) throw revisionError('revision_impact_proof_invalid',
180
+ `Bind concrete changed statements, earliest source, actual phase and every exact quality record; unknown or affected associations require reassessment. ${proofProblems.slice(0, 5).join('; ')}.`);
163
181
  const affected = SOURCE_GATES.slice(index);
164
182
  const qualityPaths = proposal.quality_assessment.map(row => row.record.path).filter(Boolean);
165
183
  const history = collectRunRevisionClosure(root, run, state, operationId, boundary.approved, [evidence, ...qualityPaths], { proposalMaterial: input.proposalMaterial });
@@ -50,7 +50,12 @@ export function sourceRevisionQualityFacts(state, readArtefact) {
50
50
  return facts;
51
51
  }
52
52
 
53
- export function validExpandedRevisionProof(proposal, state, readArtefact, phase) {
53
+ export const validExpandedRevisionProof = (...args) => revisionProofProblems(...args).length === 0;
54
+
55
+ // The same rules as a list of named violations, so a rejected proposal can be
56
+ // corrected deliberately instead of by trial. Later checks rely on the structure
57
+ // confirmed by earlier ones, so each block stops at its own violations.
58
+ export function revisionProofProblems(proposal, state, readArtefact, phase) {
54
59
  const changed = proposal.changed_statements, affected = SOURCE_GATES.slice(SOURCE_GATES.indexOf(proposal.source_gate));
55
60
  const nonempty = value => typeof value === 'string' && value.trim() && value.length <= 4096;
56
61
  const assessmentProof = proposal.impact_assessment, index = SOURCE_GATES.indexOf(proposal.source_gate);
@@ -64,25 +69,40 @@ export function validExpandedRevisionProof(proposal, state, readArtefact, phase)
64
69
  || !Array.isArray(assessmentProof.analyses) || assessmentProof.analyses.length !== analyticalTypes.length
65
70
  || !assessmentProof.analyses.every((row, i) => exactObject(row, REVISION_ANALYSIS_FIELDS) && row.type === analyticalTypes[i]
66
71
  && row.path === state.artefacts.get(row.type).path && hashes(row.digest) && nonempty(row.reason)
67
- && ['retain', 'reassess'].includes(row.disposition) && (proposal.source_gate !== 'UR' || row.disposition === 'reassess'))) return false;
68
- if (proposal.policy_version !== 2 || proposal.initiating_phase !== phase || !Array.isArray(changed) || !changed.length
69
- || !changed.every(row => exactObject(row, REVISION_STATEMENT_FIELDS) && affected.includes(row.type)
70
- && proposal.sources.some(source => source.type === row.type && source.path === row.path && source.digest === row.digest)
71
- && [row.statement, row.intended_change, row.reason].every(nonempty)
72
- && String(readArtefact(row.path)).replace(/\r\n?/gu, '\n').includes(row.statement))
73
- || !changed.some(row => row.type === proposal.source_gate)) return false;
72
+ && ['retain', 'reassess'].includes(row.disposition) && (proposal.source_gate !== 'UR' || row.disposition === 'reassess')))
73
+ return ['impact_assessment is invalid'];
74
+ const statementProblem = (row, i) => {
75
+ if (!exactObject(row, REVISION_STATEMENT_FIELDS)) return `changed_statements[${i}] fields must be exactly ${REVISION_STATEMENT_FIELDS.join(', ')}`;
76
+ if (!affected.includes(row.type)) return `changed_statements[${i}].type must be one of ${affected.join(', ')}`;
77
+ if (!proposal.sources.some(source => source.type === row.type && source.path === row.path && source.digest === row.digest))
78
+ return `changed_statements[${i}] path and digest must equal its entry in sources`;
79
+ if (![row.statement, row.intended_change, row.reason].every(nonempty)) return `changed_statements[${i}] statement, intended_change and reason must be nonempty text`;
80
+ if (!String(readArtefact(row.path)).replace(/\r\n?/gu, '\n').includes(row.statement))
81
+ return `changed_statements[${i}].statement must be an exact excerpt of ${row.path}`;
82
+ return null;
83
+ };
84
+ const changeProblems = [
85
+ ...(proposal.policy_version === 2 ? [] : ['policy_version must be 2']),
86
+ ...(proposal.initiating_phase === phase ? [] : [`initiating_phase must be "${phase}"`]),
87
+ ...(Array.isArray(changed) && changed.length ? changed.map(statementProblem).filter(Boolean) : ['changed_statements must be a nonempty list']),
88
+ ...(Array.isArray(changed) && changed.some(row => row?.type === proposal.source_gate) ? [] : [`changed_statements must include a ${proposal.source_gate} statement`]),
89
+ ];
90
+ if (changeProblems.length) return changeProblems;
74
91
  const facts = sourceRevisionQualityFacts(state, readArtefact), assessment = proposal.quality_assessment;
75
- if (!Array.isArray(assessment) || !assessment.length || assessment.length !== facts.length) return false;
76
- return assessment.every((row, i) => {
77
- if (!exactObject(row, REVISION_QUALITY_FIELDS) || !['retain', 'reassess'].includes(row.disposition) || !nonempty(row.reason)) return false;
92
+ if (!Array.isArray(assessment) || !assessment.length || assessment.length !== facts.length)
93
+ return [`quality_assessment must contain exactly ${facts.length} rows, one per quality record in order`];
94
+ return assessment.map((row, i) => {
95
+ if (!exactObject(row, REVISION_QUALITY_FIELDS) || !['retain', 'reassess'].includes(row.disposition) || !nonempty(row.reason))
96
+ return `quality_assessment[${i}] needs exactly ${REVISION_QUALITY_FIELDS.join(', ')} with disposition retain or reassess and a nonempty reason`;
78
97
  const { disposition, reason, ...proof } = row;
79
- if (canonicalJson(proof) !== canonicalJson(facts[i])) return false;
98
+ if (canonicalJson(proof) !== canonicalJson(facts[i])) return `quality_assessment[${i}] must copy quality record ${canonicalJson(facts[i])} unchanged`;
80
99
  const analysis = proposal.impact_assessment.analyses.find(item => item.type === row.record.type);
81
- if (analysis && (analysis.disposition !== disposition || analysis.path !== row.record.path || analysis.digest !== row.record.digest)) return false;
82
- return disposition !== 'retain' || row.association === 'current'
83
- && !row.sources.some(source => affected.includes(source.type))
84
- && !(proposal.source_gate === 'UR' && analysis);
85
- });
100
+ if (analysis && (analysis.disposition !== disposition || analysis.path !== row.record.path || analysis.digest !== row.record.digest))
101
+ return `quality_assessment[${i}].disposition must equal the ${row.record.type} analysis disposition`;
102
+ if (disposition === 'retain' && !(row.association === 'current' && !row.sources.some(source => affected.includes(source.type))
103
+ && !(proposal.source_gate === 'UR' && analysis))) return `quality_assessment[${i}] must be reassess: its record is not current or depends on a changed source`;
104
+ return null;
105
+ }).filter(Boolean);
86
106
  }
87
107
 
88
108
  export function sourceRevisionImpact(proposal, activeBindings) {
@@ -41,6 +41,15 @@ export function readSelectedRun(root, id) {
41
41
  last_updated_at: parsed.meta.updated_at || stateStats.mtime.toISOString() };
42
42
  }
43
43
 
44
+ // The entry names discovery treats as runs: every non-dot entry of the runs folder, sorted. Empty when the
45
+ // folder is missing or no directory. It reads no layout, so the companion run view also counts the run
46
+ // folders of a store whose layout is not supported (PRD AC-002, TP T-004).
47
+ export function runFolderEntries(root) {
48
+ const dir = join(root, ".agdf", "control", "runs");
49
+ if (!existsSync(dir) || lstatKindSync(dir) !== 'directory') return [];
50
+ return readdirSync(dir).filter((entry) => !entry.startsWith(".")).sort();
51
+ }
52
+
44
53
  function discoverCapturedRuns(root) {
45
54
  const dir = join(root, ".agdf", "control", "runs");
46
55
  if (!existsSync(dir)) return [];
@@ -48,9 +57,7 @@ function discoverCapturedRuns(root) {
48
57
  if (lstatKindSync(dir) !== 'directory') {
49
58
  return [invalidRun("<repository>", dir)];
50
59
  }
51
- return readdirSync(dir)
52
- .filter((entry) => !entry.startsWith("."))
53
- .sort()
60
+ return runFolderEntries(root)
54
61
  .map((id) => {
55
62
  const directory = join(dir, id);
56
63
  const path = join(directory, "RUN_STATE.md");
@@ -1,6 +1,6 @@
1
1
  import { extractField } from '../control-evaluation/verified-change.js';
2
2
  import { transitionDecisionForRunState } from '../control-evaluation/gate-policy.js';
3
- import { runDocumentPath, runEvidencePath, isRunDataPath } from './run-identity.js';
3
+ import { runDocumentPath, runEvidencePath, isRunDataPath, RUN_STEP_SLOTS } from './run-identity.js';
4
4
  import { resolutionRecordVersion } from './input-resolution.js';
5
5
  import { resolveControlCommandTarget } from './approval-command-contract.js';
6
6
 
@@ -48,7 +48,7 @@ import { withRunMutationLock, writeRunMutationLocked } from './run-mutation.js';
48
48
  import { APPROVAL_GATES as DECISION_GATES, REVISION_ID_PATTERN } from './run-identity.js';
49
49
  import { runSealState, artefactFileDigest } from './run-seal.js';
50
50
  import { inspectUxIntentDecision } from '../control-evaluation/definition-sources.js';
51
- import { readReportDecision } from '../control-evaluation/report-decision.js';
51
+ import { readReportDecision, REPORT_DECISION_FORMAT } from '../control-evaluation/report-decision.js';
52
52
  import { prepareVerifiedChangeRecording } from './run-verified-change.js';
53
53
 
54
54
  // Record one typed transition and its owned files in a single sealed revision.
@@ -77,6 +77,8 @@ function readRunArtefact(root, runId, rawPath, limit = 131072) {
77
77
  // Review reports are read within the bound of the closeout quality inspection (inspectQaReports).
78
78
  const REVIEW_REPORT_LIMIT = 262144;
79
79
  // Every recording of a review or QA report applies the closeout quality rules; at most 64 diagnostics are returned.
80
+ // The staged report must state the result where the parser reads it.
81
+ const unconfirmedDecision = runId => rejected(runId, 'internal_result_unconfirmed', { recovery: REPORT_DECISION_FORMAT.instruction });
80
82
  const findingsRefusal = (runId, diagnostics) => rejected(runId, 'review_findings_invalid',
81
83
  { diagnostics: diagnostics.slice(0, 64), ...(diagnostics.length > 64 ? { diagnostics_truncated: true } : {}) });
82
84
  function qaReportDiagnostics(root, runId, qa, control, proposedContent) {
@@ -132,7 +134,7 @@ function recordRunStepLocked(root, input, { policy, date, afterWrite, operationC
132
134
  stagedSource = resolveMaterial?.(input.sourceBinding);
133
135
  const slot = input.step === 'internal' ? input.internalStep : input.step === 'verified_change' ? 'Verified Change'
134
136
  : input.step === 'closeout' && stagedSource?.slot === 'Verified Change' ? 'Verified Change'
135
- : ({ ur: 'UR', route: 'Brownfield Review', review: 'CR', evidence: 'EVIDENCE', closeout: 'OR' })[input.step];
137
+ : RUN_STEP_SLOTS[input.step];
136
138
  if (!stagedSource || stagedSource.slot !== slot) return rejected(runId, 'material_slot_mismatch');
137
139
  const file = slot === 'EVIDENCE' ? `EVIDENCE-${stagedSource.digest.slice(7)}.md`
138
140
  : ({ UR: 'UR.md', OR: 'OR.md', 'Verified Change': 'VERIFIED_CHANGE.md', ...INTERNAL_STEP_FILES })[slot];
@@ -178,7 +180,7 @@ function recordRunStepLocked(root, input, { policy, date, afterWrite, operationC
178
180
  if (step === "review") {
179
181
  if (!['CR', 'Quick Task Execution'].includes(before.current_gate)
180
182
  || !['pass', 'revise', 'block'].includes(input.decision)) return rejected(runId, "review_step_not_ready");
181
- if (readReportDecision(operationSource.content) !== input.decision) return rejected(runId, "internal_result_unconfirmed");
183
+ if (readReportDecision(operationSource.content) !== input.decision) return unconfirmedDecision(runId);
182
184
  const diagnostics = reviewFindingDiagnostics('CR', operationSource.content, { decision: input.decision });
183
185
  if (diagnostics.length) return findingsRefusal(runId, diagnostics);
184
186
  }
@@ -218,7 +220,7 @@ function recordRunStepLocked(root, input, { policy, date, afterWrite, operationC
218
220
  if (!checked.well_formed || !checked.supported || checked.decision !== input.decision
219
221
  || input.decision === 'not_applicable' && (control.source_revision_ux_required || before.next_operation?.analysis === input.internalStep)) return rejected(runId, 'internal_result_unconfirmed');
220
222
  } else {
221
- if (readReportDecision(operationSource.content, { allowNotApplicable: true }) !== input.decision) return rejected(runId, "internal_result_unconfirmed");
223
+ if (readReportDecision(operationSource.content, { allowNotApplicable: true }) !== input.decision) return unconfirmedDecision(runId);
222
224
  }
223
225
  if (CORRECTABLE_REVIEWS.includes(input.internalStep)) {
224
226
  const diagnostics = reviewFindingDiagnostics(input.internalStep, operationSource.content, { decision: input.decision });
@@ -306,6 +308,7 @@ function recordRunStepLocked(root, input, { policy, date, afterWrite, operationC
306
308
  // The quality obligation still names the decisive finding and, where available, its correction path.
307
309
  const qa = readiness.findings.includes('closeout_quality_unconfirmed') ? inspectQaReports(root, control, runId, 'pass', 64, true) : null;
308
310
  return rejected(runId, 'closeout_obligations_unfulfilled', { findings: readiness.findings,
311
+ ...(readiness.expected ? { expected: readiness.expected } : {}),
309
312
  ...(qa?.reason ? { reason_detail: closeoutQualityDetail(qa, control) } : {}) });
310
313
  }
311
314
  if (!exactApprovedArtefacts(root, control)) return rejected(runId, 'approved_source_proof_invalid');
@@ -4,12 +4,14 @@ import { createDeliveryContinuationService } from "./delivery-continuation/servi
4
4
  import { createControlInspectService } from "./control-inspect/service.js";
5
5
  import { createRunCommandService } from "./control-state/run-command-service.js";
6
6
  import { createRunMaterialStore } from './control-state/run-material.js';
7
+ import { withMaterialRecordingCalls } from './control-inspect/material-recording-calls.js';
7
8
  import { evaluateGateCheck } from './control-evaluation/gate-check.js';
8
9
  import { evaluateDoctor } from './control-evaluation/doctor.js';
9
10
  export { createResourceContext, resources } from "./resources/context.js";
10
11
  export function createCoreServices({ resources: context = resources, observers = {}, runtimeBinding } = {}) {
11
12
  const contracts = { runtimeContractModules: () => runtimeContractModules(context.pluginDefinition), readRuntimeContract: module => readRuntimeContract(module, { context }), readSkillRuntimeContracts: skill => readSkillRuntimeContracts(skill, { context }) };
12
- const material = createRunMaterialStore();
13
+ const store = createRunMaterialStore();
14
+ const material = Object.freeze({ ...store, stage: input => withMaterialRecordingCalls(input, store.stage(input)) });
13
15
  const reads = observers.gitObservation ? { ...observers,
14
16
  evaluateGateCheck: observers.evaluateGateCheck ?? ((root, selection) => evaluateGateCheck(root, selection, observers.gitObservation)),
15
17
  evaluateDoctor: observers.evaluateDoctor ?? ((root, selection) => evaluateDoctor(root, selection, observers.gitObservation)),
@@ -1,5 +1,6 @@
1
1
  import { INSTALL_SETUP_STATES, INSTALL_ACTIONS, INSTALL_FAILURE_PHASES, INSTALL_FAILURE_CODES,
2
- DISPATCH_RECOVERY_CODES, CODEX_HOOK_STATES, DECISION_CARD_LABEL_KEYS, FLOW_EVIDENCE_LABEL_KEYS } from "./interaction-catalog.js";
2
+ DISPATCH_RECOVERY_CODES, CODEX_HOOK_STATES, DECISION_CARD_LABEL_KEYS, FLOW_EVIDENCE_LABEL_KEYS,
3
+ COMPANION_MOODS, COMPANION_UNREADABLE_REASONS } from "./interaction-catalog.js";
3
4
 
4
5
  // Build-time validation, not a second vocabulary. Unknown and missing copy both fail closed.
5
6
  export function validateInteractionCatalog(registry) {
@@ -18,6 +19,10 @@ export function validateInteractionCatalog(registry) {
18
19
  check(`${language}.skillDispatch.recoveries`, pack.skillDispatch?.recoveries, DISPATCH_RECOVERY_CODES);
19
20
  check(`${language}.decisionCard`, pack.decisionCard, DECISION_CARD_LABEL_KEYS);
20
21
  check(`${language}.flowEvidence`, pack.flowEvidence, FLOW_EVIDENCE_LABEL_KEYS);
22
+ check(`${language}.companion.states`, pack.companion?.states, COMPANION_MOODS);
23
+ check(`${language}.companion.statusStates`, pack.companion?.statusStates, [...COMPANION_MOODS, "unreadable"]);
24
+ const unreadable = pack.companion?.unreadable;
25
+ check(`${language}.companion.unreadable`, unreadable && Object.fromEntries(COMPANION_UNREADABLE_REASONS.map(key => [key, unreadable[key]])), COMPANION_UNREADABLE_REASONS);
21
26
  }
22
27
  for (const row of Object.values(CODEX_HOOK_STATES)) {
23
28
  if (!INSTALL_SETUP_STATES[row.state]?.actions.includes(row.action)) throw new Error("AGDF_INTERACTION_CATALOG_HOOK_INVALID");
@@ -96,3 +96,11 @@ export const DECISION_CARD_COPY_BUDGETS = freeze(Object.fromEntries(DECISION_CAR
96
96
  'previewUnavailable', 'previewChanged', 'previewIllustrative', 'previewUnsupported'].includes(key)
97
97
  ? 'description' : 'label'
98
98
  ])));
99
+
100
+ // Companion figure moods (SD SDD-004) and the per-run reasons it names; the catalog's companion
101
+ // section must carry copy for exactly these keys in every locale.
102
+ export const COMPANION_MOODS = freeze(['laeuft', 'baut', 'wartet', 'blockiert', 'fertig', 'bereit']);
103
+ // The store reasons are every bounded code `boundedCode` can return (CR finding B9).
104
+ export const COMPANION_UNREADABLE_REASONS = freeze(['run_version_unsupported', 'run_state_invalid', 'too_large', 'missing',
105
+ 'control_layout_unsupported', 'control_invalid', 'read_failed', 'resource_denied', 'source_changed', 'timeout', 'resource_limit',
106
+ 'unavailable', 'exit', 'malformed']);