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
@@ -4,7 +4,7 @@ import { validateCommandOptions, commandGroupRoutes } from "../cli/command-regis
4
4
  import { CliUsageError, parseArgs } from "../cli/parse-args.js";
5
5
  import { pluginDefinition } from "../cli/runtime-context.js";
6
6
 
7
- const validatorCommands = new Set(["project-state", "operation-status", "target-check", "skill-dispatch", "doctor", "gate-check", "delivery-map", "inventory", "delivery-path-search", "contract", "control-maintenance", "run-create", "run-present", "run-revise", "run-step", "run-approve", "run-close", ...Object.keys(commandGroupRoutes)]);
7
+ const validatorCommands = new Set(["project-state", "operation-status", "run-view", "target-check", "skill-dispatch", "doctor", "gate-check", "delivery-map", "inventory", "delivery-path-search", "contract", "control-maintenance", "run-create", "run-present", "run-revise", "run-step", "run-approve", "run-close", ...Object.keys(commandGroupRoutes)]);
8
8
 
9
9
  export async function runValidatorCli(argv = process.argv.slice(2), adapters = {}) {
10
10
  const io = adapters.io ?? console;
@@ -22,7 +22,7 @@ export async function runValidatorCli(argv = process.argv.slice(2), adapters = {
22
22
  return error.exitCode;
23
23
  }
24
24
  if (parsed.kind === "help") {
25
- io.log("AGDF surface-local validator: project-state | operation-status | target-check | skill-dispatch | doctor | gate-check | delivery-map | inventory | delivery-path-search | contract | control-maintenance | run-create | run-present | run-revise | run-step | run-approve | run-close");
25
+ io.log("AGDF surface-local validator: project-state | operation-status | run-view | target-check | skill-dispatch | doctor | gate-check | delivery-map | inventory | delivery-path-search | contract | control-maintenance | run-create | run-present | run-revise | run-step | run-approve | run-close");
26
26
  return 0;
27
27
  }
28
28
  let options;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agdf/local-validator-runtime",
3
- "version": "0.30.0",
3
+ "version": "0.32.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "imports": {
@@ -0,0 +1,157 @@
1
+ // Companion events (SD SDD-005, SDD-009): the pure mapping from the final result of an AGDF MCP tool
2
+ // call to zero or one event of schema `companion-event/1`, and the reader of the versioned journal
3
+ // format. Events come from real MCP outcomes, never from host hooks, so the agent cannot shape them.
4
+ // Read-only tools produce none. Review facts (SDD-008) are computed by the runtime and handed in.
5
+ // Host-neutral: no Node built-in, no host API.
6
+ import { COMMAND_TOOL_NAMES, TOOL_RESULT_META } from '../mcp-tool-errors.js';
7
+ import { RUN_STEP_SLOTS } from '../control-state/run-identity.js';
8
+
9
+ export const COMPANION_EVENT_SCHEMA = 'companion-event/1';
10
+ export const EVENT_KINDS = Object.freeze(['approval_requested', 'approval_recorded', 'write_rejected', 'effect_unknown',
11
+ 'recovery_required', 'run_completed', 'material_staged', 'document_recorded']);
12
+ // Decision P1: these kinds interrupt as a toast, together with a digest mismatch (a drift warning of
13
+ // the run view); approval recorded and write rejected appear in the pane only.
14
+ export const TOAST_KINDS = Object.freeze(['approval_requested', 'effect_unknown', 'recovery_required', 'run_completed']);
15
+ // Decision P2: at most 40 lines or 4 KiB, whichever is smaller.
16
+ export const PREVIEW_LIMITS = Object.freeze({ lines: 40, bytes: 4096 });
17
+ export const SOURCE_MATCHES = Object.freeze(['match', 'mismatch', 'not_determinable', 'unavailable']);
18
+
19
+ // The tool-result descriptor key and the command tools come from the MCP tool-error contract, and the
20
+ // slot of a recorded step from the run identity owner; all three modules are pure (CR finding CIR-04).
21
+ export const TOOL_RESULT_META_KEY = TOOL_RESULT_META;
22
+ const COMMAND_TOOLS = new Set(COMMAND_TOOL_NAMES);
23
+ const TEXT_LIMIT = 256;
24
+ const encoder = new TextEncoder();
25
+ const bounded = value => (typeof value === 'string' && value.length ? value.slice(0, TEXT_LIMIT) : null);
26
+
27
+ // The bounded preview of a text: cut after the 40th line break unless the text ends there, then to
28
+ // at most 4096 UTF-8 bytes on a character boundary. Exactly 40 lines or 4096 bytes stay whole.
29
+ export function reviewPreview(text) {
30
+ const value = String(text ?? '');
31
+ let preview = value;
32
+ let at = -1;
33
+ for (let line = 0; line < PREVIEW_LIMITS.lines; line += 1) {
34
+ at = value.indexOf('\n', at + 1);
35
+ if (at < 0) break;
36
+ }
37
+ if (at >= 0 && at < value.length - 1) preview = value.slice(0, at + 1);
38
+ if (encoder.encode(preview).length > PREVIEW_LIMITS.bytes) {
39
+ let size = 0;
40
+ let cut = 0;
41
+ for (const char of preview) {
42
+ const width = encoder.encode(char).length;
43
+ if (size + width > PREVIEW_LIMITS.bytes) break;
44
+ size += width;
45
+ cut += char.length;
46
+ }
47
+ preview = preview.slice(0, cut);
48
+ }
49
+ return { text: preview, truncated: preview.length < value.length, bytes: encoder.encode(value).length };
50
+ }
51
+
52
+ // One review fact: what the runtime saw of recorded or staged material. Unknown values stay null
53
+ // ("unavailable"), never guessed (PRD AC-007).
54
+ export function reviewFact({ slot = null, source, path = null, digest = null, text = null, sourceMatch }) {
55
+ const preview = typeof text === 'string' ? reviewPreview(text) : null;
56
+ return {
57
+ slot: bounded(slot),
58
+ source: source === 'path' ? 'path' : 'staged',
59
+ path: bounded(path),
60
+ digest: /^sha256:[a-f0-9]{64}$/u.test(digest ?? '') ? digest : null,
61
+ bytes: preview?.bytes ?? null,
62
+ preview: preview?.text ?? null,
63
+ truncated: preview?.truncated ?? null,
64
+ source_match: SOURCE_MATCHES.includes(sourceMatch) ? sourceMatch : 'unavailable',
65
+ };
66
+ }
67
+
68
+ // The slot (gate or step) a command names, for the event's gate field.
69
+ export function commandGate(command = {}) {
70
+ const payload = command.payload ?? {};
71
+ if (command.kind === 'closeout.record') return 'OR';
72
+ if (command.kind === 'step.record') return payload.internal_step ?? RUN_STEP_SLOTS[payload.step] ?? null;
73
+ if (command.kind === 'verified_change.record') return 'Verified Change';
74
+ if (command.kind === 'ur_draft.replace') return 'UR';
75
+ if (command.kind === 'operation.recover') return null;
76
+ return typeof payload.gate === 'string' ? payload.gate : null;
77
+ }
78
+
79
+ function successKind(command, result) {
80
+ const kind = command?.kind;
81
+ if (kind === 'presentation.deliver') {
82
+ return result.historical_effect === true || result.presentation_delivery?.historical_delivery === true
83
+ ? null : 'approval_requested';
84
+ }
85
+ if (kind === 'approval.record') return 'approval_recorded';
86
+ if (kind === 'closeout.record') return 'run_completed';
87
+ if (kind === 'material.stage' && command.payload?.action === 'seal') return 'material_staged';
88
+ // Every document recording of run-steps.recordRunStep (run-command-service.js).
89
+ if (['step.record', 'artefact.record', 'verified_change.record', 'ur_draft.replace'].includes(kind)) return 'document_recorded';
90
+ return null;
91
+ }
92
+
93
+ // The event kind of one final command result, or null for a read, a replay or an outcome outside
94
+ // the eight kinds. A failure is classified by the projected descriptor's effect state first, so a
95
+ // thrown failure of unknown effect is never reported as rejected.
96
+ export function eventKind(toolName, input, result) {
97
+ if (!COMMAND_TOOLS.has(toolName) || !result || typeof result !== 'object') return null;
98
+ const descriptor = result._meta?.[TOOL_RESULT_META_KEY] ?? null;
99
+ if (result.outcome === 'accepted') return successKind(input?.command, result);
100
+ if (result.outcome === 'already_applied') return null;
101
+ if (result.outcome === 'recovery_required') return 'recovery_required';
102
+ if (result.outcome === 'effect_unknown' || descriptor?.effect_state === 'unknown') return 'effect_unknown';
103
+ if (descriptor?.failed || result.outcome === 'rejected') return 'write_rejected';
104
+ return null;
105
+ }
106
+
107
+ // The event of one final tool result: { schema_version, id, at, root, run_id, gate, kind, outcome,
108
+ // reason, operation_id, review } or null. `id`, `at` and `root` come from the writer; `review` holds
109
+ // the runtime's review facts for staged or recorded material.
110
+ export function deriveEvent(toolName, input, result, { id, at, root = null, review = null } = {}) {
111
+ const kind = eventKind(toolName, input, result);
112
+ if (!kind) return null;
113
+ const descriptor = result._meta?.[TOOL_RESULT_META_KEY] ?? null;
114
+ const facts = Array.isArray(review) && review.length && ['material_staged', 'document_recorded'].includes(kind) ? review.slice(0, 4) : null;
115
+ return {
116
+ schema_version: COMPANION_EVENT_SCHEMA,
117
+ id: bounded(id),
118
+ at: bounded(at),
119
+ root: typeof root === 'string' && root.length ? root : bounded(input?.primary_target) ?? bounded(input?.working_directory),
120
+ run_id: bounded(input?.run_id) ?? bounded(result.run_id),
121
+ gate: kind === 'material_staged' ? facts?.[0]?.slot ?? null : commandGate(input?.command),
122
+ kind,
123
+ outcome: bounded(result.outcome) ?? (descriptor?.failed ? 'failed' : null),
124
+ reason: descriptor?.failed ? bounded(descriptor.code) ?? bounded(result.reason) : null,
125
+ operation_id: bounded(input?.operation_id),
126
+ review: facts,
127
+ };
128
+ }
129
+
130
+ // A journal line is an event when it parses and carries the schema, a known kind and an id.
131
+ export function validEvent(value) {
132
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value) && value.schema_version === COMPANION_EVENT_SCHEMA
133
+ && EVENT_KINDS.includes(value.kind) && typeof value.id === 'string' && typeof value.at === 'string';
134
+ }
135
+
136
+ // The events of a journal text in file order; malformed or foreign lines are skipped, since several
137
+ // MCP servers may interleave lines (SDD-006).
138
+ export function parseEventLines(text) {
139
+ const events = [];
140
+ for (const line of String(text ?? '').split('\n')) {
141
+ if (!line.trim()) continue;
142
+ try {
143
+ const value = JSON.parse(line);
144
+ if (validEvent(value)) events.push(value);
145
+ } catch { /* an interleaved or cut line */ }
146
+ }
147
+ return events;
148
+ }
149
+
150
+ // Roots compare after normalizing separators, a trailing separator and, on win32, case (SD 5).
151
+ export function sameRoot(left, right, { win32 = false } = {}) {
152
+ const normal = value => {
153
+ const text = String(value ?? '').replaceAll('\\', '/').replace(/\/+$/u, '');
154
+ return win32 ? text.toLowerCase() : text;
155
+ };
156
+ return Boolean(left) && Boolean(right) && normal(left) === normal(right);
157
+ }
@@ -0,0 +1,38 @@
1
+ // Companion figure state (SD SDD-004): one mood and a reason descriptor per run-view entry, derived
2
+ // only from the status-card fields the run view copied. Pure and host-neutral; hosts localize the
3
+ // reason with the catalog's companion section and may overlay their own activity (turn in progress).
4
+ import { COMPANION_MOODS } from '../interaction-catalog.js';
5
+ import { RUN_MODES } from '../control-state/run-identity.js';
6
+
7
+ export { COMPANION_MOODS };
8
+ // The internal steps between an approved TP and QA, shown as building in structured runs; hosts that
9
+ // mark the build phase use this list (CR finding CIR-04).
10
+ export const BUILD_GATES = Object.freeze(['Brownfield Analysis', 'CD+Tests', 'CR']);
11
+ export const STRUCTURED_MODES = Object.freeze(RUN_MODES.filter(mode => mode.startsWith('structured_')));
12
+ // The ended lifecycles of run-operations.js (ENDED_LIFECYCLES), kept here so this module stays pure;
13
+ // the companion tests assert both agree.
14
+ export const ENDED_LIFECYCLES = Object.freeze(['superseded', 'abandoned']);
15
+ // The pending approval as the run inventory reads it (run-inventory.js runAttention): a negated
16
+ // mention ("No Approval: QA") is no pending approval.
17
+ const pendingApproval = missing => String(missing ?? '').match(/(?<!\b(?:[Nn]o|[Nn]ot|[Ww]ithout) )\bApproval: (UR|PRD|SD|TP|QA|UAT|OR)\b/u)?.[1] ?? null;
18
+
19
+ // { mood, reason: { key, gate?, decision?, reason? } }; null or no entry is the idle figure.
20
+ export function deriveFigureState(entry) {
21
+ if (!entry) return { mood: 'bereit', reason: { key: 'idle' } };
22
+ if (entry.valid === false) return { mood: 'blockiert', reason: { key: 'unreadable', reason: entry.unreadable_reason ?? 'run_state_invalid' } };
23
+ if (entry.evaluation_error) return { mood: 'blockiert', reason: { key: 'unreadable', reason: entry.evaluation_error } };
24
+ // A completed run whose evaluation is blocked is not done: the block is shown like any other.
25
+ const blocked = entry.evaluated === true && (entry.status === 'blocked' || Boolean(entry.blocking_reason && entry.blocking_reason !== 'none'));
26
+ // A completed run without confirmed completion is still shown, but claims nothing: its own reason.
27
+ if (entry.lifecycle === 'completed' && !blocked) return entry.completion_confirmed === true
28
+ ? { mood: 'fertig', reason: { key: 'done' } } : { mood: 'bereit', reason: { key: 'completionUnconfirmed' } };
29
+ if (ENDED_LIFECYCLES.includes(entry.lifecycle)) return { mood: 'bereit', reason: { key: 'ended' } };
30
+ if (!entry.evaluated) return { mood: 'bereit', reason: { key: 'idle' } };
31
+ const gate = entry.current_gate;
32
+ if (blocked) return { mood: 'blockiert', reason: { key: 'blocked', gate, decision: entry.blocking_reason } };
33
+ const approval = pendingApproval(entry.missing_approval);
34
+ if (approval) return { mood: 'wartet', reason: { key: 'waitingApproval', gate: approval } };
35
+ if (entry.user_action_required === 'yes') return { mood: 'wartet', reason: { key: 'waitingDecision', gate } };
36
+ if (STRUCTURED_MODES.includes(entry.mode) && BUILD_GATES.includes(gate)) return { mood: 'baut', reason: { key: 'building', gate } };
37
+ return { mood: 'laeuft', reason: { key: 'working', gate } };
38
+ }
@@ -0,0 +1,186 @@
1
+ // Companion event journal (SD SDD-006, SDD-008): the runtime appends one line per event of a final
2
+ // AGDF MCP tool result to `companion/events.jsonl` below the plugin data root, bounded to 256 KiB with
3
+ // one rotation file `events.1.jsonl`. It is a notification channel only, never a source of truth, and
4
+ // nothing here writes under `.agdf/control`. Every failure is swallowed: the tool result never changes.
5
+ import { createHash, randomUUID } from 'node:crypto';
6
+ import { appendFileSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, statSync } from 'node:fs';
7
+ import { isAbsolute, join, resolve } from 'node:path';
8
+ import { resolveTaskTarget } from '../task-target-resolution.js';
9
+ import { artefactFileDigest } from '../control-state/run-seal-compute.js';
10
+ import { containedRegularFile } from '../control-state/contained-file.js';
11
+ import { atomicWrite, withOwnedFileLock } from '../owned-file-lock.js';
12
+ import { commandGate, deriveEvent, eventKind, reviewFact } from './events.js';
13
+
14
+ export const JOURNAL_DIRECTORY = 'companion';
15
+ export const JOURNAL_FILE = 'events.jsonl';
16
+ export const JOURNAL_ROTATED_FILE = 'events.1.jsonl';
17
+ export const JOURNAL_LIMIT_BYTES = 256 * 1024;
18
+ // The launcher hands its data root (CLAUDE_PLUGIN_DATA on Claude, --data on Codex) to the runtime
19
+ // processes through this variable (SDD-006).
20
+ export const COMPANION_DATA_ROOT_ENV = 'AGDF_COMPANION_DATA_ROOT';
21
+ const SOURCE_TEXT_LIMIT = 1024 * 1024;
22
+ const RESERVATIONS_FILE = 'approval-requests.json';
23
+ const RESERVATIONS_LIFETIME_MS = 30 * 60_000;
24
+ const RESERVATIONS_MAX_ENTRIES = 8192;
25
+ const RESERVATIONS_MAX_BYTES = 1024 * 1024;
26
+ const RESERVATIONS_LOCK_WAIT_MS = 50;
27
+ const DIGEST = /^[a-f0-9]{64}$/u;
28
+
29
+ export const journalPath = dataRoot => join(dataRoot, JOURNAL_DIRECTORY, JOURNAL_FILE);
30
+
31
+ const isLink = path => { try { return lstatSync(path).isSymbolicLink(); } catch { return false; } };
32
+
33
+ function readReservations(path) {
34
+ let stat;
35
+ try { stat = lstatSync(path); }
36
+ catch (error) { if (error.code === 'ENOENT') return []; throw error; }
37
+ if (!stat.isFile() || stat.size > RESERVATIONS_MAX_BYTES) throw Error('invalid notification reservations');
38
+ const bytes = readFileSync(path);
39
+ if (bytes.length > RESERVATIONS_MAX_BYTES) throw Error('invalid notification reservations');
40
+ const stored = JSON.parse(bytes);
41
+ if (!stored || typeof stored !== 'object' || Array.isArray(stored)) throw Error('invalid notification reservations');
42
+ const entries = Object.entries(stored);
43
+ if (entries.length > RESERVATIONS_MAX_ENTRIES
44
+ || entries.some(([key, expires]) => !DIGEST.test(key) || !Number.isSafeInteger(expires) || expires < 0)) {
45
+ throw Error('invalid notification reservations');
46
+ }
47
+ return entries;
48
+ }
49
+
50
+ // Only the notification survives a serving-process restart. Neither this
51
+ // reservation nor the journal line participates in delivery or approval.
52
+ function reserveApprovalRequest(dataRoot, input, at) {
53
+ const identity = [input.target_id, input.run_id, input.expected_revision_id,
54
+ input.command.payload.presentation_id, input.operation_id];
55
+ if (!identity.every(value => typeof value === 'string' && value.length > 0)) return false;
56
+ const key = createHash('sha256').update(JSON.stringify(identity)).digest('hex');
57
+ const directory = join(dataRoot, JOURNAL_DIRECTORY);
58
+ if (isLink(dataRoot) || isLink(directory)) return false;
59
+ mkdirSync(directory, { recursive: true, mode: 0o700 });
60
+ const path = join(directory, RESERVATIONS_FILE);
61
+ const deadline = performance.now() + RESERVATIONS_LOCK_WAIT_MS;
62
+ for (;;) {
63
+ try {
64
+ return withOwnedFileLock(path, () => {
65
+ const live = new Map(readReservations(path).filter(([, expires]) => expires > at));
66
+ if (live.has(key) || live.size >= RESERVATIONS_MAX_ENTRIES) return false;
67
+ live.set(key, at + RESERVATIONS_LIFETIME_MS);
68
+ const bytes = JSON.stringify(Object.fromEntries(live));
69
+ if (Buffer.byteLength(bytes) > RESERVATIONS_MAX_BYTES) return false;
70
+ atomicWrite(path, bytes);
71
+ return true;
72
+ });
73
+ } catch (error) {
74
+ const remaining = deadline - performance.now();
75
+ if (error.lock_owner_status !== 'live' || remaining <= 0) return false;
76
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, Math.min(5, remaining));
77
+ }
78
+ }
79
+ }
80
+
81
+ // Appends one event line; rotates first when the line would cross the bound. Returns whether the line
82
+ // was written. Concurrent servers may interleave whole lines or lose some at rotation (accepted).
83
+ export function appendJournalEvent(dataRoot, event, { limit = JOURNAL_LIMIT_BYTES } = {}) {
84
+ try {
85
+ if (typeof dataRoot !== 'string' || !dataRoot || !event) return false;
86
+ const directory = join(dataRoot, JOURNAL_DIRECTORY);
87
+ if (isLink(dataRoot) || isLink(directory)) return false;
88
+ mkdirSync(directory, { recursive: true });
89
+ const line = `${JSON.stringify(event)}\n`;
90
+ const size = Buffer.byteLength(line, 'utf8');
91
+ if (size > limit) return false;
92
+ const file = join(directory, JOURNAL_FILE);
93
+ if (isLink(file)) return false;
94
+ let current = 0;
95
+ try { current = statSync(file).size; } catch { current = 0; }
96
+ if (current > 0 && current + size > limit) renameSync(file, join(directory, JOURNAL_ROTATED_FILE));
97
+ appendFileSync(file, line, 'utf8');
98
+ return true;
99
+ } catch {
100
+ return false;
101
+ }
102
+ }
103
+
104
+ // The governance root of a command input, resolved like the material store does; the input's own
105
+ // target path when it does not resolve.
106
+ export function commandRoot(input = {}) {
107
+ try {
108
+ const target = resolveTaskTarget({ workingDirectory: input.working_directory, targetSource: input.target_source, primaryTarget: input.primary_target });
109
+ if (target.resolution_state === 'resolved' && target.governance_target) return target.governance_target;
110
+ } catch { /* fall back to the input path */ }
111
+ return input.primary_target ?? input.working_directory ?? null;
112
+ }
113
+
114
+ // The event's root: the caller's target path when it names the same directory as the resolved root
115
+ // through another spelling (junction, symlink, subst drive), so a host comparing with its own session
116
+ // root still matches (SD SDD-005 "the MCP target path", CR finding A6); else the resolved root.
117
+ // Review facts keep reading below the resolved root.
118
+ export function eventRoot(input = {}, root = commandRoot(input)) {
119
+ const given = typeof input.primary_target === 'string' && isAbsolute(input.primary_target) ? resolve(input.primary_target) : null;
120
+ if (!given || !root || given === root) return root;
121
+ try { return realpathSync(given) === realpathSync(root) ? given : root; } catch { return root; }
122
+ }
123
+
124
+ // Review facts of a path source: the file read now, its canonical digest from the node digest owner
125
+ // compared with the declared digest (match or mismatch); unreadable files are unavailable.
126
+ function pathFact(root, slot, source) {
127
+ const current = root ? artefactFileDigest(root, source.path) : 'unresolved';
128
+ if (!/^sha256:/u.test(current)) return reviewFact({ slot, source: 'path', path: source.path, digest: source.digest, sourceMatch: 'unavailable' });
129
+ let text = null;
130
+ try {
131
+ const file = containedRegularFile(root, String(source.path).trim());
132
+ if (file.status === 'valid' && statSync(file.path).size <= SOURCE_TEXT_LIMIT) text = readFileSync(file.path, 'utf8');
133
+ } catch { text = null; }
134
+ return reviewFact({ slot, source: 'path', path: source.path, digest: source.digest, text, sourceMatch: current === source.digest ? 'match' : 'mismatch' });
135
+ }
136
+
137
+ // Review facts (SDD-008, decision P4): sealed staged material carries its preview, size and digest
138
+ // with source_match not_determinable; a recording from a path carries the path, the recorded digest
139
+ // and whether the file still matches it; a recording from staged material names its digest only.
140
+ export function reviewFacts(toolName, input, result, { sealedFiles = () => null, root = commandRoot(input) } = {}) {
141
+ const kind = eventKind(toolName, input, result);
142
+ const command = input?.command ?? {};
143
+ if (kind === 'material_staged') {
144
+ const files = sealedFiles(command.payload?.transfer_id) ?? [];
145
+ return files.map(file => reviewFact({ slot: file.slot, source: 'staged', digest: file.digest, text: file.content, sourceMatch: 'not_determinable' }));
146
+ }
147
+ if (kind !== 'document_recorded') return null;
148
+ const payload = command.payload ?? {};
149
+ const source = payload.source ?? payload.material ?? null;
150
+ const slot = commandGate(command);
151
+ if (source?.path) return [pathFact(root, slot, source)];
152
+ if (source?.material_id) return [reviewFact({ slot, source: 'staged', digest: source.digest, sourceMatch: 'not_determinable' })];
153
+ return null;
154
+ }
155
+
156
+ // The runtime's observer of final tool results. Without a data root it does nothing.
157
+ export function createCompanionJournal({ dataRoot = process.env[COMPANION_DATA_ROOT_ENV], sealedFiles = () => null,
158
+ now = () => new Date(), newId = randomUUID, limit = JOURNAL_LIMIT_BYTES } = {}) {
159
+ const enabled = typeof dataRoot === 'string' && dataRoot.length > 0;
160
+ return Object.freeze({
161
+ enabled,
162
+ path: enabled ? journalPath(dataRoot) : null,
163
+ // Whether `observe` writes an event for this result, without writing: a facade asks this for a
164
+ // result its generation produced, and so already journaled.
165
+ observes(toolName, input, result) {
166
+ if (!enabled) return false;
167
+ try { return Boolean(eventKind(toolName, input, result)); } catch { return false; }
168
+ },
169
+ observe(toolName, input, result) {
170
+ if (!enabled) return false;
171
+ try {
172
+ const kind = eventKind(toolName, input, result);
173
+ if (!kind) return false;
174
+ const root = commandRoot(input);
175
+ const review = reviewFacts(toolName, input, result, { sealedFiles, root });
176
+ const at = now();
177
+ const event = deriveEvent(toolName, input, result, { id: newId(), at: at.toISOString(), root: eventRoot(input, root), review });
178
+ if (!event) return false;
179
+ if (kind === 'approval_requested' && !reserveApprovalRequest(dataRoot, input, at.getTime())) return false;
180
+ return appendJournalEvent(dataRoot, event, { limit });
181
+ } catch {
182
+ return false;
183
+ }
184
+ },
185
+ });
186
+ }
@@ -0,0 +1,227 @@
1
+ // Companion run view (SD SDD-001, SDD-002): a read-only projection of the unchanged gate-check
2
+ // report of each run, beside the run inventory of one project root. It never re-derives a gate
3
+ // rule: status fields are copied from the status card verbatim, so every consumer of the run
4
+ // view sees exactly what the status card shows. Nothing here writes or authorizes.
5
+ import { existsSync, realpathSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import { readFileSync, statSync } from '../control-read/fs.js';
8
+ import { captureControlScope } from '../control-read/snapshot.js';
9
+ import { containedRegularFile, hasSymlinkComponent, isSafeControlRelativePath } from '../control-state/contained-file.js';
10
+ import { parseControlState } from '../control-state/run-state-parser.js';
11
+ import { runIdentity } from '../control-state/run-title.js';
12
+ import { localizedDecisionFields } from '../control-evaluation/decision-content-readiness.js';
13
+ import { evaluateGateCheck } from '../control-evaluation/gate-check.js';
14
+ import { readRunInventory } from '../control-state/run-inventory-reader.js';
15
+ import { runFolderEntries } from '../control-state/run-state-reader.js';
16
+ import { readArtefactBindings } from '../control-state/artefact-bindings.js';
17
+ import { readEvidenceBindings } from '../control-state/evidence-bindings.js';
18
+ import { runRelativePath } from '../control-state/run-identity.js';
19
+ import { artefactFileDigest } from '../control-state/run-seal-compute.js';
20
+ import { boundedCode } from '../control-inspect/run-classification.js';
21
+ import { localizeBlockingCondition, localizeOperationalValue } from '../interaction-presentation.js';
22
+ import { interactionLocales, resolveArtifactPresentationLanguages } from '../resources/context.js';
23
+ import { deriveFigureState } from './figure-state.js';
24
+
25
+ export const RUN_VIEW_SCHEMA = 'run-view/1';
26
+ export const DIAGNOSIS_REASONS = Object.freeze(['run_version_unsupported', 'run_state_invalid', 'too_large', 'missing']);
27
+ // The reader's findings for a run folder without a readable run state: the path finding of
28
+ // run-state-reader.js (invalidRun) and the inventory's finding for a run without text (run-inventory.js).
29
+ const MISSING_CODES = new Set(['AGDF_RUN_PATH_INVALID', 'AGDF_RUN_STATE_UNAVAILABLE']);
30
+ // The classifications run-state-resolver.js treats as unsupported (AGDF_RUN_VERSION_UNSUPPORTED).
31
+ const UNSUPPORTED_CLASSIFICATIONS = new Set(['unsupported_version', 'unsupported_layout']);
32
+
33
+ // Per-run reason of an unreadable run (SD SDD-003) from the reader findings: unsupported by their
34
+ // classification, as run-state-resolver.js and the cockpit decide it (CR finding CIR-03), missing
35
+ // when only the missing-state findings are present, otherwise invalid. Core reports no size bound
36
+ // of its own, so an oversized run state is reported with the reason Core gives it; too_large stays
37
+ // reserved for such a bound (contradiction C1).
38
+ export function diagnosisReason(findings) {
39
+ if (findings.some(finding => UNSUPPORTED_CLASSIFICATIONS.has(finding.classification))) return 'run_version_unsupported';
40
+ if (findings.length && findings.every(finding => MISSING_CODES.has(finding.code))) return 'missing';
41
+ return 'run_state_invalid';
42
+ }
43
+
44
+ export function projectDiagnosis(entries) {
45
+ const unreadable = entries.filter(entry => !entry.valid).map(entry => ({ run_id: entry.run_id, reason: entry.unreadable_reason ?? 'run_state_invalid', codes: entry.findings }));
46
+ const by_reason = Object.fromEntries(DIAGNOSIS_REASONS.map(reason => [reason, unreadable.filter(row => row.reason === reason).length]));
47
+ return { unreadable, counts: { unreadable: unreadable.length, readable: entries.length - unreadable.length, by_reason } };
48
+ }
49
+
50
+ const NAMING_TEXT_LIMIT = 500;
51
+ const UR_READ_LIMIT = 131072;
52
+ const bounded = value => (typeof value === 'string' && value.length > NAMING_TEXT_LIMIT ? `${value.slice(0, NAMING_TEXT_LIMIT - 1)}…` : value);
53
+
54
+ // How a run names itself to a person, read inside the run's own snapshot: the stable title of the
55
+ // shared Run title rule (registered UR heading, then the authored Objective; a bare Run ID is no
56
+ // title) and the goal in the display language. Where display and artefact language differ, the goal
57
+ // is the one the UR's localized approval summary states. An unreadable UR leaves both to the Run document.
58
+ export function readRunNaming(root, runId, language = 'en') {
59
+ let content;
60
+ try { content = readFileSync(join(root, runRelativePath(runId, '', 'RUN_STATE.md')), 'utf8'); } catch { return null; }
61
+ let urHeading = '', localizedGoal = null;
62
+ try {
63
+ const path = String(parseControlState(content).artefacts.get('UR')?.path ?? '').replace(/^`|`$/gu, '').trim();
64
+ if (isSafeControlRelativePath(path) && path.startsWith('.agdf/control/') && !hasSymlinkComponent(root, path)) {
65
+ const file = containedRegularFile(root, path);
66
+ if (file.status === 'valid' && statSync(file.path).size <= UR_READ_LIMIT) {
67
+ const text = readFileSync(file.path, 'utf8');
68
+ urHeading = (text.match(/^#\s+(.+)$/mu)?.[1] ?? '').replace(/^UR:\s*/iu, '').trim();
69
+ const sourceLanguage = resolveArtifactPresentationLanguages(root, language).artifact_language;
70
+ localizedGoal = localizedDecisionFields('UR', text, { locale: String(language).split('-')[0], sourceLanguage, path }).fields.goal ?? null;
71
+ }
72
+ }
73
+ } catch (error) { if (error?.code === 'source_changed') throw error; }
74
+ const identity = runIdentity({ runId, runContent: content, urHeading });
75
+ return { run_title: identity.title_source === 'run_id' ? null : bounded(identity.title),
76
+ run_title_source: identity.title_source, goal: bounded(localizedGoal ?? identity.objective) };
77
+ }
78
+
79
+ // One run entry: inventory facts plus, when evaluated, the status card fields of its report.
80
+ // `naming` is readRunNaming's result for an evaluated run; without it the inventory names the run.
81
+ export function projectRunEntry(inventoryEntry, report, { registry = interactionLocales, language = 'en', naming = null } = {}) {
82
+ const authored = inventoryEntry.title_source === 'objective' ? inventoryEntry.title ?? null : null;
83
+ const base = {
84
+ run_id: inventoryEntry.run_id,
85
+ title: inventoryEntry.title ?? null,
86
+ // The stable human title, the same at every gate; null while only the Run ID could name the run.
87
+ run_title: naming ? naming.run_title : authored,
88
+ run_title_source: naming?.run_title_source ?? (authored ? 'objective' : 'run_id'),
89
+ // The goal in the display language where the UR states one, otherwise the authored objective.
90
+ goal: naming ? naming.goal : inventoryEntry.objective ?? null,
91
+ // The authored objective only, from the shared Run title rule; a template objective or a
92
+ // bare Run ID is no goal (CR finding C2).
93
+ objective: inventoryEntry.objective ?? null,
94
+ lifecycle: inventoryEntry.lifecycle ?? null,
95
+ valid: inventoryEntry.valid !== false,
96
+ revision: inventoryEntry.revision ?? null,
97
+ revision_id: inventoryEntry.revision_id ?? null,
98
+ last_activity: inventoryEntry.last_activity?.at ?? null,
99
+ findings: (inventoryEntry.findings ?? []).map(finding => finding.code),
100
+ };
101
+ if (!base.valid) base.unreadable_reason = diagnosisReason(inventoryEntry.findings ?? []);
102
+ // A run whose evaluation threw is reported with the bounded reason, never as an idle run (CR A7).
103
+ if (report?.evaluation_error) return withFigure({ ...base, evaluated: false, evaluation_error: report.evaluation_error });
104
+ if (!report?.status_card) return withFigure({ ...base, evaluated: false });
105
+ const card = report.status_card;
106
+ return withFigure({
107
+ ...base,
108
+ title: card.humanPresentation?.runTitle ?? base.title,
109
+ evaluated: true,
110
+ completion_confirmed: base.lifecycle === 'completed' && report.completion?.fulfilled === true,
111
+ status: card.status,
112
+ current_gate: card.current_gate,
113
+ path: card.breadcrumb.map(({ gate, status }) => ({ gate, status })),
114
+ missing_approval: card.missing_approval,
115
+ blocking_reason: card.blocking_condition,
116
+ blocking_text: localizeBlockingCondition(card.blocking_condition, { registry, requestedLocale: language }) ?? card.blocking_condition,
117
+ next_step: card.next_step,
118
+ next_step_text: localizeOperationalValue(card.next_step, { registry, requestedLocale: language }) ?? card.next_step,
119
+ user_action_required: card.user_action_required,
120
+ doctor_status: report.doctor_status,
121
+ mode: card.mode_slice_decision ?? card.mode ?? null,
122
+ });
123
+ }
124
+ const withFigure = entry => ({ ...entry, figure: deriveFigureState(entry) });
125
+
126
+ // Pure assembly of the versioned view from already evaluated parts.
127
+ export function projectRunView({ root, language, store, entries }) {
128
+ return {
129
+ schema_version: RUN_VIEW_SCHEMA,
130
+ root,
131
+ language,
132
+ store,
133
+ diagnosis: projectDiagnosis(entries.filter(entry => entry.listed !== false)),
134
+ runs: entries,
135
+ authorizes: false,
136
+ };
137
+ }
138
+
139
+ // Drift warnings (SD SDD-008, PRD AC-008): documents the run state binds with a recorded digest
140
+ // (effective artefact binding destination, latest evidence report per path) whose file now has another
141
+ // canonical digest from the node digest owner. Declared input files of a recording are not kept in
142
+ // the run state, so only bound documents are compared (contradiction C5 of this run). An unreadable
143
+ // run state yields no warning; the status card reports it.
144
+ export function readRunDrift(root, runId) {
145
+ let content;
146
+ try { content = readFileSync(join(root, runRelativePath(runId, '', 'RUN_STATE.md')), 'utf8'); } catch { return []; }
147
+ const declared = new Map();
148
+ const artefacts = readArtefactBindings(content);
149
+ // A binding a recorded source revision invalidated is superseded for Core, so its document being
150
+ // re-drafted is no drift (CR finding A5).
151
+ for (const receipt of artefacts.valid ? artefacts.active ?? [] : []) {
152
+ declared.set(receipt.destination.path, { document: receipt.destination.type, digest: receipt.destination.digest });
153
+ }
154
+ const evidence = readEvidenceBindings(content);
155
+ for (const row of evidence.valid ? evidence.bindings : []) {
156
+ declared.set(row.report.path, { document: row.report.path.split('/').at(-1).replace(/\.md$/u, ''), digest: row.report.digest });
157
+ }
158
+ const drift = [];
159
+ for (const [path, { document, digest }] of declared) {
160
+ const current = artefactFileDigest(root, path);
161
+ if (current === digest) continue;
162
+ drift.push({ document, path, recorded_digest: digest, current_digest: /^sha256:/u.test(current) ? current : null });
163
+ }
164
+ return drift;
165
+ }
166
+
167
+ // Active runs are evaluated; terminal and unreadable runs are summarized unless requested by id, so
168
+ // neither a large history nor many unreadable runs cost one evaluation per run. An unreadable run's
169
+ // reason and figure come from the inventory findings.
170
+ const needsEvaluation = (entry, requested) => requested.has(entry.run_id) || (entry.valid && entry.lifecycle === 'active');
171
+
172
+ // A write that lands during a capture invalidates it (source_changed); the read is repeated a bounded
173
+ // number of times before the change is reported (CR delta review RV-1).
174
+ export const CAPTURE_ATTEMPTS = 3;
175
+ export function captured(readRoot, project, capture = captureControlScope) {
176
+ for (let attempt = 1; ; attempt += 1) {
177
+ try { return capture(readRoot, view => project(view.root)).data; }
178
+ catch (error) { if (error?.code !== 'source_changed' || attempt >= CAPTURE_ATTEMPTS) throw error; }
179
+ }
180
+ }
181
+
182
+ // Reads inside bounded, revalidated control snapshots (TP T-002, SD section 4: captureControlScope,
183
+ // CR finding CIR-01): the inventory in one snapshot and each evaluated run with its drift in its own,
184
+ // so a read-boundary failure of one run (a linked file, a document outside the control folder, a read
185
+ // limit) marks only that run and the readable runs stay listed (SDD-003, RV-2). The root is resolved
186
+ // first, as MCP canonicalizes its target, so a junction or symlinked project root reads like its
187
+ // target; the view keeps the caller's spelling (RV-3). A missing root has no control folder (RV-5).
188
+ export function readRunView(root, { language = 'en', runIds = [] } = {}) {
189
+ const empty = store => projectRunView({ root, language, store, entries: [] });
190
+ if (!existsSync(root)) return empty({ status: 'no_control', reason: null });
191
+ let readRoot;
192
+ try { readRoot = realpathSync(root); } catch (error) { return empty({ status: 'unavailable', reason: boundedCode(error) }); }
193
+ const requested = new Set(runIds);
194
+ const evaluate = runId => {
195
+ try {
196
+ return captured(readRoot, base => {
197
+ const report = evaluateGateCheck(base, { runId, ignoreRunIdEnv: true, presentationLanguage: language });
198
+ return { report, drift: report?.status_card ? readRunDrift(base, runId) : null, naming: readRunNaming(base, runId, language) };
199
+ });
200
+ } catch (error) { return { report: { evaluation_error: boundedCode(error) }, drift: null, naming: null }; }
201
+ };
202
+ const entryOf = (inventoryEntry, evaluated) => {
203
+ const entry = projectRunEntry(inventoryEntry, evaluated?.report ?? null, { language, naming: evaluated?.naming ?? null });
204
+ return entry.evaluated && entry.valid !== false && evaluated.drift ? { ...entry, drift: evaluated.drift } : entry;
205
+ };
206
+ let inventory = null, store;
207
+ try {
208
+ inventory = captured(readRoot, base => readRunInventory(base, { language, observe: false }));
209
+ store = { status: inventory.state === 'absent' ? 'no_control' : 'ok', reason: null };
210
+ } catch (error) {
211
+ store = { status: 'unavailable', reason: boundedCode(error) };
212
+ // An unsupported layout lists no run, but its run folders can be counted, so a consumer names count
213
+ // and reason instead of "no open runs" (PRD AC-002, TP T-004). A failing count leaves the field out.
214
+ if (store.reason === 'control_layout_unsupported') {
215
+ try { store.run_folders = captured(readRoot, base => runFolderEntries(base).length); } catch { /* count unknown */ }
216
+ }
217
+ }
218
+ const listed = inventory?.runs ?? [];
219
+ const entries = listed.map(entry => entryOf(entry, needsEvaluation(entry, requested) ? evaluate(entry.run_id) : null));
220
+ for (const runId of requested) {
221
+ if (listed.some(entry => entry.run_id === runId)) continue;
222
+ // Requested but not listed by the inventory (for example behind an unsupported layout):
223
+ // evaluated for its card, never counted in the diagnosis of listed runs.
224
+ entries.push({ ...entryOf({ run_id: runId, findings: [] }, evaluate(runId)), valid: null, listed: false });
225
+ }
226
+ return projectRunView({ root, language, store, entries });
227
+ }
@@ -4,6 +4,8 @@ const CRITERION_ID = /^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)*$/u;
4
4
  export const ACCEPTANCE_CRITERIA_FORMAT = Object.freeze({
5
5
  section: "Acceptance Criteria", declaration: "criterion_id: <stable id>",
6
6
  fields: "semicolon-separated inline fields or following bullet key: value lines",
7
+ description_fields: Object.freeze(["observable_success", "visible_feedback", "expected_effective_state"]),
8
+ description_rule: "For same-language approval rendering, provide at least one nonempty description field separately. Never concatenate field names or infer their meaning from combined keys.",
7
9
  id_pattern: CRITERION_ID.source,
8
10
  examples: Object.freeze([
9
11
  "## Acceptance Criteria\n- criterion_id: AC-001; observable_success: Saved choice remains selected",