create-agdf 0.30.0 → 0.32.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 (366) 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 +436 -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 +119 -0
  20. package/generated/plugins/agdf/companion/figure.mjs +242 -111
  21. package/generated/plugins/agdf/companion/gate-bar.mjs +10 -28
  22. package/generated/plugins/agdf/companion/localization.mjs +15 -32
  23. package/generated/plugins/agdf/companion/notice.mjs +20 -10
  24. package/generated/plugins/agdf/companion/register.mjs +310 -66
  25. package/generated/plugins/agdf/companion/run-state.mjs +89 -90
  26. package/generated/plugins/agdf/companion/state.mjs +6 -32
  27. package/generated/plugins/agdf/companion/view.mjs +199 -24
  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 +14 -3
  36. package/generated/plugins/agdf/mcp/server/src/generation.js +8 -2
  37. package/generated/plugins/agdf/mcp/server/src/main.js +5 -1
  38. package/generated/plugins/agdf/mcp/server/src/next-action.js +227 -0
  39. package/generated/plugins/agdf/mcp/server/src/prepared-call-transport.js +306 -0
  40. package/generated/plugins/agdf/mcp/server/src/safe-read-continuation.js +62 -0
  41. package/generated/plugins/agdf/mcp/server/src/server.js +155 -37
  42. package/generated/plugins/agdf/mcp/server/ui/cockpit.html +130 -33
  43. package/generated/plugins/agdf/mcp/server/ui/decision-manifest.json +3 -3
  44. package/generated/plugins/agdf/mcp/server/ui/decision.html +3 -3
  45. package/generated/plugins/agdf/mcp/server/ui/manifest.json +3 -3
  46. package/generated/plugins/agdf/meta/agdf-interaction-locales.json +436 -2
  47. package/generated/plugins/agdf/meta/agdf-mcp-capability.json +1 -1
  48. package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  49. package/generated/plugins/agdf/meta/contracts/gate-artifact-preparation.md +9 -3
  50. package/generated/plugins/agdf/meta/contracts/interaction.md +10 -4
  51. package/generated/plugins/agdf/meta/contracts/modes.md +5 -0
  52. package/generated/plugins/agdf/meta/contracts/prd-definition.md +40 -1
  53. package/generated/plugins/agdf/meta/contracts/quality.md +35 -0
  54. package/generated/plugins/agdf/meta/contracts/request-activation.md +6 -3
  55. package/generated/plugins/agdf/meta/contracts/sd-definition.md +46 -0
  56. package/generated/plugins/agdf/meta/contracts/tp-definition.md +8 -0
  57. package/generated/plugins/agdf/meta/contracts/ur-definition.md +9 -5
  58. package/generated/plugins/agdf/meta/copilot-payload-baseline.json +3 -3
  59. package/generated/plugins/agdf/meta/distribution-profile-history.json +14 -0
  60. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +436 -2
  61. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  62. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/gate-artifact-preparation.md +9 -3
  63. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/interaction.md +10 -4
  64. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/modes.md +5 -0
  65. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/prd-definition.md +40 -1
  66. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/quality.md +35 -0
  67. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/request-activation.md +6 -3
  68. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/sd-definition.md +46 -0
  69. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/tp-definition.md +8 -0
  70. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/ur-definition.md +9 -5
  71. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/command-registry.js +2 -1
  72. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/parse-args.js +7 -4
  73. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/project-read-command.js +29 -0
  74. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/validation-handlers.js +2 -1
  75. package/generated/plugins/agdf/runtime/create-agdf/lib/mcp-dispatch-runtime.js +36 -7
  76. package/generated/plugins/agdf/runtime/create-agdf/lib/mcp-lifecycle/package.js +7 -0
  77. package/generated/plugins/agdf/runtime/create-agdf/lib/mcp-lifecycle/plugin-runtime.js +34 -6
  78. package/generated/plugins/agdf/runtime/create-agdf/lib/mcp-lifecycle/prepared-call-journal.js +269 -0
  79. package/generated/plugins/agdf/runtime/create-agdf/lib/mcp-worker-runtime.js +86 -83
  80. package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/validator-application.js +2 -2
  81. package/generated/plugins/agdf/runtime/create-agdf/package.json +1 -1
  82. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/events.js +157 -0
  83. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/figure-state.js +38 -0
  84. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/journal.js +186 -0
  85. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/companion/run-view.js +227 -0
  86. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/acceptance-criteria.js +2 -0
  87. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/closeout-readiness.js +24 -6
  88. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/decision-content-placeholder.js +4 -1
  89. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-check.js +49 -7
  90. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-policy-inputs.js +2 -0
  91. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-policy-pure.js +7 -3
  92. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/prd-readiness.js +7 -2
  93. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/report-decision.js +9 -0
  94. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/traceability-readiness.js +57 -8
  95. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/ur-readiness.js +15 -2
  96. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/artifact-readiness.js +5 -3
  97. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/authoring-inputs.js +107 -22
  98. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/cockpit-list.js +10 -8
  99. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/cockpit-session.js +11 -3
  100. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/cockpit-work.js +4 -2
  101. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/cockpit.js +47 -33
  102. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/contract.js +64 -24
  103. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/material-recording-calls.js +54 -0
  104. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/project-state.js +2 -1
  105. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/run-classification.js +31 -0
  106. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/selection.js +6 -0
  107. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/service.js +32 -5
  108. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/work-situation.js +25 -0
  109. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-read/cockpit-worker.js +1 -0
  110. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/authoring-text-handoff.js +3 -0
  111. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/input-resolution.js +7 -5
  112. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/material-limits.js +4 -0
  113. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/prepared-control-calls.js +137 -0
  114. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/presentation-delivery.js +4 -1
  115. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-command-contract.js +10 -4
  116. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-command-schema.js +9 -3
  117. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-command-service.js +35 -11
  118. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-create-command.js +6 -1
  119. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-identity.js +2 -0
  120. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-inventory-reader.js +54 -5
  121. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-inventory.js +12 -27
  122. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-material.js +25 -7
  123. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-presentation-render.js +113 -28
  124. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-revision.js +38 -20
  125. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-source-revisions.js +37 -17
  126. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-state-reader.js +10 -3
  127. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-steps.js +8 -5
  128. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-title.js +43 -0
  129. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/index.js +3 -1
  130. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/interaction-catalog-validation.js +7 -1
  131. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/interaction-catalog.js +8 -0
  132. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/interaction-presentation.js +31 -10
  133. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/mcp-input-hints.js +30 -1
  134. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/mcp-tool-errors.js +5 -3
  135. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/resources/canonical-init-plan.js +1 -1
  136. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/runtime/plugin-provenance.js +9 -2
  137. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/contract.js +6 -4
  138. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/delivery-intake.js +3 -1
  139. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/mcp-continuation.js +25 -2
  140. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/prd-definition.js +3 -2
  141. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/readiness-findings.js +7 -0
  142. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/sd-definition.js +4 -2
  143. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/service.js +17 -4
  144. package/generated/plugins/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/tp-definition.js +4 -2
  145. package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
  146. package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +4 -3
  147. package/generated/plugins/agdf/skills/ux-intent-definition/SKILL.md +31 -2
  148. package/generated/plugins/agdf/skills/ux-intent-definition/help.md +8 -1
  149. package/generated/plugins/agdf/submission/openai/availability.md +1 -1
  150. package/generated/plugins/agdf/submission/openai/capability-matrix.json +1 -1
  151. package/generated/plugins/agdf/submission/openai/release-notes.md +5 -8
  152. package/generated/plugins/agdf/submission/openai/reviewer-cases.json +1 -1
  153. package/generated/plugins/agdf-planning/.codex-plugin/plugin.json +1 -1
  154. package/generated/plugins/agdf-planning/plugin.json +1 -1
  155. package/generated/plugins/copilot/agdf/.agdf-payload-inventory.json +358 -203
  156. package/generated/plugins/copilot/agdf/control/README.md +5 -0
  157. package/generated/plugins/copilot/agdf/control/templates/BRAND.md +28 -0
  158. package/generated/plugins/copilot/agdf/control/templates/artefacts/PRD.md +11 -0
  159. package/generated/plugins/copilot/agdf/control/templates/artefacts/SD.md +4 -0
  160. package/generated/plugins/copilot/agdf/control/templates/artefacts/UX_INTENT_DEFINITION.md +1 -1
  161. package/generated/plugins/copilot/agdf/mcp/server/README.md +26 -2
  162. package/generated/plugins/copilot/agdf/mcp/server/package.json +3 -3
  163. package/generated/plugins/copilot/agdf/mcp/server/src/generation-runtime.js +14 -3
  164. package/generated/plugins/copilot/agdf/mcp/server/src/generation.js +8 -2
  165. package/generated/plugins/copilot/agdf/mcp/server/src/main.js +5 -1
  166. package/generated/plugins/copilot/agdf/mcp/server/src/next-action.js +227 -0
  167. package/generated/plugins/copilot/agdf/mcp/server/src/prepared-call-transport.js +306 -0
  168. package/generated/plugins/copilot/agdf/mcp/server/src/safe-read-continuation.js +62 -0
  169. package/generated/plugins/copilot/agdf/mcp/server/src/server.js +155 -37
  170. package/generated/plugins/copilot/agdf/meta/agdf-plugin.definition.json +1 -1
  171. package/generated/plugins/copilot/agdf/meta/distribution-profile-history.json +14 -0
  172. package/generated/plugins/copilot/agdf/plugin.json +1 -1
  173. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +436 -2
  174. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  175. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/gate-artifact-preparation.md +9 -3
  176. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/interaction.md +10 -4
  177. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/modes.md +5 -0
  178. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/prd-definition.md +40 -1
  179. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/quality.md +35 -0
  180. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/request-activation.md +6 -3
  181. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/sd-definition.md +46 -0
  182. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/tp-definition.md +8 -0
  183. package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/contracts/ur-definition.md +9 -5
  184. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/command-registry.js +2 -1
  185. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/parse-args.js +7 -4
  186. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/project-read-command.js +29 -0
  187. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/validation-handlers.js +2 -1
  188. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/mcp-dispatch-runtime.js +36 -7
  189. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/mcp-lifecycle/package.js +7 -0
  190. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/mcp-lifecycle/plugin-runtime.js +34 -6
  191. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/mcp-lifecycle/prepared-call-journal.js +269 -0
  192. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/mcp-worker-runtime.js +81 -78
  193. package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime/validator-application.js +2 -2
  194. package/generated/plugins/copilot/agdf/runtime/create-agdf/package.json +1 -1
  195. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/events.js +157 -0
  196. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/figure-state.js +38 -0
  197. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/journal.js +186 -0
  198. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/companion/run-view.js +227 -0
  199. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/acceptance-criteria.js +2 -0
  200. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/closeout-readiness.js +24 -6
  201. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/decision-content-placeholder.js +4 -1
  202. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-check.js +49 -7
  203. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-policy-inputs.js +2 -0
  204. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/gate-policy-pure.js +7 -3
  205. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/prd-readiness.js +7 -2
  206. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/report-decision.js +9 -0
  207. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/traceability-readiness.js +57 -8
  208. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-evaluation/ur-readiness.js +15 -2
  209. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/artifact-readiness.js +5 -3
  210. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/authoring-inputs.js +107 -22
  211. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/contract.js +64 -24
  212. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/material-recording-calls.js +54 -0
  213. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/project-state.js +2 -1
  214. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/run-classification.js +31 -0
  215. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/selection.js +6 -0
  216. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/service.js +32 -5
  217. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-inspect/work-situation.js +25 -0
  218. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/authoring-text-handoff.js +3 -0
  219. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/input-resolution.js +7 -5
  220. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/material-limits.js +4 -0
  221. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/prepared-control-calls.js +137 -0
  222. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/presentation-delivery.js +4 -1
  223. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-command-contract.js +10 -4
  224. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-command-schema.js +9 -3
  225. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-command-service.js +35 -11
  226. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-create-command.js +6 -1
  227. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-identity.js +2 -0
  228. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-inventory-reader.js +54 -5
  229. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-inventory.js +12 -27
  230. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-material.js +25 -7
  231. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-presentation-render.js +113 -28
  232. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-revision.js +38 -20
  233. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-source-revisions.js +37 -17
  234. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-state-reader.js +10 -3
  235. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-steps.js +8 -5
  236. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/control-state/run-title.js +43 -0
  237. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/index.js +3 -1
  238. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/interaction-catalog-validation.js +7 -1
  239. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/interaction-catalog.js +8 -0
  240. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/interaction-presentation.js +31 -10
  241. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/mcp-input-hints.js +30 -1
  242. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/mcp-tool-errors.js +5 -3
  243. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/resources/canonical-init-plan.js +1 -1
  244. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/runtime/plugin-provenance.js +9 -2
  245. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/contract.js +6 -4
  246. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/delivery-intake.js +3 -1
  247. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/mcp-continuation.js +25 -2
  248. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/prd-definition.js +3 -2
  249. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/readiness-findings.js +7 -0
  250. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/sd-definition.js +4 -2
  251. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/service.js +17 -4
  252. package/generated/plugins/copilot/agdf/runtime/create-agdf/runtime/core/lib/skill-dispatch/tp-definition.js +4 -2
  253. package/generated/plugins/copilot/agdf/runtime/runtime-manifest.json +2 -2
  254. package/generated/plugins/copilot/agdf/skills/agdf-interaction-locales.json +1 -1
  255. package/generated/plugins/copilot/agdf/skills/agdf-ux-intent-definition/SKILL.md +31 -2
  256. package/generated/plugins/copilot/agdf/skills/contracts/gate-artifact-preparation.md +9 -3
  257. package/generated/plugins/copilot/agdf/skills/contracts/interaction.md +10 -4
  258. package/generated/plugins/copilot/agdf/skills/contracts/modes.md +5 -0
  259. package/generated/plugins/copilot/agdf/skills/contracts/prd-definition.md +40 -1
  260. package/generated/plugins/copilot/agdf/skills/contracts/quality.md +35 -0
  261. package/generated/plugins/copilot/agdf/skills/contracts/request-activation.md +6 -3
  262. package/generated/plugins/copilot/agdf/skills/contracts/sd-definition.md +46 -0
  263. package/generated/plugins/copilot/agdf/skills/contracts/tp-definition.md +8 -0
  264. package/generated/plugins/copilot/agdf/skills/contracts/ur-definition.md +9 -5
  265. package/generated/submissions/openai/agdf/.codex-plugin/plugin.json +1 -1
  266. package/generated/submissions/openai/agdf/meta/agdf-interaction-locales.json +436 -2
  267. package/generated/submissions/openai/agdf/meta/agdf-plugin.definition.json +1 -1
  268. package/generated/submissions/openai/agdf/meta/contracts/gate-artifact-preparation.md +9 -3
  269. package/generated/submissions/openai/agdf/meta/contracts/interaction.md +10 -4
  270. package/generated/submissions/openai/agdf/meta/contracts/modes.md +5 -0
  271. package/generated/submissions/openai/agdf/meta/contracts/prd-definition.md +40 -1
  272. package/generated/submissions/openai/agdf/meta/contracts/quality.md +35 -0
  273. package/generated/submissions/openai/agdf/meta/contracts/request-activation.md +6 -3
  274. package/generated/submissions/openai/agdf/meta/contracts/sd-definition.md +46 -0
  275. package/generated/submissions/openai/agdf/meta/contracts/tp-definition.md +8 -0
  276. package/generated/submissions/openai/agdf/meta/contracts/ur-definition.md +9 -5
  277. package/generated/submissions/openai/agdf/meta/copilot-payload-baseline.json +3 -3
  278. package/generated/submissions/openai/agdf/meta/distribution-profile-history.json +14 -0
  279. package/generated/submissions/openai/agdf/plugin.json +1 -1
  280. package/generated/submissions/openai/agdf/skills/ux-intent-definition/SKILL.md +31 -2
  281. package/generated/submissions/openai/agdf/skills/ux-intent-definition/help.md +8 -1
  282. package/generated/submissions/openai/agdf/submission/openai/availability.md +1 -1
  283. package/generated/submissions/openai/agdf/submission/openai/capability-matrix.json +1 -1
  284. package/generated/submissions/openai/agdf/submission/openai/inventory.json +24 -24
  285. package/generated/submissions/openai/agdf/submission/openai/listing.json +1 -1
  286. package/generated/submissions/openai/agdf/submission/openai/readiness.json +2 -2
  287. package/generated/submissions/openai/agdf/submission/openai/readiness.md +2 -2
  288. package/generated/submissions/openai/agdf/submission/openai/release-notes.md +5 -8
  289. package/generated/submissions/openai/agdf/submission/openai/reviewer-cases.json +1 -1
  290. package/lib/cli/command-registry.js +2 -1
  291. package/lib/cli/parse-args.js +7 -4
  292. package/lib/cli/project-read-command.js +29 -0
  293. package/lib/cli/validation-handlers.js +2 -1
  294. package/lib/mcp-dispatch-runtime.js +36 -7
  295. package/lib/mcp-lifecycle/package.js +7 -0
  296. package/lib/mcp-lifecycle/plugin-runtime.js +34 -6
  297. package/lib/mcp-lifecycle/prepared-call-journal.js +269 -0
  298. package/lib/mcp-worker-runtime.js +86 -83
  299. package/lib/runtime/validator-application.js +2 -2
  300. package/lib/scaffold/plan.js +2 -0
  301. package/package.json +1 -1
  302. package/runtime/core/lib/companion/events.js +157 -0
  303. package/runtime/core/lib/companion/figure-state.js +38 -0
  304. package/runtime/core/lib/companion/journal.js +186 -0
  305. package/runtime/core/lib/companion/run-view.js +227 -0
  306. package/runtime/core/lib/control-evaluation/acceptance-criteria.js +2 -0
  307. package/runtime/core/lib/control-evaluation/closeout-readiness.js +24 -6
  308. package/runtime/core/lib/control-evaluation/decision-content-placeholder.js +4 -1
  309. package/runtime/core/lib/control-evaluation/gate-check.js +49 -7
  310. package/runtime/core/lib/control-evaluation/gate-policy-inputs.js +2 -0
  311. package/runtime/core/lib/control-evaluation/gate-policy-pure.js +7 -3
  312. package/runtime/core/lib/control-evaluation/prd-readiness.js +7 -2
  313. package/runtime/core/lib/control-evaluation/report-decision.js +9 -0
  314. package/runtime/core/lib/control-evaluation/traceability-readiness.js +57 -8
  315. package/runtime/core/lib/control-evaluation/ur-readiness.js +15 -2
  316. package/runtime/core/lib/control-inspect/artifact-readiness.js +5 -3
  317. package/runtime/core/lib/control-inspect/authoring-inputs.js +107 -22
  318. package/runtime/core/lib/control-inspect/cockpit-list.js +10 -8
  319. package/runtime/core/lib/control-inspect/cockpit-session.js +11 -3
  320. package/runtime/core/lib/control-inspect/cockpit-work.js +4 -2
  321. package/runtime/core/lib/control-inspect/cockpit.js +47 -33
  322. package/runtime/core/lib/control-inspect/contract.js +64 -24
  323. package/runtime/core/lib/control-inspect/material-recording-calls.js +54 -0
  324. package/runtime/core/lib/control-inspect/project-state.js +2 -1
  325. package/runtime/core/lib/control-inspect/run-classification.js +31 -0
  326. package/runtime/core/lib/control-inspect/selection.js +6 -0
  327. package/runtime/core/lib/control-inspect/service.js +32 -5
  328. package/runtime/core/lib/control-inspect/work-situation.js +25 -0
  329. package/runtime/core/lib/control-read/cockpit-worker.js +1 -0
  330. package/runtime/core/lib/control-state/authoring-text-handoff.js +3 -0
  331. package/runtime/core/lib/control-state/input-resolution.js +7 -5
  332. package/runtime/core/lib/control-state/material-limits.js +4 -0
  333. package/runtime/core/lib/control-state/prepared-control-calls.js +137 -0
  334. package/runtime/core/lib/control-state/presentation-delivery.js +4 -1
  335. package/runtime/core/lib/control-state/run-command-contract.js +10 -4
  336. package/runtime/core/lib/control-state/run-command-schema.js +9 -3
  337. package/runtime/core/lib/control-state/run-command-service.js +35 -11
  338. package/runtime/core/lib/control-state/run-create-command.js +6 -1
  339. package/runtime/core/lib/control-state/run-identity.js +2 -0
  340. package/runtime/core/lib/control-state/run-inventory-reader.js +54 -5
  341. package/runtime/core/lib/control-state/run-inventory.js +12 -27
  342. package/runtime/core/lib/control-state/run-material.js +25 -7
  343. package/runtime/core/lib/control-state/run-presentation-render.js +113 -28
  344. package/runtime/core/lib/control-state/run-revision.js +38 -20
  345. package/runtime/core/lib/control-state/run-source-revisions.js +37 -17
  346. package/runtime/core/lib/control-state/run-state-reader.js +10 -3
  347. package/runtime/core/lib/control-state/run-steps.js +8 -5
  348. package/runtime/core/lib/control-state/run-title.js +43 -0
  349. package/runtime/core/lib/index.js +3 -1
  350. package/runtime/core/lib/interaction-catalog-validation.js +7 -1
  351. package/runtime/core/lib/interaction-catalog.js +8 -0
  352. package/runtime/core/lib/interaction-presentation.js +31 -10
  353. package/runtime/core/lib/mcp-input-hints.js +30 -1
  354. package/runtime/core/lib/mcp-tool-errors.js +5 -3
  355. package/runtime/core/lib/resources/canonical-init-plan.js +1 -1
  356. package/runtime/core/lib/runtime/plugin-provenance.js +9 -2
  357. package/runtime/core/lib/skill-dispatch/contract.js +6 -4
  358. package/runtime/core/lib/skill-dispatch/delivery-intake.js +3 -1
  359. package/runtime/core/lib/skill-dispatch/mcp-continuation.js +25 -2
  360. package/runtime/core/lib/skill-dispatch/prd-definition.js +3 -2
  361. package/runtime/core/lib/skill-dispatch/readiness-findings.js +7 -0
  362. package/runtime/core/lib/skill-dispatch/sd-definition.js +4 -2
  363. package/runtime/core/lib/skill-dispatch/service.js +17 -4
  364. package/runtime/core/lib/skill-dispatch/tp-definition.js +4 -2
  365. package/generated/plugins/agdf/companion/core.mjs +0 -16
  366. 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) {
@@ -300,6 +355,35 @@ function artifactSummary(gate, markdown, { runId, revisionId, language, sourceLa
300
355
  return { markdown: summary, digest: hash(summary) };
301
356
  }
302
357
 
358
+ // Cockpit presentation reuses the same language and summary owner as authoring checks.
359
+ // The structured rows are derived from that summary; no second content authority is created.
360
+ export function projectCockpitSummary(gate, markdown, languages, context = {}) {
361
+ if (!['UR', 'PRD', 'SD', 'TP'].includes(gate)) return null;
362
+ try {
363
+ const readiness = evaluateApprovalSummaryReadiness(gate, markdown, languages, { path: context.artefactPath });
364
+ if (!readiness.ready) return null;
365
+ const summary = artifactSummary(gate, markdown, { ...context, language: languages.presentation_language, sourceLanguage: languages.artifact_language });
366
+ const priorities = {
367
+ UR: [/^(?:Ziel|Goal)$/iu, /^(?:Umfang|Scope)$/iu, /^(?:Abnahme|Abnahmesignal|Acceptance)$/iu],
368
+ PRD: [/^(?:Ziel|Nutzerziel|Goal|User intent)$/iu, /^(?:Umfang|Produktumfang|Scope|Product scope)$/iu, /^(?:AC-[A-Z0-9-]+)$/iu],
369
+ SD: [/^(?:Lösung|Solution)$/iu, /^(?:Verantwortung|Ownership)$/iu, /^(?:Entscheidungen|Design decisions)$/iu],
370
+ TP: [/^(?:Aufgaben|Tasks)$/iu, /^(?:Tests)$/iu, /^(?:Risiken|Risks)$/iu],
371
+ };
372
+ const rows = summary.markdown.split(/\r?\n/u).flatMap(line => {
373
+ const match = line.match(/^[-*]\s+([^:\n]{1,80}):\s*(\S.*)$/u);
374
+ return match && match[2].length <= 1200 ? [{ label: match[1].trim(), text: match[2].trim() }] : [];
375
+ });
376
+ const items = priorities[gate].flatMap(pattern => {
377
+ const matches = rows.filter(row => pattern.test(row.label));
378
+ return matches.length === 1 ? matches : [];
379
+ });
380
+ return { state: 'available', gate, items, markdown: summary.markdown, authorizes: false };
381
+ } catch (error) {
382
+ if (String(error.message).startsWith('approval_summary_')) return null;
383
+ throw error;
384
+ }
385
+ }
386
+
303
387
  // A compact decision brief is a projection of the already validated, source-
304
388
  // authored summary. Missing authored fields stay absent; no agent/UI paraphrase.
305
389
  export function decisionBriefFromSummary(summary, language) {
@@ -388,16 +472,17 @@ export function renderReviewableApproval(root, report, { runId, gate, revisionId
388
472
  const languageName = pack.statusCard[locale.split("-")[0] === "de" ? "languageGerman" : "languageEnglish"];
389
473
  const artifactLabel = german ? "Artefakt" : "Artefact";
390
474
  const requiredHeading = languages.approval_summary_heading ?? `AGDF Approval Summary (${locale}; source=${sourceLanguage})`;
475
+ const sourceIssue = error.message === 'approval_summary_criterion_content_missing';
391
476
  error.recovery = [
392
- `## ${pack.statusCard.summaryRecovery}`,
477
+ `## ${sourceIssue ? (german ? 'PRD-Quellkriterien korrigieren' : 'Correct PRD source criteria') : pack.statusCard.summaryRecovery}`,
393
478
  `Run: \`${runId}\` · Gate: \`${gate}\` · Revision: \`${revisionId}\``,
394
479
  renderApprovalSummaryIssue({ reason: error.message, required_heading: requiredHeading, diagnostics: error.diagnostics }, {
395
480
  path, presentationLanguage: p.presentation_language,
396
481
  }),
397
- pack.operationalValues.approvalSummaryRecovery.replace("{language}", languageName),
398
- `${german ? "Erforderlicher Abschnitt" : "Required section"}: \`${requiredHeading}\``,
482
+ ...(!sourceIssue ? [pack.operationalValues.approvalSummaryRecovery.replace("{language}", languageName),
483
+ `${german ? "Erforderlicher Abschnitt" : "Required section"}: \`${requiredHeading}\``] : []),
399
484
  `${artifactLabel}: [${basename(path)}](<${path}>)`,
400
- pack.operationalValues.completeMissingApprovalSummary,
485
+ ...(!sourceIssue ? [pack.operationalValues.completeMissingApprovalSummary] : []),
401
486
  ].join("\n\n");
402
487
  }
403
488
  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');
@@ -0,0 +1,43 @@
1
+ // One owner for a Run's human title and orienting Objective. Pure: no `node:` import, so the run
2
+ // inventory, the cockpit, the companion run view and the status card share exactly this rule.
3
+ // Precedence: registered UR heading, then the authored Objective, then the normalized Run ID.
4
+ import { isDecisionContentPlaceholder } from "../control-evaluation/decision-content-placeholder.js";
5
+
6
+ // The run-ID title every reader shows when no authored title exists.
7
+ export function normalizedRunTitle(runId) {
8
+ const words = String(runId ?? "").replace(/[._-]+/g, " ").replace(/\s+/g, " ").trim();
9
+ return words ? words.replace(/\b\p{L}/gu, (letter) => letter.toUpperCase()) : "AGDF run";
10
+ }
11
+
12
+ // A heading as display text: no leading hashes and no inline Markdown marks.
13
+ export function plainHeading(value) {
14
+ return String(value ?? "").replace(/^#+\s*/u, "").replace(/[`*_]/gu, "").replace(/\s+/gu, " ").trim();
15
+ }
16
+
17
+ // Lines of one level-two section up to the next heading; an empty section stays empty.
18
+ function sectionLines(text, heading) {
19
+ const lines = text.split("\n"), start = lines.findIndex(line => line.trimEnd() === `## ${heading}`);
20
+ if (start < 0) return [];
21
+ const rest = lines.slice(start + 1), end = rest.findIndex(line => /^#{1,2} /u.test(line));
22
+ return end < 0 ? rest : rest.slice(0, end);
23
+ }
24
+
25
+ // The first paragraph of the Objective, so hard-wrapped objectives stay whole. A template prompt,
26
+ // a status word or a bare generated Run ID is no orientation and yields null.
27
+ export function authoredObjective(runContent) {
28
+ const text = String(runContent ?? "").replace(/^/u, "").replace(/\r\n?/gu, "\n");
29
+ const lines = sectionLines(text, "Objective").map(value => value.trim()), first = lines.findIndex(Boolean);
30
+ if (first < 0) return null;
31
+ const paragraph = lines.slice(first), end = paragraph.indexOf("");
32
+ const value = (end < 0 ? paragraph : paragraph.slice(0, end)).join(" ")
33
+ .replace(/^(?:[-*>]|#+|\d+\.)\s+/u, "").replace(/\*\*|__|`/gu, "").replace(/\s+/gu, " ").trim();
34
+ return isDecisionContentPlaceholder(value) ? null : value;
35
+ }
36
+
37
+ // `urHeading` is the registered UR document heading without its "UR:" prefix, when the caller read it.
38
+ export function runIdentity({ runId, runContent, urHeading } = {}) {
39
+ const objective = authoredObjective(runContent), ur = plainHeading(urHeading);
40
+ if (ur) return { title: ur, title_source: "ur", objective };
41
+ if (objective) return { title: objective, title_source: "objective", objective };
42
+ return { title: normalizedRunTitle(runId), title_source: "run_id", objective: null };
43
+ }
@@ -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)),