specrails-desktop 2.55.0 → 2.56.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 (368) hide show
  1. package/cli/dist/args.js +80 -0
  2. package/cli/dist/desktop-client.js +125 -0
  3. package/cli/dist/desktop-control.js +339 -0
  4. package/cli/dist/format.js +45 -0
  5. package/cli/dist/help.js +68 -0
  6. package/cli/dist/output.js +47 -0
  7. package/cli/dist/run-command.js +340 -0
  8. package/cli/dist/specrails-desktop.js +41 -1107
  9. package/cli/dist/status.js +111 -0
  10. package/client/dist/assets/{ActivityFeedPage-ILXMZXcb.js → ActivityFeedPage-DdMNmVBk.js} +1 -1
  11. package/client/dist/assets/{AgentBrowserCapture-UcsupCy7.js → AgentBrowserCapture-CeerUf--.js} +1 -1
  12. package/client/dist/assets/{AgentModeAnalyticsPane-CQuxYh8B.js → AgentModeAnalyticsPane-BjGb-8rh.js} +2 -2
  13. package/client/dist/assets/{AgentModeCodePane-zPWbW7ei.js → AgentModeCodePane-8XRkQEo_.js} +2 -2
  14. package/client/dist/assets/{AgentModeJobsPane-CInByIdl.js → AgentModeJobsPane-BN3dgJOA.js} +1 -1
  15. package/client/dist/assets/{AgentsPage-BWvgxaBv.js → AgentsPage-DfHWgP_x.js} +1 -1
  16. package/client/dist/assets/{AnalyticsPage-DP3jakv-.js → AnalyticsPage-CyfhYiBn.js} +1 -1
  17. package/client/dist/assets/{CodePage-BKXiwE5a.js → CodePage-C2Nf-Q5W.js} +1 -1
  18. package/client/dist/assets/{DesktopAnalyticsPage-DryJOaMz.js → DesktopAnalyticsPage-tc0p4jJW.js} +1 -1
  19. package/client/dist/assets/{DocsDialog-BsCG9ksG.js → DocsDialog-Br3bczCp.js} +1 -1
  20. package/client/dist/assets/{DocsPage-Cc-GeKYl.js → DocsPage-BcDr2cBm.js} +1 -1
  21. package/client/dist/assets/{ExportDropdown-ClahJdDn.js → ExportDropdown-BVGKEgkd.js} +1 -1
  22. package/client/dist/assets/{InteractiveJobComposer-DfLF6N0f.js → InteractiveJobComposer-DFPpdg2D.js} +1 -1
  23. package/client/dist/assets/{JobDetailModal-DHeur9YK.js → JobDetailModal-BdOv-l-M.js} +1 -1
  24. package/client/dist/assets/{JobDetailPage-CraDKzeG.js → JobDetailPage-vBbNeRuz.js} +1 -1
  25. package/client/dist/assets/{JobsPage-CcbuSwj7.js → JobsPage-CJCtPhIz.js} +1 -1
  26. package/client/dist/assets/{LoopBuilderPage-BKtNp494.js → LoopBuilderPage-BemvUOxy.js} +1 -1
  27. package/client/dist/assets/{LoopPreviewModal-DbZd9isy.js → LoopPreviewModal-C6hNAeYN.js} +1 -1
  28. package/client/dist/assets/{LoopsPage-DEg6sIkG.js → LoopsPage-BZr0quvh.js} +1 -1
  29. package/client/dist/assets/{MinimizedChatsContext-BGtEjWsc.js → MinimizedChatsContext-D3QzgV0B.js} +1 -1
  30. package/client/dist/assets/{PluginsPage-BVqSsPzL.js → PluginsPage-DOpTV75p.js} +1 -1
  31. package/client/dist/assets/{ProjectSettingsDialog-CAjLgJAZ.js → ProjectSettingsDialog-Dkwrn0z1.js} +1 -1
  32. package/client/dist/assets/{RepositoryDeliveries-DrcrxLe0.js → RepositoryDeliveries-Du2UCf1a.js} +1 -1
  33. package/client/dist/assets/{RepositoryScopeSelector-CAGY3xW6.js → RepositoryScopeSelector-hPGZNRBm.js} +1 -1
  34. package/client/dist/assets/ReviewPacketPage-DcOq-UTA.js +4 -0
  35. package/client/dist/assets/{TemplatePreviewModal-Bqs-9fhn.js → TemplatePreviewModal-Cejcquah.js} +1 -1
  36. package/client/dist/assets/{TicketDetailModalContext-C0jQ5yOk.js → TicketDetailModalContext-DAK-NoK6.js} +1 -1
  37. package/client/dist/assets/{Trans-DtWlCJN9.js → Trans-DEbgkuAY.js} +1 -1
  38. package/client/dist/assets/{dashboard-CTfEtkqX.js → dashboard-B9uWNGO2.js} +1 -1
  39. package/client/dist/assets/{dashboard-DMklepmz.js → dashboard-BIwmO9wc.js} +1 -1
  40. package/client/dist/assets/{dashboard-DBAP2_SQ.js → dashboard-BV4vOZkR.js} +1 -1
  41. package/client/dist/assets/{dashboard-D40HP2ks.js → dashboard-Bf6MPVsM.js} +1 -1
  42. package/client/dist/assets/{dashboard-Csf1yGSN.js → dashboard-C8klWVvZ.js} +1 -1
  43. package/client/dist/assets/{dashboard-C29Qwa-L.js → dashboard-D1ORhPF0.js} +1 -1
  44. package/client/dist/assets/{dashboard-Cr4D8oMn.js → dashboard-DSubvs9x.js} +1 -1
  45. package/client/dist/assets/{dashboard-CSJzf5OT.js → dashboard-px_vlRx_.js} +1 -1
  46. package/client/dist/assets/{formatDistanceToNow-DYmvOA3R.js → formatDistanceToNow-Cc2u5FwQ.js} +1 -1
  47. package/client/dist/assets/{getTimezoneOffsetInMilliseconds-BAhW5E6V.js → getTimezoneOffsetInMilliseconds-BHm2U-w1.js} +1 -1
  48. package/client/dist/assets/index-C51x61Bz.css +2 -0
  49. package/client/dist/assets/{index-BfAZg5DG.js → index-Cxb288Rj.js} +29 -29
  50. package/client/dist/assets/{jira-api-B27Za5FP.js → jira-api-DrGxiD9p.js} +1 -1
  51. package/client/dist/assets/packet-8nn9RsU7.js +1 -0
  52. package/client/dist/assets/packet-B0H15elv.js +1 -0
  53. package/client/dist/assets/packet-BQvBgrwh.js +1 -0
  54. package/client/dist/assets/packet-BR7mO0-a.js +1 -0
  55. package/client/dist/assets/packet-BgcQ6q_K.js +1 -0
  56. package/client/dist/assets/packet-D2s92Jnn.js +1 -0
  57. package/client/dist/assets/packet-YbtcD1HS.js +1 -0
  58. package/client/dist/assets/packet-c0Qj_9eX.js +1 -0
  59. package/client/dist/assets/{project-repositories-DixIZHTA.js → project-repositories-9Wzs0coQ.js} +1 -1
  60. package/client/dist/assets/{spending-BGmZBX5B.js → spending-T5GM7wpn.js} +1 -1
  61. package/client/dist/assets/{useDesktop-CPB4cnwI.js → useDesktop-QogiN0zR.js} +1 -1
  62. package/client/dist/assets/{useRuntimeRuns-DEQ9HdAM.js → useRuntimeRuns-Dqmz9fS9.js} +1 -1
  63. package/client/dist/assets/{useSharedWebSocket-DcPRKR8R.js → useSharedWebSocket-Dz-HI6De.js} +2 -2
  64. package/client/dist/index.html +17 -17
  65. package/docs/codex.md +5 -5
  66. package/docs/customizing.md +1 -1
  67. package/docs/gemini-cli-provider-study.md +14 -14
  68. package/docs/gemini-core-support-evaluation.md +1 -1
  69. package/docs/gemini.md +5 -5
  70. package/docs/internals/README.md +6 -2
  71. package/docs/internals/adding-a-provider.md +22 -15
  72. package/docs/internals/agent-runtime-framework-evaluation.md +8 -8
  73. package/docs/internals/api-reference.md +2 -2
  74. package/docs/internals/architecture.md +9 -9
  75. package/docs/internals/browser-capture-performance.md +3 -3
  76. package/docs/internals/code-explorer-story.md +2 -2
  77. package/docs/internals/companion-rails-as-loops-contract.md +2 -2
  78. package/docs/internals/core-runtime-updates.md +5 -0
  79. package/docs/internals/embedded-browser-native-webview-evaluation.md +5 -5
  80. package/docs/internals/gemini-mcp-registration.md +2 -2
  81. package/docs/internals/global-artifacts-alignment-contract.md +1 -1
  82. package/docs/internals/global-artifacts-relocation-evaluation.md +1 -1
  83. package/docs/internals/interactive-jobs.md +8 -8
  84. package/docs/internals/legacy-implementation-notes.md +722 -0
  85. package/docs/internals/loop-step-log-explorer.md +8 -8
  86. package/docs/internals/mission-rail-cards.md +14 -14
  87. package/docs/internals/modular-architecture.md +107 -0
  88. package/docs/internals/profiles.md +1 -1
  89. package/docs/internals/programmatic-agent-runtime.md +66 -0
  90. package/docs/internals/project-builder.md +18 -18
  91. package/docs/internals/review-packet.md +8 -8
  92. package/docs/internals/safe-pr-review-flow.md +11 -11
  93. package/docs/internals/source-architecture.md +131 -0
  94. package/docs/internals/source-map.md +1356 -0
  95. package/docs/internals/spec-addenda.md +65 -9
  96. package/docs/internals/switch-to-agent-mode-design.md +11 -11
  97. package/docs/tracking-cost.md +1 -1
  98. package/package.json +5 -2
  99. package/server/dist/agent-mcp-config.js +1 -1
  100. package/server/dist/attachment-manager.js +1 -1
  101. package/server/dist/build-dirs.js +2 -2
  102. package/server/dist/config.js +0 -3
  103. package/server/dist/core-completion.js +1 -1
  104. package/server/dist/core-execution.js +1 -1
  105. package/server/dist/db/activity.js +39 -0
  106. package/server/dist/db/connection.js +41 -0
  107. package/server/dist/db/conversations.js +48 -0
  108. package/server/dist/db/jobs.js +251 -0
  109. package/server/dist/db/migrations.js +1521 -0
  110. package/server/dist/db/proposals.js +50 -0
  111. package/server/dist/db/settings.js +15 -0
  112. package/server/dist/db/stats.js +61 -0
  113. package/server/dist/db/telemetry.js +56 -0
  114. package/server/dist/db/templates.js +37 -0
  115. package/server/dist/db/types.js +2 -0
  116. package/server/dist/db.js +66 -2224
  117. package/server/dist/desktop-db.js +3 -3
  118. package/server/dist/desktop-router.js +8 -8
  119. package/server/dist/git-diagnostics.js +1 -1
  120. package/server/dist/index.js +11 -11
  121. package/server/dist/integration-branch.js +3 -14
  122. package/server/dist/jira/jira-issue-fields.js +0 -1
  123. package/server/dist/jira/jira-materializer.js +1 -1
  124. package/server/dist/jira/jira-sync-manager.js +1 -1
  125. package/server/dist/mcp/guide.js +16 -2
  126. package/server/dist/mcp/mcp-admin-router.js +1 -1
  127. package/server/dist/mcp/mcp-server.js +1 -1
  128. package/server/dist/mcp/tools/catalog.js +2 -0
  129. package/server/dist/mcp/tools/jobs.js +23 -5
  130. package/server/dist/mcp/tools/rails.js +2 -2
  131. package/server/dist/mcp/tools/recovery.js +35 -0
  132. package/server/dist/mcp/tools/specs.js +2 -2
  133. package/server/dist/mcp/tools/support.js +4 -1
  134. package/server/dist/mcp/tools/types.js +3 -3
  135. package/server/dist/mobile/mobile-gateway.js +1 -1
  136. package/server/dist/mobile/mobile-missions.js +1 -1
  137. package/server/dist/{desktop-analytics.js → modules/accounting/runtime/desktop-analytics.js} +2 -2
  138. package/server/dist/{pricing.js → modules/accounting/runtime/pricing.js} +10 -3
  139. package/server/dist/{result-event.js → modules/accounting/runtime/result-event.js} +3 -3
  140. package/server/dist/{spending.js → modules/accounting/runtime/spending.js} +0 -3
  141. package/server/dist/{telemetry-compactor.js → modules/accounting/runtime/telemetry-compactor.js} +1 -1
  142. package/server/dist/{telemetry-receiver.js → modules/accounting/runtime/telemetry-receiver.js} +2 -2
  143. package/server/dist/{agent-runtime-bridge.js → modules/agent-runtime/runtime/agent-runtime-bridge.js} +2 -2
  144. package/server/dist/{agent-runtime-controls-router.js → modules/agent-runtime/runtime/agent-runtime-controls-router.js} +16 -0
  145. package/server/dist/{agent-runtime-controls.js → modules/agent-runtime/runtime/agent-runtime-controls.js} +100 -8
  146. package/server/dist/{agent-runtime-effective-config.js → modules/agent-runtime/runtime/agent-runtime-effective-config.js} +1 -1
  147. package/server/dist/{agent-runtime-loader.js → modules/agent-runtime/runtime/agent-runtime-loader.js} +3 -3
  148. package/server/dist/{agent-runtime-paths.js → modules/agent-runtime/runtime/agent-runtime-paths.js} +1 -1
  149. package/server/dist/modules/agent-runtime/runtime/agent-runtime-recovery.js +53 -0
  150. package/server/dist/{agent-runtime-settings-router.js → modules/agent-runtime/runtime/agent-runtime-settings-router.js} +2 -2
  151. package/server/dist/{agent-runtime-settings.js → modules/agent-runtime/runtime/agent-runtime-settings.js} +3 -3
  152. package/server/dist/{agent-runtime-settlement.js → modules/agent-runtime/runtime/agent-runtime-settlement.js} +6 -6
  153. package/server/dist/{agent-defaults.js → modules/agents/runtime/agent-defaults.js} +3 -3
  154. package/server/dist/{agent-generator.js → modules/agents/runtime/agent-generator.js} +7 -7
  155. package/server/dist/{agent-refine-manager.js → modules/agents/runtime/agent-refine-manager.js} +5 -5
  156. package/server/dist/{agent-store.js → modules/agents/runtime/agent-store.js} +1 -1
  157. package/server/dist/{profile-manager.js → modules/agents/runtime/profile-manager.js} +2 -2
  158. package/server/dist/{profiles-router.js → modules/agents/runtime/profiles-router.js} +5 -5
  159. package/server/dist/{browser-capture-manager.js → modules/browser/runtime/browser-capture-manager.js} +2 -4
  160. package/server/dist/{browser-playwright.js → modules/browser/runtime/browser-playwright.js} +1 -1
  161. package/server/dist/{blueprint-chat-manager.js → modules/builder/runtime/blueprint-chat-manager.js} +7 -7
  162. package/server/dist/{blueprint-commit.js → modules/builder/runtime/blueprint-commit.js} +8 -8
  163. package/server/dist/{blueprint-draft-parser.js → modules/builder/runtime/blueprint-draft-parser.js} +1 -1
  164. package/server/dist/{blueprint-generation.js → modules/builder/runtime/blueprint-generation.js} +1 -1
  165. package/server/dist/{blueprint-operator-prompt.js → modules/builder/runtime/blueprint-operator-prompt.js} +1 -1
  166. package/server/dist/{blueprint-router.js → modules/builder/runtime/blueprint-router.js} +3 -3
  167. package/server/dist/{blueprint-spec-quality.js → modules/builder/runtime/blueprint-spec-quality.js} +1 -1
  168. package/server/dist/{milestone-chain.js → modules/builder/runtime/milestone-chain.js} +1 -1
  169. package/server/dist/{milestone-progress.js → modules/builder/runtime/milestone-progress.js} +6 -6
  170. package/server/dist/{code-activity.js → modules/code/runtime/code-activity.js} +1 -1
  171. package/server/dist/{code-explorer-router.js → modules/code/runtime/code-explorer-router.js} +5 -5
  172. package/server/dist/{file-provenance.js → modules/code/runtime/file-provenance.js} +1 -1
  173. package/server/dist/{file-story-manager.js → modules/code/runtime/file-story-manager.js} +2 -2
  174. package/server/dist/{file-story.js → modules/code/runtime/file-story.js} +2 -2
  175. package/server/dist/{file-summary-generator.js → modules/code/runtime/file-summary-generator.js} +3 -3
  176. package/server/dist/{file-summary-manager.js → modules/code/runtime/file-summary-manager.js} +3 -3
  177. package/server/dist/{project-code-discovery.js → modules/code/runtime/project-code-discovery.js} +2 -2
  178. package/server/dist/modules/conversations/domain/draft-stream.js +91 -0
  179. package/server/dist/modules/conversations/domain/recovery-context.js +101 -0
  180. package/server/dist/modules/conversations/index.js +10 -0
  181. package/server/dist/{chat-manager.js → modules/conversations/runtime/chat-manager.js} +32 -216
  182. package/server/dist/{context-budget.js → modules/conversations/runtime/context-budget.js} +1 -1
  183. package/server/dist/{explore-cwd-manager.js → modules/conversations/runtime/explore-cwd-manager.js} +1 -1
  184. package/server/dist/{explore-smash.js → modules/conversations/runtime/explore-smash.js} +1 -1
  185. package/server/dist/{explore-stdin-session.js → modules/conversations/runtime/explore-stdin-session.js} +1 -1
  186. package/server/dist/modules/delivery/adapters/decisions/chains.js +138 -0
  187. package/server/dist/modules/delivery/adapters/decisions/contracts.js +2 -0
  188. package/server/dist/modules/delivery/adapters/decisions/create.js +234 -0
  189. package/server/dist/modules/delivery/adapters/decisions/discard.js +251 -0
  190. package/server/dist/modules/delivery/adapters/decisions/dispatch.js +107 -0
  191. package/server/dist/modules/delivery/adapters/decisions/evidence.js +205 -0
  192. package/server/dist/modules/delivery/adapters/decisions/merge-local.js +338 -0
  193. package/server/dist/modules/delivery/adapters/decisions/metadata.js +109 -0
  194. package/server/dist/modules/delivery/adapters/decisions/observations.js +260 -0
  195. package/server/dist/modules/delivery/adapters/decisions/ownership.js +83 -0
  196. package/server/dist/modules/delivery/adapters/decisions/publish.js +242 -0
  197. package/server/dist/modules/delivery/adapters/decisions/recovery.js +425 -0
  198. package/server/dist/modules/delivery/adapters/decisions/retry.js +112 -0
  199. package/server/dist/modules/delivery/adapters/decisions/transitions.js +94 -0
  200. package/server/dist/modules/delivery/domain/decision-policy.js +50 -0
  201. package/server/dist/modules/delivery/domain/state.js +2 -0
  202. package/server/dist/modules/delivery/index.js +7 -0
  203. package/server/dist/{active-pr-continuation.js → modules/delivery/runtime/active-pr-continuation.js} +2 -2
  204. package/server/dist/{delivery-evidence.js → modules/delivery/runtime/delivery-evidence.js} +3 -3
  205. package/server/dist/{multi-repo-bases.js → modules/delivery/runtime/multi-repo-bases.js} +1 -1
  206. package/server/dist/{multi-repo-checkout.js → modules/delivery/runtime/multi-repo-checkout.js} +4 -4
  207. package/server/dist/{multi-repo-delivery.js → modules/delivery/runtime/multi-repo-delivery.js} +4 -4
  208. package/server/dist/{multi-repo-execution.js → modules/delivery/runtime/multi-repo-execution.js} +8 -8
  209. package/server/dist/{pr-body.js → modules/delivery/runtime/pr-body.js} +1 -1
  210. package/server/dist/{pr-follow-up-scope.js → modules/delivery/runtime/pr-follow-up-scope.js} +2 -2
  211. package/server/dist/{pr-follow-up.js → modules/delivery/runtime/pr-follow-up.js} +1 -1
  212. package/server/dist/{pr-naming.js → modules/delivery/runtime/pr-naming.js} +1 -1
  213. package/server/dist/{pr-publisher.js → modules/delivery/runtime/pr-publisher.js} +2 -2
  214. package/server/dist/{rail-isolated-launch.js → modules/delivery/runtime/rail-isolated-launch.js} +29 -27
  215. package/server/dist/{rail-launch-parser.js → modules/delivery/runtime/rail-launch-parser.js} +2 -2
  216. package/server/dist/{rail-merge-orchestrator.js → modules/delivery/runtime/rail-merge-orchestrator.js} +5 -5
  217. package/server/dist/modules/delivery/runtime/rail-pr-decision.js +12 -0
  218. package/server/dist/{rail-pr-delivery.js → modules/delivery/runtime/rail-pr-delivery.js} +1 -1
  219. package/server/dist/{rail-pr-store.js → modules/delivery/runtime/rail-pr-store.js} +2 -2
  220. package/server/dist/{rail-pr-ticket-effects.js → modules/delivery/runtime/rail-pr-ticket-effects.js} +3 -3
  221. package/server/dist/{rail-worktree-release.js → modules/delivery/runtime/rail-worktree-release.js} +4 -4
  222. package/server/dist/{rails-router.js → modules/delivery/runtime/rails-router.js} +66 -67
  223. package/server/dist/{review-packet.js → modules/delivery/runtime/review-packet.js} +3 -3
  224. package/server/dist/modules/execution/adapters/budget-storage.js +21 -0
  225. package/server/dist/modules/execution/adapters/usage-reader.js +38 -0
  226. package/server/dist/modules/execution/application/enforce-budget.js +14 -0
  227. package/server/dist/modules/execution/application/record-job-invocations.js +64 -0
  228. package/server/dist/modules/execution/application/recover-job-usage.js +200 -0
  229. package/server/dist/modules/execution/domain/job-accounting.js +2 -0
  230. package/server/dist/modules/execution/domain/scheduling.js +24 -0
  231. package/server/dist/modules/execution/domain/usage.js +21 -0
  232. package/server/dist/modules/execution/index.js +14 -0
  233. package/server/dist/modules/execution/ports.js +2 -0
  234. package/server/dist/{accept-ladder.js → modules/execution/runtime/accept-ladder.js} +1 -1
  235. package/server/dist/{interactive-job-session.js → modules/execution/runtime/interactive-job-session.js} +11 -11
  236. package/server/dist/{job-phase-breakdown.js → modules/execution/runtime/job-phase-breakdown.js} +1 -1
  237. package/server/dist/{queue-manager.js → modules/execution/runtime/queue-manager.js} +74 -491
  238. package/server/dist/{revision-seed.js → modules/execution/runtime/revision-seed.js} +1 -1
  239. package/server/dist/{spawn-lifecycle.js → modules/execution/runtime/spawn-lifecycle.js} +3 -3
  240. package/server/dist/{stuck-run-detector.js → modules/execution/runtime/stuck-run-detector.js} +1 -1
  241. package/server/dist/{loop-command-catalog.js → modules/loops/runtime/loop-command-catalog.js} +1 -1
  242. package/server/dist/{loop-executors.js → modules/loops/runtime/loop-executors.js} +17 -17
  243. package/server/dist/{loop-factory.js → modules/loops/runtime/loop-factory.js} +2 -21
  244. package/server/dist/{loop-graph.js → modules/loops/runtime/loop-graph.js} +25 -0
  245. package/server/dist/{loop-role-engines.js → modules/loops/runtime/loop-role-engines.js} +4 -4
  246. package/server/dist/{loop-run-manager.js → modules/loops/runtime/loop-run-manager.js} +101 -26
  247. package/server/dist/{loop-runs-store.js → modules/loops/runtime/loop-runs-store.js} +1 -1
  248. package/server/dist/{loop-shell-invocation.js → modules/loops/runtime/loop-shell-invocation.js} +2 -2
  249. package/server/dist/{loop-step-idle.js → modules/loops/runtime/loop-step-idle.js} +1 -1
  250. package/server/dist/{loop-templates.js → modules/loops/runtime/loop-templates.js} +24 -29
  251. package/server/dist/{loops-router.js → modules/loops/runtime/loops-router.js} +2 -2
  252. package/server/dist/{agent-chat-manager.js → modules/missions/runtime/agent-chat-manager.js} +19 -19
  253. package/server/dist/{agent-chat-router.js → modules/missions/runtime/agent-chat-router.js} +6 -6
  254. package/server/dist/{agent-context-resolver.js → modules/missions/runtime/agent-context-resolver.js} +8 -8
  255. package/server/dist/{agent-failure-briefing.js → modules/missions/runtime/agent-failure-briefing.js} +4 -2
  256. package/server/dist/{agent-fence-promotion.js → modules/missions/runtime/agent-fence-promotion.js} +1 -1
  257. package/server/dist/{agent-input-store.js → modules/missions/runtime/agent-input-store.js} +1 -1
  258. package/server/dist/{agent-operator-prompt.js → modules/missions/runtime/agent-operator-prompt.js} +75 -12
  259. package/server/dist/{agent-spec-framing.js → modules/missions/runtime/agent-spec-framing.js} +4 -4
  260. package/server/dist/{agent-steering.js → modules/missions/runtime/agent-steering.js} +1 -1
  261. package/server/dist/{mission-run-notify.js → modules/missions/runtime/mission-run-notify.js} +5 -2
  262. package/server/dist/modules/project-settings/adapters/http.js +24 -0
  263. package/server/dist/modules/project-settings/adapters/sqlite.js +120 -0
  264. package/server/dist/modules/project-settings/application.js +11 -0
  265. package/server/dist/modules/project-settings/domain.js +95 -0
  266. package/server/dist/modules/project-settings/index.js +11 -0
  267. package/server/dist/modules/project-settings/ports.js +2 -0
  268. package/server/dist/{contract-refine-runner.js → modules/specs/runtime/contract-refine-runner.js} +12 -12
  269. package/server/dist/{proposal-manager.js → modules/specs/runtime/proposal-manager.js} +8 -8
  270. package/server/dist/{smash-runner.js → modules/specs/runtime/smash-runner.js} +26 -58
  271. package/server/dist/{spec-addenda-core.js → modules/specs/runtime/spec-addenda-core.js} +2 -2
  272. package/server/dist/{spec-addenda.js → modules/specs/runtime/spec-addenda.js} +3 -3
  273. package/server/dist/{spec-launcher-manager.js → modules/specs/runtime/spec-launcher-manager.js} +8 -8
  274. package/server/dist/{spec-models.js → modules/specs/runtime/spec-models.js} +1 -1
  275. package/server/dist/{ticket-watcher.js → modules/specs/runtime/ticket-watcher.js} +1 -1
  276. package/server/dist/{background-process-control.js → modules/terminals/runtime/background-process-control.js} +1 -1
  277. package/server/dist/{background-process-service.js → modules/terminals/runtime/background-process-service.js} +3 -3
  278. package/server/dist/{background-process-store.js → modules/terminals/runtime/background-process-store.js} +1 -1
  279. package/server/dist/{background-windows-bootstrap.js → modules/terminals/runtime/background-windows-bootstrap.js} +3 -3
  280. package/server/dist/{terminal-manager.js → modules/terminals/runtime/terminal-manager.js} +3 -3
  281. package/server/dist/project-git.js +1 -1
  282. package/server/dist/project-profile-support.js +32 -0
  283. package/server/dist/project-registry.js +34 -40
  284. package/server/dist/project-router-background-processes.js +1 -1
  285. package/server/dist/project-router-chat.js +6 -6
  286. package/server/dist/project-router-git.js +1 -1
  287. package/server/dist/project-router-helpers.js +2 -2
  288. package/server/dist/project-router-jobs.js +14 -12
  289. package/server/dist/project-router-loop-runs.js +12 -12
  290. package/server/dist/project-router-settings.js +15 -75
  291. package/server/dist/project-router-setup.js +2 -2
  292. package/server/dist/project-router-spending.js +4 -4
  293. package/server/dist/project-router-terminals.js +4 -4
  294. package/server/dist/project-router-tickets.js +14 -14
  295. package/server/dist/project-router.js +13 -13
  296. package/server/dist/providers/claude-adapter.js +3 -2
  297. package/server/dist/providers/codex-adapter.js +3 -3
  298. package/server/dist/providers/gemini-adapter.js +1 -1
  299. package/server/dist/providers/telemetry-env.js +55 -0
  300. package/server/dist/runtime-role-prompts-router.js +2 -2
  301. package/server/dist/setup-manager.js +2 -48
  302. package/server/dist/shared/git-branch-name.js +16 -0
  303. package/server/dist/transient-children.js +3 -3
  304. package/server/dist/util/distribute-int.js +33 -0
  305. package/client/dist/assets/ReviewPacketPage-D-UjTef3.js +0 -4
  306. package/client/dist/assets/index-BAtqmAZR.css +0 -2
  307. package/client/dist/assets/packet-CjS_478C.js +0 -1
  308. package/client/dist/assets/packet-D69dKsHN.js +0 -1
  309. package/client/dist/assets/packet-D9rU6Oxo.js +0 -1
  310. package/client/dist/assets/packet-DMDFoamg.js +0 -1
  311. package/client/dist/assets/packet-Dd2RMs1h.js +0 -1
  312. package/client/dist/assets/packet-DjC9OibC.js +0 -1
  313. package/client/dist/assets/packet-DwPtJoDz.js +0 -1
  314. package/client/dist/assets/packet-ceahDg8_.js +0 -1
  315. package/server/dist/rail-pr-decision.js +0 -2377
  316. package/server/dist/ticket-broadcast.js +0 -47
  317. /package/server/dist/{ai-invocations.js → modules/accounting/runtime/ai-invocations.js} +0 -0
  318. /package/server/dist/{codex-otel-bridge.js → modules/accounting/runtime/codex-otel-bridge.js} +0 -0
  319. /package/server/dist/{metrics.js → modules/accounting/runtime/metrics.js} +0 -0
  320. /package/server/dist/{telemetry-export.js → modules/accounting/runtime/telemetry-export.js} +0 -0
  321. /package/server/dist/{agent-runtime-accounting.js → modules/agent-runtime/runtime/agent-runtime-accounting.js} +0 -0
  322. /package/server/dist/{agent-runtime-events.js → modules/agent-runtime/runtime/agent-runtime-events.js} +0 -0
  323. /package/server/dist/{agent-runtime-history.js → modules/agent-runtime/runtime/agent-runtime-history.js} +0 -0
  324. /package/server/dist/{agent-runtime-metrics.js → modules/agent-runtime/runtime/agent-runtime-metrics.js} +0 -0
  325. /package/server/dist/{agent-runtime-package.js → modules/agent-runtime/runtime/agent-runtime-package.js} +0 -0
  326. /package/server/dist/{agent-runtime-repositories.js → modules/agent-runtime/runtime/agent-runtime-repositories.js} +0 -0
  327. /package/server/dist/{agent-runtime-verification-suggestions.js → modules/agent-runtime/runtime/agent-runtime-verification-suggestions.js} +0 -0
  328. /package/server/dist/{agent-refine-db.js → modules/agents/runtime/agent-refine-db.js} +0 -0
  329. /package/server/dist/{browser-capture-types.js → modules/browser/runtime/browser-capture-types.js} +0 -0
  330. /package/server/dist/{browser-context-pool.js → modules/browser/runtime/browser-context-pool.js} +0 -0
  331. /package/server/dist/{browser-network.js → modules/browser/runtime/browser-network.js} +0 -0
  332. /package/server/dist/{browser-viewport.js → modules/browser/runtime/browser-viewport.js} +0 -0
  333. /package/server/dist/{blueprint-render.js → modules/builder/runtime/blueprint-render.js} +0 -0
  334. /package/server/dist/{blueprint-spec-fixtures.js → modules/builder/runtime/blueprint-spec-fixtures.js} +0 -0
  335. /package/server/dist/{blueprint-store.js → modules/builder/runtime/blueprint-store.js} +0 -0
  336. /package/server/dist/{blueprint-types.js → modules/builder/runtime/blueprint-types.js} +0 -0
  337. /package/server/dist/{builder-cwd-manager.js → modules/builder/runtime/builder-cwd-manager.js} +0 -0
  338. /package/server/dist/{milestone-chain-store.js → modules/builder/runtime/milestone-chain-store.js} +0 -0
  339. /package/server/dist/{changes-reader.js → modules/code/runtime/changes-reader.js} +0 -0
  340. /package/server/dist/{context-scope.js → modules/conversations/runtime/context-scope.js} +0 -0
  341. /package/server/dist/{explore-contract-refine.js → modules/conversations/runtime/explore-contract-refine.js} +0 -0
  342. /package/server/dist/{explore-draft-title.js → modules/conversations/runtime/explore-draft-title.js} +0 -0
  343. /package/server/dist/{merge-manager.js → modules/delivery/runtime/merge-manager.js} +0 -0
  344. /package/server/dist/{multi-repo-execution-store.js → modules/delivery/runtime/multi-repo-execution-store.js} +0 -0
  345. /package/server/dist/{pr-lifecycle.js → modules/delivery/runtime/pr-lifecycle.js} +0 -0
  346. /package/server/dist/{rail-isolation.js → modules/delivery/runtime/rail-isolation.js} +0 -0
  347. /package/server/dist/{rail-pr-recovery-git.js → modules/delivery/runtime/rail-pr-recovery-git.js} +0 -0
  348. /package/server/dist/{rail-worktrees-store.js → modules/delivery/runtime/rail-worktrees-store.js} +0 -0
  349. /package/server/dist/{rails-store.js → modules/delivery/runtime/rails-store.js} +0 -0
  350. /package/server/dist/{job-listing.js → modules/execution/runtime/job-listing.js} +0 -0
  351. /package/server/dist/{job-spawn-idempotency.js → modules/execution/runtime/job-spawn-idempotency.js} +0 -0
  352. /package/server/dist/{run-duration-stats.js → modules/execution/runtime/run-duration-stats.js} +0 -0
  353. /package/server/dist/{verification-sentinel.js → modules/execution/runtime/verification-sentinel.js} +0 -0
  354. /package/server/dist/{loop-constants.js → modules/loops/runtime/loop-constants.js} +0 -0
  355. /package/server/dist/{loop-decider.js → modules/loops/runtime/loop-decider.js} +0 -0
  356. /package/server/dist/{loop-effect.js → modules/loops/runtime/loop-effect.js} +0 -0
  357. /package/server/dist/{loop-preview.js → modules/loops/runtime/loop-preview.js} +0 -0
  358. /package/server/dist/{loop-templates-ported.js → modules/loops/runtime/loop-templates-ported.js} +0 -0
  359. /package/server/dist/{loops-store.js → modules/loops/runtime/loops-store.js} +0 -0
  360. /package/server/dist/{agent-chat-registry.js → modules/missions/runtime/agent-chat-registry.js} +0 -0
  361. /package/server/dist/{agent-cwd-manager.js → modules/missions/runtime/agent-cwd-manager.js} +0 -0
  362. /package/server/dist/{agent-tier.js → modules/missions/runtime/agent-tier.js} +0 -0
  363. /package/server/dist/{spec-contract-prompt.js → modules/specs/runtime/spec-contract-prompt.js} +0 -0
  364. /package/server/dist/{spec-draft-parser.js → modules/specs/runtime/spec-draft-parser.js} +0 -0
  365. /package/server/dist/{ticket-store.js → modules/specs/runtime/ticket-store.js} +0 -0
  366. /package/server/dist/{terminal-marks-store.js → modules/terminals/runtime/terminal-marks-store.js} +0 -0
  367. /package/server/dist/{terminal-osc-parser.js → modules/terminals/runtime/terminal-osc-parser.js} +0 -0
  368. /package/server/dist/{terminal-settings.js → modules/terminals/runtime/terminal-settings.js} +0 -0
@@ -40,7 +40,7 @@ function buildRevisionSeed(input) {
40
40
  const specs = (input.specSnapshot ?? []).length > 0
41
41
  ? (input.specSnapshot ?? []).map((entry) => [
42
42
  `### #${entry.ticketId} ${entry.title ?? '(untitled)'}`,
43
- entry.description ? clamp(entry.description, exports.SEED_SPEC_BODY_CAP) : '(no description recorded)',
43
+ ...(input.includeSpecBody === false ? [] : [entry.description ? clamp(entry.description, exports.SEED_SPEC_BODY_CAP) : '(no description recorded)']),
44
44
  ].join('\n')).join('\n\n')
45
45
  : input.ticketIds.map((id) => `### #${id}\n(spec text was not captured at launch)`).join('\n\n');
46
46
  return [
@@ -14,9 +14,9 @@
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.runAiCliInvocation = runAiCliInvocation;
16
16
  const node_readline_1 = require("node:readline");
17
- const win_spawn_1 = require("./util/win-spawn");
18
- const cli_prompt_1 = require("./util/cli-prompt");
19
- const runtime_1 = require("./providers/runtime");
17
+ const win_spawn_1 = require("../../../util/win-spawn");
18
+ const cli_prompt_1 = require("../../../util/cli-prompt");
19
+ const runtime_1 = require("../../../providers/runtime");
20
20
  const STDERR_TAIL_CAP = 64 * 1024;
21
21
  /**
22
22
  * Run one AI-CLI invocation: spawn → stream → settle. Resolves exactly once on
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.StuckRunDetector = exports.STUCK_SWEEP_INTERVAL_MS = exports.STUCK_FLOOR_MS = void 0;
4
4
  exports.resolveStuckThresholdMs = resolveStuckThresholdMs;
5
5
  exports.findStuckCandidates = findStuckCandidates;
6
- const loop_runs_store_1 = require("./loop-runs-store");
6
+ const loop_runs_store_1 = require("../../loops/runtime/loop-runs-store");
7
7
  /** Never notify sooner than this, however the threshold is configured. */
8
8
  exports.STUCK_FLOOR_MS = 10 * 60 * 1000;
9
9
  /** How often the sweep runs while the server is up. */
@@ -26,7 +26,7 @@ exports.expandCommands = expandCommands;
26
26
  exports.dominantTicketScope = dominantTicketScope;
27
27
  exports.referencesClaudeOnlyCommand = referencesClaudeOnlyCommand;
28
28
  exports.referencesUnsupportedProviderCommand = referencesUnsupportedProviderCommand;
29
- const registry_1 = require("./providers/registry");
29
+ const registry_1 = require("../../../providers/registry");
30
30
  const loop_constants_1 = require("./loop-constants");
31
31
  /** The autonomous prompt a `native` command (Freestyle) expands to for the
32
32
  * LoopRunManager path. Factory Freestyle loops route to QueueManager's real
@@ -9,30 +9,30 @@ exports.createLoopExecutors = createLoopExecutors;
9
9
  * from coverage; the engine's traversal/decision logic is unit-tested against
10
10
  * fake executors in `loop-run-manager.test.ts`.
11
11
  */
12
- const core_completion_1 = require("./core-completion");
13
- const core_execution_1 = require("./core-execution");
14
- const agent_runtime_bridge_1 = require("./agent-runtime-bridge");
15
- const codex_spawn_1 = require("./plugins/codex-spawn");
12
+ const core_completion_1 = require("../../../core-completion");
13
+ const core_execution_1 = require("../../../core-execution");
14
+ const agent_runtime_bridge_1 = require("../../agent-runtime/runtime/agent-runtime-bridge");
15
+ const codex_spawn_1 = require("../../../plugins/codex-spawn");
16
16
  const node_child_process_1 = require("node:child_process");
17
17
  const node_crypto_1 = require("node:crypto");
18
18
  const node_fs_1 = require("node:fs");
19
19
  const node_path_1 = require("node:path");
20
- const win_spawn_1 = require("./util/win-spawn");
21
- const providers_1 = require("./providers");
22
- const registry_1 = require("./providers/registry");
23
- const workspace_manager_1 = require("./workspace-manager");
24
- const claude_trust_1 = require("./claude-trust");
25
- const spawn_lifecycle_1 = require("./spawn-lifecycle");
26
- const result_event_1 = require("./result-event");
27
- const terminal_result_1 = require("./providers/terminal-result");
28
- const claude_background_tasks_1 = require("./providers/claude-background-tasks");
20
+ const win_spawn_1 = require("../../../util/win-spawn");
21
+ const providers_1 = require("../../../providers");
22
+ const registry_1 = require("../../../providers/registry");
23
+ const workspace_manager_1 = require("../../../workspace-manager");
24
+ const claude_trust_1 = require("../../../claude-trust");
25
+ const spawn_lifecycle_1 = require("../../execution/runtime/spawn-lifecycle");
26
+ const result_event_1 = require("../../accounting/runtime/result-event");
27
+ const terminal_result_1 = require("../../../providers/terminal-result");
28
+ const claude_background_tasks_1 = require("../../../providers/claude-background-tasks");
29
29
  const loop_constants_1 = require("./loop-constants");
30
30
  const loop_decider_1 = require("./loop-decider");
31
- const rail_isolation_1 = require("./rail-isolation");
32
- const repo_map_1 = require("./repo-map");
33
- const feature_flags_1 = require("./feature-flags");
31
+ const rail_isolation_1 = require("../../delivery/runtime/rail-isolation");
32
+ const repo_map_1 = require("../../../repo-map");
33
+ const feature_flags_1 = require("../../../feature-flags");
34
34
  const loop_step_idle_1 = require("./loop-step-idle");
35
- const runtime_1 = require("./providers/runtime");
35
+ const runtime_1 = require("../../../providers/runtime");
36
36
  const loop_shell_invocation_1 = require("./loop-shell-invocation");
37
37
  // Per-step wall-clock caps so a single hung step can't block the engine's
38
38
  // overall deadline check (which only runs between nodes).
@@ -42,7 +42,7 @@ const SDD_QUICK_OPENSPEC_FACTORY = {
42
42
  mode: 'loop',
43
43
  graph: (0, loop_templates_1.opsxLifecycleGraph)(),
44
44
  };
45
- /** The Architect-less revision loop every "ask for a change" launch runs. */
45
+ /** Legacy persisted id. Resolves to Quick SDD; no Revision loop is listed or run. */
46
46
  exports.FACTORY_REVISION_LOOP_ID = 'factory:revision';
47
47
  exports.FACTORY_LOOPS = [
48
48
  {
@@ -70,30 +70,11 @@ exports.FACTORY_LOOPS = [
70
70
  requiredCapability: 'freestyle',
71
71
  graph: (0, loop_templates_1.fixLoopGraph)(['{{cmd:freestyle}}'], GREEN_GOAL, FACTORY_MAX_ITERATIONS, FACTORY_LOOP_TIMEOUT_MIN, FACTORY_AI_STEP_TIMEOUT_MIN),
72
72
  },
73
- {
74
- id: exports.FACTORY_REVISION_LOOP_ID,
75
- name: 'Revision',
76
- // Listed so the platform's behaviour is discoverable, but `launchable:false`:
77
- // the app runs this itself when a user asks for a change to a delivery. Its
78
- // prompt consumes `{{const:REVISION_REQUEST}}`, and an unresolved constant
79
- // renders as an EMPTY string, so a hand-launched run would have a blank
80
- // central instruction. The launch route refuses it; the prompt guards too.
81
- launchable: false,
82
- description: "Apply the ONE change the user asked for on top of work already delivered, then run one independent reviewer-owned verification gate and refine until green. No re-planning. Runs automatically when you ask for changes on a review — it is not started by hand.",
83
- mode: 'loop',
84
- // Revision is the ONLY factory loop that swaps the generic `{{cmd:verify}}`
85
- // gate: `{{cmd:revision-verify}}` owns reviewer re-grading AND the full pass
86
- // of record in ONE read-only step, so the mutator no longer runs sr-reviewer
87
- // itself and no second repository-wide gate follows it. The trailing `true`
88
- // makes verify/fix run in a FRESH provider session — the gate's verdict and
89
- // its confidence artifact then describe the candidate on disk rather than
90
- // the mutator's own account of it.
91
- graph: (0, loop_templates_1.fixLoopGraph)(['{{cmd:revise}}'], GREEN_GOAL, FACTORY_MAX_ITERATIONS, FACTORY_LOOP_TIMEOUT_MIN, FACTORY_AI_STEP_TIMEOUT_MIN, '{{cmd:revision-verify}}', true),
92
- },
93
73
  SDD_QUICK_OPENSPEC_FACTORY,
94
74
  ];
95
75
  const FACTORY_ALIASES = new Map([
96
76
  ['factory:openspec', SDD_QUICK_OPENSPEC_FACTORY],
77
+ [exports.FACTORY_REVISION_LOOP_ID, SDD_QUICK_OPENSPEC_FACTORY],
97
78
  ]);
98
79
  const FACTORY_BY_ID = new Map([
99
80
  ...exports.FACTORY_LOOPS.map((f) => [f.id, f]),
@@ -11,6 +11,7 @@
11
11
  */
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.assertLoopShellRepositoryScope = assertLoopShellRepositoryScope;
14
+ exports.assertLoopFailureRecovery = assertLoopFailureRecovery;
14
15
  exports.emptyLoopGraph = emptyLoopGraph;
15
16
  exports.validateLoopGraph = validateLoopGraph;
16
17
  exports.nodesById = nodesById;
@@ -27,6 +28,24 @@ function assertLoopShellRepositoryScope(graph, repositoryIds) {
27
28
  }
28
29
  }
29
30
  }
31
+ /** Recovery is bounded and may only repeat a phase or repair artifacts before revalidation. */
32
+ function assertLoopFailureRecovery(graph) {
33
+ for (const node of graph.nodes) {
34
+ const recovery = node.data?.failureRecovery;
35
+ if (recovery === undefined)
36
+ continue;
37
+ const target = graph.nodes.find((candidate) => candidate.id === recovery?.target);
38
+ if (!recovery || node.data?.stopOnFailure !== true ||
39
+ !['ai-step', 'shell'].includes(node.type) ||
40
+ recovery.maxRetries !== 1 ||
41
+ (recovery.artifactOnly !== undefined && typeof recovery.artifactOnly !== 'boolean') ||
42
+ !target || (target.id !== node.id &&
43
+ !(node.type === 'shell' && target.type === 'ai-step' && recovery.artifactOnly === true)) ||
44
+ (recovery.artifactOnly === true && (target?.type !== 'ai-step' || target.data?.operation === 'core-implementation' || target.data?.stopOnFailure !== true))) {
45
+ throw new Error(`Invalid failure recovery for step ${node.id}: use one retry of this phase or an AI artifact repair.`);
46
+ }
47
+ }
48
+ }
30
49
  /** A minimal, structurally-valid empty graph (used to seed a new Draft). */
31
50
  function emptyLoopGraph() {
32
51
  return { nodes: [], edges: [], config: { maxIterations: 10, timeoutMinutes: 30 } };
@@ -70,6 +89,12 @@ function validateLoopGraph(graph) {
70
89
  errors.push({ code: 'INVALID_NODE', nodeId: node.id, message: 'A shell repositoryId must be a non-empty registered repository id.' });
71
90
  }
72
91
  }
92
+ try {
93
+ assertLoopFailureRecovery(graph);
94
+ }
95
+ catch (error) {
96
+ errors.push({ code: 'INVALID_NODE', message: error.message });
97
+ }
73
98
  // ── Start cardinality ──────────────────────────────────────────────────────
74
99
  const starts = nodes.filter((n) => n.type === 'start');
75
100
  if (starts.length === 0) {
@@ -29,10 +29,10 @@ exports.resolveLoopRoleEngine = resolveLoopRoleEngine;
29
29
  // `agent-runtime.json`; absent ⇒ every loop role inherits the project's primary.
30
30
  const node_fs_1 = require("node:fs");
31
31
  const node_path_1 = __importDefault(require("node:path"));
32
- const providers_1 = require("./providers");
33
- const spec_models_1 = require("./spec-models");
34
- const workspace_resolution_1 = require("./workspace-resolution");
35
- const provider_selection_1 = require("./provider-selection");
32
+ const providers_1 = require("../../../providers");
33
+ const spec_models_1 = require("../../specs/runtime/spec-models");
34
+ const workspace_resolution_1 = require("../../../workspace-resolution");
35
+ const provider_selection_1 = require("../../../provider-selection");
36
36
  /** The rail engine value meaning "per-role engines, no flattening override". */
37
37
  exports.ROLES_ENGINE = 'roles';
38
38
  exports.LOOP_ROLES = ['verifier', 'decider'];
@@ -10,29 +10,31 @@ exports.seedChangeId = seedChangeId;
10
10
  exports.openspecChangeState = openspecChangeState;
11
11
  exports.extractChangeId = extractChangeId;
12
12
  exports.recoverOrphanLoopStepAccounting = recoverOrphanLoopStepAccounting;
13
+ const crypto_1 = require("crypto");
14
+ const spec_addenda_core_1 = require("../../specs/runtime/spec-addenda-core");
13
15
  const path_1 = __importDefault(require("path"));
14
16
  const fs_1 = __importDefault(require("fs"));
15
- const agent_runtime_events_1 = require("./agent-runtime-events");
16
- const agent_runtime_settings_1 = require("./agent-runtime-settings");
17
- const multi_repo_execution_store_1 = require("./multi-repo-execution-store");
18
- const win_spawn_1 = require("./util/win-spawn");
19
- const db_1 = require("./db");
20
- const interactive_job_session_1 = require("./interactive-job-session");
17
+ const agent_runtime_events_1 = require("../../agent-runtime/runtime/agent-runtime-events");
18
+ const agent_runtime_settings_1 = require("../../agent-runtime/runtime/agent-runtime-settings");
19
+ const multi_repo_execution_store_1 = require("../../delivery/runtime/multi-repo-execution-store");
20
+ const win_spawn_1 = require("../../../util/win-spawn");
21
+ const db_1 = require("../../../db");
22
+ const interactive_job_session_1 = require("../../execution/runtime/interactive-job-session");
21
23
  const loop_graph_1 = require("./loop-graph");
22
24
  const loop_command_catalog_1 = require("./loop-command-catalog");
23
25
  const loop_constants_1 = require("./loop-constants");
24
26
  const loop_step_idle_1 = require("./loop-step-idle");
25
27
  const loop_decider_1 = require("./loop-decider");
26
28
  const loop_runs_store_1 = require("./loop-runs-store");
27
- const ai_invocations_1 = require("./ai-invocations");
28
- const provider_limit_1 = require("./provider-limit");
29
- const ids_1 = require("./ids");
30
- const providers_1 = require("./providers");
31
- const agent_runtime_accounting_1 = require("./agent-runtime-accounting");
32
- const runtime_1 = require("./providers/runtime");
33
- const result_event_1 = require("./result-event");
34
- const rails_store_1 = require("./rails-store");
35
- const verification_sentinel_1 = require("./verification-sentinel");
29
+ const ai_invocations_1 = require("../../accounting/runtime/ai-invocations");
30
+ const provider_limit_1 = require("../../../provider-limit");
31
+ const ids_1 = require("../../../ids");
32
+ const providers_1 = require("../../../providers");
33
+ const agent_runtime_accounting_1 = require("../../agent-runtime/runtime/agent-runtime-accounting");
34
+ const runtime_1 = require("../../../providers/runtime");
35
+ const result_event_1 = require("../../accounting/runtime/result-event");
36
+ const rails_store_1 = require("../../delivery/runtime/rails-store");
37
+ const verification_sentinel_1 = require("../../execution/runtime/verification-sentinel");
36
38
  const IMPLEMENT_CMD_TOKEN_RE = /\{\{\s*cmd:(?:implement|batch)\s*\}\}/;
37
39
  const IMPLEMENT_CMD_TEXT_RE = /(?:^|\s)(?:\/(?:specrails:|sr:)?(?:implement|batch-implement)|\$(?:implement|batch-implement))\b/;
38
40
  const LOOP_BLOCKED_LINE_RE = /(?:^|\n)\s*LOOP_BLOCKED:\s*(.+?)(?:\n|$)/i;
@@ -141,9 +143,20 @@ function resolveRunVars(text, vars) {
141
143
  return text.replace(RUN_TOKEN_RE, (_m, key) => vars[key] ?? '');
142
144
  }
143
145
  const CHANGE_NAME_RE = /^[a-z0-9][a-z0-9._-]{0,99}$/i;
144
- /** The durable OpenSpec change name a launch already carries, if any: the
145
- * follow-up's declared name wins, else the spec's `openspecChangeName`. */
146
+ /** The authoritative OpenSpec target for a launch.
147
+ * Addenda/delivery deltas get a run-specific name; ordinary launches use the
148
+ * follow-up's declared name, else the spec's `openspecChangeName`. */
146
149
  function seedChangeId(req) {
150
+ if (req.addenda?.ids.length) {
151
+ // A new generation never picks an old/archived proposal, even when the
152
+ // ticket metadata still names it. Resuming the same run keeps its target.
153
+ const hash = (0, crypto_1.createHash)('sha256').update(JSON.stringify([req.runId, req.addenda.ids, req.addenda.briefing])).digest('hex').slice(0, 20);
154
+ return { id: `spec-addenda-${hash}`, source: 'addenda' };
155
+ }
156
+ if (req.constants?.REVISION_REQUEST) {
157
+ const hash = (0, crypto_1.createHash)('sha256').update(JSON.stringify([req.runId, req.constants.REVISION_REQUEST])).digest('hex').slice(0, 20);
158
+ return { id: `delivery-change-${hash}`, source: 'revision' };
159
+ }
147
160
  const fromFollowUp = typeof req.followUp?.openspecChangeName === 'string' ? req.followUp.openspecChangeName.trim() : '';
148
161
  if (fromFollowUp && CHANGE_NAME_RE.test(fromFollowUp))
149
162
  return { id: fromFollowUp, source: 'followUp' };
@@ -605,6 +618,13 @@ class LoopRunManager {
605
618
  // Later fresh steps must verify the admitted spec, even if its caller or
606
619
  // backlog changes while the implementation is running.
607
620
  req = { ...req, runtimeProviderOverride: (0, agent_runtime_settings_1.validateRuntimeProviderOverride)(req.runtimeProviderOverride), graph: structuredClone(req.graph) };
621
+ (0, loop_graph_1.assertLoopFailureRecovery)(req.graph);
622
+ if (req.addenda)
623
+ req = { ...req, addenda: structuredClone(req.addenda) };
624
+ if (req.followUp)
625
+ req = { ...req, followUp: structuredClone(req.followUp) };
626
+ if (req.constants)
627
+ req = { ...req, constants: structuredClone(req.constants) };
608
628
  if (req.spec)
609
629
  req = { ...req, spec: structuredClone(req.spec) };
610
630
  if (req.executionManifest)
@@ -902,7 +922,7 @@ class LoopRunManager {
902
922
  // change in one go (run 644eb404) never mentioned an active path, so the
903
923
  // validate/archive shell steps refused to run and a green implementation
904
924
  // settled `failed`. A step's own capture still wins when nothing is seeded.
905
- const seededChangeId = seedChangeId(req);
925
+ const seededChangeId = seedChangeId({ ...req, runId });
906
926
  if (seededChangeId) {
907
927
  runVars.changeId = seededChangeId.id;
908
928
  logLine(`↪ OpenSpec change id seeded from ${seededChangeId.source}: ${seededChangeId.id}`);
@@ -1087,6 +1107,27 @@ class LoopRunManager {
1087
1107
  });
1088
1108
  return decision;
1089
1109
  };
1110
+ const recoveryCounts = new Map();
1111
+ let phaseRecovery;
1112
+ const recoverPhase = (node, detail, previousFailure) => {
1113
+ const policy = node.data?.failureRecovery;
1114
+ if (!policy || phaseRecovery || this._cancelled.has(runId) ||
1115
+ (recoveryCounts.get(node.id) ?? 0) >= policy.maxRetries)
1116
+ return undefined;
1117
+ recoveryCounts.set(node.id, (recoveryCounts.get(node.id) ?? 0) + 1);
1118
+ phaseRecovery = { failedId: node.id, target: policy.target, detail: truncate(detail, 4000), artifactOnly: policy.artifactOnly === true };
1119
+ passFailed = previousFailure;
1120
+ emitRunEvent('loop_phase_recovery', { nodeId: node.id, target: policy.target, attempt: recoveryCounts.get(node.id) });
1121
+ logLine(`↪ Recovering phase ${node.id} once; preserving completed phases and the current change.`);
1122
+ return policy.target;
1123
+ };
1124
+ const afterPhase = (nodeId, successor) => {
1125
+ if (phaseRecovery?.target !== nodeId)
1126
+ return successor;
1127
+ const failedId = phaseRecovery.failedId;
1128
+ phaseRecovery = undefined;
1129
+ return failedId === nodeId ? successor : failedId;
1130
+ };
1090
1131
  const firstStepId = start ? req.graph.edges.find((e) => e.source === start.id)?.target : undefined;
1091
1132
  try {
1092
1133
  let nodeId = start?.id;
@@ -1144,16 +1185,24 @@ class LoopRunManager {
1144
1185
  // NATIVE per-provider invocation (claude `/specrails:implement #<id>
1145
1186
  // --yes`, codex `$implement #<id> --yes`) — then resolve `{{spec.*}}`
1146
1187
  // data tokens and finally `{{const:*}}` library constants.
1147
- const rawTemplate = String(node.data?.prompt ?? '');
1188
+ const priorPhaseFailure = passFailed;
1189
+ const repair = phaseRecovery?.target === node.id ? phaseRecovery : undefined;
1190
+ const rawTemplate = repair?.artifactOnly
1191
+ ? 'Repair ONLY the OpenSpec artifacts at openspec/changes/{{run.changeId}}/ using the validation diagnostics below. Preserve requirements and acceptance criteria. Do not edit implementation code, run repository tests, archive, create a new change, or expand scope. {{const:GUARDRAILS}}'
1192
+ : String(node.data?.prompt ?? '');
1148
1193
  const requiresCoreCompletion = node.data?.operation === 'core-implementation' || /\{\{\s*cmd:(?:implement|batch)\s*\}\}/.test(rawTemplate)
1149
1194
  || /^\s*(?:\/specrails:|\/skill:specrails-|\$)(?:implement|batch-implement)(?:\s|$)/.test(rawTemplate);
1150
- const expanded = (0, loop_constants_1.resolveConstants)(resolveRunVars((0, loop_graph_1.interpolateSpec)((0, loop_command_catalog_1.expandCommands)(rawTemplate, { provider: nodeProvider, ticketIds: req.spec?.ticketIds, specId: req.spec?.id }), req.spec), runVars), constMap);
1195
+ const expanded = (0, loop_constants_1.resolveConstants)(resolveRunVars((0, loop_graph_1.interpolateSpec)((0, loop_command_catalog_1.expandCommands)(rawTemplate, { provider: nodeProvider, ticketIds: req.spec?.ticketIds, specId: req.spec?.id }), seededChangeId && ['addenda', 'revision'].includes(seededChangeId.source)
1196
+ ? { ...req.spec, openspecChangeName: seededChangeId.id } : req.spec), runVars), constMap);
1151
1197
  // The follow-up briefing is APPENDED (after any slash command, which
1152
1198
  // must lead the message) so it becomes part of the command's
1153
1199
  // arguments for CLI providers and plain prompt text for the runner.
1154
1200
  // The spec-addenda briefing rides the same slot: appended to every
1155
1201
  // step so no phase can miss the iteration delta.
1156
- const base = [(0, multi_repo_execution_store_1.executionManifestPrompt)(req.executionManifest), withReviewContinuationContext(expanded, rawTemplate, req.spec), req.followUp?.briefing, req.addenda?.briefing].filter(Boolean).join('\n\n');
1202
+ const deltaTarget = seededChangeId && ['addenda', 'revision'].includes(seededChangeId.source)
1203
+ ? `DELTA OPENSPEC TARGET: ${seededChangeId.id}. Use ONLY openspec/changes/${seededChangeId.id}/. Create it if missing; the seeded name does not imply artifacts exist. Ignore the original spec/follow-up change name and unrelated active or archived changes. Inspect the current branch first. For delivered work, prepare artifacts and tasks ONLY for the attached addenda or delivery change request and preserve existing behavior outside this delta. For a spec with no delivered work, the scope is the full spec together with its addenda. Map each requested change to acceptance checks. Do not redo or archive the original proposal. Perform only the current phase on this exact change: preparation/repair edits artifacts only; implementation applies and verifies. The implementation phase must report every attached addendum with concrete files and test evidence before VERIFICATION: PASS.`
1204
+ : undefined;
1205
+ const base = [(0, multi_repo_execution_store_1.executionManifestPrompt)(req.executionManifest), withReviewContinuationContext(expanded, rawTemplate, req.spec), repair ? `PHASE RECOVERY: keep the same change and completed work. Correct only the remaining failure; do not repeat preparation or unaffected passing checks. Prior failure (diagnostic data):\n${repair.detail}` : undefined, req.followUp?.briefing, req.constants?.REVISION_REQUEST, deltaTarget, req.addenda?.briefing].filter(Boolean).join('\n\n');
1157
1206
  // Inject the cross-iteration history only when there's no live session
1158
1207
  // to carry it (a fresh pass) OR right after a Decider 'continue' (so the
1159
1208
  // step sees the verdict). A mid-body resumed step already has it.
@@ -1330,7 +1379,11 @@ class LoopRunManager {
1330
1379
  const blockedReason = aiStepBlockedReason(res.text);
1331
1380
  const requiresVerification = node.data?.requireVerificationPass === true
1332
1381
  || /\{\{cmd:(?:verify|revision-verify|opsx:verify)\}\}/.test(rawTemplate);
1333
- const verificationFailed = requiresVerification
1382
+ const addendaReport = requiresVerification && req.addenda?.ids.length
1383
+ ? (0, spec_addenda_core_1.parseSpecAddendaReport)(res.text, req.addenda.ids.map((id) => ({ id }))) : null;
1384
+ const addendaFailed = Boolean(requiresVerification && req.addenda?.ids.length
1385
+ && req.addenda.ids.some((id) => !addendaReport?.some((line) => line.addendumId === id && line.verdict === 'applied' && line.files && line.tests)));
1386
+ const verificationFailed = addendaFailed || requiresVerification
1334
1387
  && (0, verification_sentinel_1.parseVerificationSentinel)(res.text).verdict !== 'pass';
1335
1388
  const stepFailed = res.failed === true || zeroWork || blockedReason !== null || verificationFailed;
1336
1389
  passFailed ||= res.failed === true || zeroWork || (verificationFailed && !blockedReason);
@@ -1339,7 +1392,7 @@ class LoopRunManager {
1339
1392
  : blockedReason
1340
1393
  ? `blocked — ${blockedReason}`
1341
1394
  : verificationFailed
1342
- ? 'verification did not finish with VERIFICATION: PASS'
1395
+ ? addendaFailed ? 'addenda verification incomplete: every addendum requires an applied report with files and tests' : 'verification did not finish with VERIFICATION: PASS'
1343
1396
  : res.failed ? 'provider invocation failed without a reported reason' : undefined);
1344
1397
  // Make the failure reason land visibly INSIDE the step's log
1345
1398
  // segment (the interactive session already surfaced its own note at
@@ -1364,9 +1417,24 @@ class LoopRunManager {
1364
1417
  // session — plus zero-work); no exit code is exposed by the AI
1365
1418
  // executors → null.
1366
1419
  emitStepEnd({ status: stepFailed ? 'failed' : 'ok', durationMs: res.durationMs });
1420
+ if (req.graph.nodes.some((candidate) => candidate.data?.failureRecovery)) {
1421
+ emitRunEvent('loop_phase_metrics', {
1422
+ nodeId: node.id, stepIndex: stepNum, usageScope: 'final-attempt', recovery: !!repair, promptChars: prompt.length, outputChars: res.text.length,
1423
+ durationMs: res.durationMs ?? null, costUsd: res.cost ?? null, estimated: res.estimated ?? null,
1424
+ tokensIn: res.tokensIn ?? null, tokensOut: res.tokensOut ?? null,
1425
+ tokensCacheRead: res.tokensCacheRead ?? null, tokensCacheCreate: res.tokensCacheCreate ?? null,
1426
+ });
1427
+ }
1367
1428
  // Linear workflows may forbid later actions (such as archive) after
1368
1429
  // a failed step. Verify/fix loops keep their existing recovery path.
1369
1430
  if (node.data?.stopOnFailure === true && stepFailed) {
1431
+ const retryTarget = !res.failed && !res.resultIsError && !zeroWork && !blockedReason && verificationFailed
1432
+ ? recoverPhase(node, `${stepErrorText}\n${res.text}`, priorPhaseFailure) : undefined;
1433
+ if (retryTarget) {
1434
+ aiSessionId = res.sessionId;
1435
+ nodeId = retryTarget;
1436
+ break;
1437
+ }
1370
1438
  outcome = 'failed';
1371
1439
  settled = true;
1372
1440
  break;
@@ -1426,10 +1494,11 @@ class LoopRunManager {
1426
1494
  else {
1427
1495
  consecutiveAiFailures = 0;
1428
1496
  }
1429
- nodeId = succs[0]?.id;
1497
+ nodeId = afterPhase(node.id, succs[0]?.id);
1430
1498
  break;
1431
1499
  }
1432
1500
  case 'shell': {
1501
+ const priorPhaseFailure = passFailed;
1433
1502
  // Guard: refuse to run when a declared run-variable was never captured
1434
1503
  // (e.g. an archive node whose `{{run.changeId}}` is empty) — running
1435
1504
  // `openspec archive -y` against an unknown change would archive the
@@ -1461,7 +1530,7 @@ class LoopRunManager {
1461
1530
  && openspecChangeState(shellCwd, runVars.changeId) === 'archived') {
1462
1531
  logLine(`Skipped: OpenSpec change ${runVars.changeId} is already archived (openspec/changes/archive) — nothing left to ${/\barchive\b/.test(rawCommand) ? 'archive' : 'validate'}.`);
1463
1532
  emitStepEnd({ status: 'ok', exitCode: 0, durationMs: 0 });
1464
- nodeId = succs[0]?.id;
1533
+ nodeId = afterPhase(node.id, succs[0]?.id);
1465
1534
  break;
1466
1535
  }
1467
1536
  logLine(`$ ${command}`);
@@ -1481,11 +1550,17 @@ class LoopRunManager {
1481
1550
  });
1482
1551
  history.push(`Shell \`${command}\` exit=${sh.exitCode}: ${truncate(sh.stdout || sh.stderr)}`);
1483
1552
  if (node.data?.stopOnFailure === true && sh.exitCode !== 0) {
1553
+ const retryTarget = sh.exitCode === 1
1554
+ ? recoverPhase(node, `${command}\n${sh.stdout}\n${sh.stderr}`, priorPhaseFailure) : undefined;
1555
+ if (retryTarget) {
1556
+ nodeId = retryTarget;
1557
+ break;
1558
+ }
1484
1559
  outcome = 'failed';
1485
1560
  settled = true;
1486
1561
  break;
1487
1562
  }
1488
- nodeId = succs[0]?.id;
1563
+ nodeId = afterPhase(node.id, succs[0]?.id);
1489
1564
  break;
1490
1565
  }
1491
1566
  case 'decider': {
@@ -260,7 +260,7 @@ function getLoopRun(db, id) {
260
260
  return db.prepare('SELECT * FROM loop_runs WHERE id = ?').get(id);
261
261
  }
262
262
  /** Activity-step count for one event, mirroring the client's deriveFrameActivity
263
- * (../../client/src/lib/frame-activity.ts) so the seeded count matches the live
263
+ * (../../client/src/features/browser/lib/frame-activity.ts) so the seeded count matches the live
264
264
  * "pasos" the Job panel shows. A single assistant frame may carry several
265
265
  * parallel tool_use blocks → each counts. Non-activity events → 0. */
266
266
  function activityStepCount(eventType, payload) {
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.bundledLoopShellInvocation = bundledLoopShellInvocation;
4
- const bundled_openspec_1 = require("./bundled-openspec");
5
- const path_resolver_1 = require("./path-resolver");
4
+ const bundled_openspec_1 = require("../../../bundled-openspec");
5
+ const path_resolver_1 = require("../../../path-resolver");
6
6
  /** Resolve the exact built-in archive operation without requiring a global CLI. */
7
7
  function bundledLoopShellInvocation(command) {
8
8
  const match = /^openspec archive ([A-Za-z0-9][A-Za-z0-9._-]*) -y$/.exec(command);
@@ -19,7 +19,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.AI_STEP_STALLED_ERROR = exports.LOOP_STEP_IDLE_DEFAULT_MS = void 0;
20
20
  exports._resetLoopStepIdleWarningForTests = _resetLoopStepIdleWarningForTests;
21
21
  exports.resolveLoopStepIdleTimeoutMs = resolveLoopStepIdleTimeoutMs;
22
- const stuck_run_detector_1 = require("./stuck-run-detector");
22
+ const stuck_run_detector_1 = require("../../execution/runtime/stuck-run-detector");
23
23
  exports.LOOP_STEP_IDLE_DEFAULT_MS = 30 * 60 * 1000;
24
24
  /** Error text the executors/engine attach to a stalled step result. */
25
25
  exports.AI_STEP_STALLED_ERROR = 'AI step stalled';
@@ -128,56 +128,51 @@ function fixLoopGraph(mainPrompts, deciderGoal, maxIterations = 12, timeoutMinut
128
128
  return { nodes, edges, config: { maxIterations, timeoutMinutes, ...(aiStepTimeoutMinutes != null ? { aiStepTimeoutMinutes } : {}) } };
129
129
  }
130
130
  // ── OpenSpec lifecycle loop ──────────────────────────────────────────────────
131
- // Lightweight lifecycle: prepare → apply/test → validate artifacts → archive.
131
+ // Lightweight lifecycle: prepare → preflight → apply/test → validate artifacts → archive.
132
132
  // CLI validation checks OpenSpec artifacts; Apply owns code tests and corrections.
133
133
  const OPSX_FF_PROMPT = [
134
134
  '{{cmd:opsx:ff}} {{spec.title}}',
135
- '',
136
135
  '{{spec.description}}',
137
- '',
138
- 'Structured OpenSpec target from the local ticket metadata: "{{spec.openspecChangeName}}". If this value is non-blank, CONTINUE that exact OpenSpec change and do NOT create a duplicate change for the same follow-up. If it is blank and no run change id exists, create a new OpenSpec change from this ticket and generate all required artifacts.',
139
- '',
140
- 'If a run change id appears here — "{{run.changeId}}" — an OpenSpec change for this ticket already exists: CONTINUE that change (do NOT create a new one) and complete its remaining work.',
141
- '',
142
- 'OpenSpec artifacts are authoritative. If the requested implementation changes requirements, acceptance criteria, design decisions, APIs, states, data models, or invariants, amend the relevant OpenSpec artifacts before any code changes.',
143
- '',
144
- 'Do NOT run `openspec archive` in this step, even if the implementation is complete: this loop validates and archives the change itself in later CLI steps, and an archived change makes those steps fail. Leave the change ACTIVE under openspec/changes/<name>/ and name that path in your final reply.',
145
- '',
146
- 'Run fully unattended: make reasonable decisions to keep momentum and NEVER stop to ask — there is no human to answer. When something is unclear, pick the most sensible option, proceed, and note the assumption.',
147
- ].join('\n');
136
+ 'Target: "{{run.changeId}}" takes precedence over ticket metadata "{{spec.openspecChangeName}}". CONTINUE that exact OpenSpec change if active; create it if missing. If both are blank, choose a new name. Do not select unrelated changes or create a duplicate.',
137
+ 'Preparation ONLY: inspect the affected code and generate the required proposal/specs/design/tasks. Do not implement code or run the repository test suite in this phase.',
138
+ 'OpenSpec artifacts are authoritative: map the full requested spec (or each delta/addendum on delivered work) to tasks and an observable acceptance check. Keep artifacts proportional to the change; reference unchanged contracts instead of rewriting them. Do not invent extra work.',
139
+ 'Do NOT run `openspec archive` in this step. Leave the change ACTIVE and name that path in your final reply. Return a concise handoff: change path, affected files, decisions, and checks for Apply; do not repeat the spec or addendum bodies.',
140
+ 'Work unattended within the requested scope. Record assumptions; report a genuine blocker instead of expanding scope.',
141
+ '{{const:GUARDRAILS}}',
142
+ ].join('\n\n');
148
143
  const OPSX_APPLY_PROMPT = [
149
- '{{cmd:opsx:apply}}',
150
- '',
151
- 'Implement every pending task of the active OpenSpec change for ticket "{{spec.title}}", editing code as needed and marking tasks complete as you finish them.',
152
- '',
153
- 'Before editing code, confirm the active OpenSpec artifacts already describe the contract being implemented. If implementation requires changing requirements, acceptance criteria, design decisions, APIs, states, data models, or invariants, stop code work and amend the OpenSpec artifacts first.',
154
- '',
155
- 'Run the relevant tests and configured checks for the changed behavior, correct failures, and confirm every required task and acceptance criterion is implemented. This step owns code verification; the later CLI validate only checks OpenSpec artifacts. Do not archive here — never run `openspec archive`; the loop archives the change in a later CLI step.',
144
+ '{{cmd:opsx:apply}} {{run.changeId}}',
145
+ 'Implement the pending tasks of this exact change for "{{spec.title}}". Inspect the diff and tasks first; preserve completed work and keep edits within the requested scope.',
146
+ 'Before editing code, confirm the artifacts describe the requested contract; amend the OpenSpec artifacts first if needed. Do not re-plan the original feature.',
147
+ 'Run the relevant tests for each requested behavior and all repository-required checks. Expand testing for affected contracts or failures. Once checks pass, repeat them only after changes that could invalidate them; do not run unrelated optional suites.',
148
+ 'Inspect the final diff for omissions and regressions, mark completed tasks, and report concrete file/check evidence for every attached addendum. CLI validation checks artifacts, not implementation. Do not archive here — never run `openspec archive`.',
156
149
  'Finish with exactly `{{const:VERIFICATION_PASS}}` only after implementation and relevant checks succeed. Otherwise finish with `{{const:VERIFICATION_FAIL}} — <remaining work or failed checks>`.',
157
- 'Run fully unattended: decide and keep momentum, never pause to ask. If you hit an ambiguity or blocker, make the most reasonable choice, implement it, and continue.',
158
- ].join('\n');
150
+ '{{const:GUARDRAILS}}',
151
+ ].join('\n\n');
159
152
  /** The OpenSpec-lifecycle graph (see comment above). Exported for unit testing. */
160
153
  function opsxLifecycleGraph() {
161
154
  return {
162
155
  nodes: [
163
156
  { id: 'start', type: 'start', position: { x: COL_X, y: 0 } },
164
- { id: 'ff', type: 'ai-step', position: { x: COL_X, y: ROW_GAP * 1 }, data: { label: 'opsx:ff', prompt: OPSX_FF_PROMPT } },
165
- { id: 'apply', type: 'ai-step', position: { x: COL_X, y: ROW_GAP * 2 }, data: { label: 'opsx:apply', prompt: OPSX_APPLY_PROMPT, requireVerificationPass: true, stopOnFailure: true } },
166
- { id: 'validate', type: 'shell', position: { x: COL_X, y: ROW_GAP * 3 }, data: { label: 'validate', stopOnFailure: true, command: 'openspec validate {{run.changeId}} --type change --strict --no-interactive', requireRunVars: ['changeId'] } },
157
+ { id: 'ff', type: 'ai-step', position: { x: COL_X, y: ROW_GAP * 1 }, data: { label: 'opsx:ff', prompt: OPSX_FF_PROMPT, stopOnFailure: true } },
158
+ { id: 'preflight', type: 'shell', position: { x: COL_X, y: ROW_GAP * 2 }, data: { label: 'validate artifacts before implementation', stopOnFailure: true, command: 'openspec validate {{run.changeId}} --type change --strict --no-interactive', requireRunVars: ['changeId'], failureRecovery: { target: 'ff', maxRetries: 1, artifactOnly: true } } },
159
+ { id: 'apply', type: 'ai-step', position: { x: COL_X, y: ROW_GAP * 3 }, data: { label: 'opsx:apply', prompt: OPSX_APPLY_PROMPT, requireVerificationPass: true, stopOnFailure: true, failureRecovery: { target: 'apply', maxRetries: 1 } } },
160
+ { id: 'validate', type: 'shell', position: { x: COL_X, y: ROW_GAP * 4 }, data: { label: 'validate', stopOnFailure: true, command: 'openspec validate {{run.changeId}} --type change --strict --no-interactive', requireRunVars: ['changeId'], failureRecovery: { target: 'ff', maxRetries: 1, artifactOnly: true } } },
167
161
  // Unattended archive: deterministic CLI, no AI, no prompt. `requireRunVars`
168
162
  // makes the engine REFUSE to run if no change id was captured (never archive
169
163
  // an unknown change); `openspec archive -y` syncs the main specs by default.
170
- { id: 'archive', type: 'shell', position: { x: COL_X, y: ROW_GAP * 4 }, data: { label: 'archive', command: 'openspec archive {{run.changeId}} -y', requireRunVars: ['changeId'] } },
171
- { id: 'done', type: 'end', position: { x: COL_X, y: ROW_GAP * 5 }, data: { outcome: 'success' } },
164
+ { id: 'archive', type: 'shell', position: { x: COL_X, y: ROW_GAP * 5 }, data: { label: 'archive', stopOnFailure: true, failureRecovery: { target: 'archive', maxRetries: 1 }, command: 'openspec archive {{run.changeId}} -y', requireRunVars: ['changeId'] } },
165
+ { id: 'done', type: 'end', position: { x: COL_X, y: ROW_GAP * 6 }, data: { outcome: 'success' } },
172
166
  ],
173
167
  edges: [
174
168
  { id: 'e-start', source: 'start', target: 'ff' },
175
- { id: 'e-ff', source: 'ff', target: 'apply' },
169
+ { id: 'e-ff', source: 'ff', target: 'preflight' },
170
+ { id: 'e-preflight', source: 'preflight', target: 'apply' },
176
171
  { id: 'e-apply', source: 'apply', target: 'validate' },
177
172
  { id: 'e-validate', source: 'validate', target: 'archive' },
178
173
  { id: 'e-archive', source: 'archive', target: 'done' },
179
174
  ],
180
- // This linear lifecycle has no retry cycle. Like the other built-ins,
175
+ // Phase recovery is bounded per failing node, without an AI Decider. Like the other built-ins,
181
176
  // the run is UNTIMED (0 = no timeout) — apply can
182
177
  // implement a whole change and must never be killed mid-flight.
183
178
  config: { maxIterations: 3, timeoutMinutes: 0, aiStepTimeoutMinutes: 0 },
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.registerLoopsRoutes = registerLoopsRoutes;
4
- const ids_1 = require("./ids");
5
- const feature_flags_1 = require("./feature-flags");
4
+ const ids_1 = require("../../../ids");
5
+ const feature_flags_1 = require("../../../feature-flags");
6
6
  const loops_store_1 = require("./loops-store");
7
7
  const loop_templates_1 = require("./loop-templates");
8
8
  const loop_factory_1 = require("./loop-factory");