specrails-desktop 2.36.0 → 2.42.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -247
- package/client/dist/assets/ActivityFeedPage-DoEMumQS.js +1 -0
- package/client/dist/assets/AgentBrowserCapture-DIh4K0gh.js +1 -0
- package/client/dist/assets/AgentModeAnalyticsPane-mbq4WEoO.js +2 -0
- package/client/dist/assets/AgentModeCodePane-CKh7zuA4.js +2 -0
- package/client/dist/assets/AgentModeJobsPane-BDokPiLJ.js +1 -0
- package/client/dist/assets/AgentsPage-BFVAZbBj.js +87 -0
- package/client/dist/assets/AnalyticsPage-3Y9LuWwz.js +1 -0
- package/client/dist/assets/CodePage-C9KyuV_C.js +3 -0
- package/client/dist/assets/DesktopAnalyticsPage-Bhi54bXQ.js +1 -0
- package/client/dist/assets/{DocsDialog-C76VdBJC.js → DocsDialog-Dj6mt4ga.js} +2 -2
- package/client/dist/assets/{DocsPage-ziMlj6B6.js → DocsPage-24yWglNI.js} +1 -1
- package/client/dist/assets/ExportDropdown-ophapHrT.js +1 -0
- package/client/dist/assets/InteractiveJobComposer-C1ucmxQh.js +19 -0
- package/client/dist/assets/JobDetailModal-DNWmgVHD.js +1 -0
- package/client/dist/assets/JobDetailPage-BcajZyuu.js +1 -0
- package/client/dist/assets/JobsPage-B9nTZw6e.js +1 -0
- package/client/dist/assets/LoopBuilderPage-Oc48iOFW.js +7 -0
- package/client/dist/assets/LoopPreviewModal-ae0Fr6SU.js +1 -0
- package/client/dist/assets/LoopsPage-CUEqzUBI.js +1 -0
- package/client/dist/assets/MinimizedChatsContext-Deg39peL.js +1 -0
- package/client/dist/assets/PluginsPage-C1xtARIx.js +2 -0
- package/client/dist/assets/ProjectSettingsDialog-JZzrXFBN.js +1 -0
- package/client/dist/assets/RepositoryDeliveries-CSDsiVUz.js +5 -0
- package/client/dist/assets/RepositoryScopeSelector-D-RCaknl.js +1 -0
- package/client/dist/assets/ReviewPacketPage-x5EfGTiL.js +1 -0
- package/client/dist/assets/TemplatePreviewModal-BSyuZxGq.js +1 -0
- package/client/dist/assets/TicketDetailModalContext-DqyF0so-.js +14 -0
- package/client/dist/assets/Trans-SnMX4MhW.js +1 -0
- package/client/dist/assets/agent-BdT0SB1D.js +1 -0
- package/client/dist/assets/agent-Bh9K7iEt.js +1 -0
- package/client/dist/assets/agent-C4ibdFk9.js +1 -0
- package/client/dist/assets/agent-CSFY22Zo.js +1 -0
- package/client/dist/assets/agent-DAnqxUn8.js +1 -0
- package/client/dist/assets/agent-DTQiu0Mi.js +1 -0
- package/client/dist/assets/agent-T54WuR_Y.js +1 -0
- package/client/dist/assets/agent-i75WDZOA.js +1 -0
- package/client/dist/assets/browser-05vRDg_y.js +1 -0
- package/client/dist/assets/browser-Bulb-cB1.js +1 -0
- package/client/dist/assets/browser-C7f-Jsg1.js +1 -0
- package/client/dist/assets/browser-CG0PE1j_.js +1 -0
- package/client/dist/assets/browser-CMzaqAjo.js +1 -0
- package/client/dist/assets/browser-DMajYdNX.js +1 -0
- package/client/dist/assets/browser-DxcOHrdP.js +1 -0
- package/client/dist/assets/browser-ObE7xVo3.js +1 -0
- package/client/dist/assets/builder-765NeYTp.js +1 -0
- package/client/dist/assets/builder-8MG3V6ea.js +1 -0
- package/client/dist/assets/builder-B-1UBs5K.js +1 -0
- package/client/dist/assets/builder-BTiGnPT0.js +1 -0
- package/client/dist/assets/builder-Cx9Rz-7u.js +1 -0
- package/client/dist/assets/builder-DKFMELBl.js +1 -0
- package/client/dist/assets/builder-OcmKrC0h.js +1 -0
- package/client/dist/assets/builder-P9qVBXFZ.js +1 -0
- package/client/dist/assets/{button-CJqFnUOL.js → button-DCBqEaZl.js} +1 -1
- package/client/dist/assets/{code-CkUuW4m9.js → code-B37GRg9C.js} +1 -1
- package/client/dist/assets/{code-B0O8-p8k.js → code-BBOEZbf9.js} +1 -1
- package/client/dist/assets/{code-B1VLZDhS.js → code-BRm-jcyx.js} +1 -1
- package/client/dist/assets/{code-Bpj4Ddml.js → code-CF7w62CR.js} +1 -1
- package/client/dist/assets/{code-BykBZ5-e.js → code-CLuvJt2r.js} +1 -1
- package/client/dist/assets/{code-B86IMAfz.js → code-D0RCm59f.js} +1 -1
- package/client/dist/assets/code-DaZ-ZwCT.js +1 -0
- package/client/dist/assets/{code-cAqaqMTx.js → code-iIHkGaCL.js} +1 -1
- package/client/dist/assets/commands-0XjAftla.js +1 -0
- package/client/dist/assets/commands-BAWSYHjQ.js +1 -0
- package/client/dist/assets/commands-BBwowgiE.js +1 -0
- package/client/dist/assets/commands-BacjIZLN.js +1 -0
- package/client/dist/assets/commands-Bs62IAZ3.js +1 -0
- package/client/dist/assets/commands-CLPdh-Hh.js +1 -0
- package/client/dist/assets/commands-DlbFzgrp.js +1 -0
- package/client/dist/assets/commands-LjPSsi5F.js +1 -0
- package/client/dist/assets/common-B5AZNYMH.js +1 -0
- package/client/dist/assets/common-BFZ0c_vN.js +1 -0
- package/client/dist/assets/common-BrlwEnL7.js +1 -0
- package/client/dist/assets/common-BttQx3z4.js +1 -0
- package/client/dist/assets/common-CM65k_-e.js +1 -0
- package/client/dist/assets/common-D4mvFFlu.js +1 -0
- package/client/dist/assets/common-TZ9Ny96S.js +1 -0
- package/client/dist/assets/common-nFpSF4GC.js +1 -0
- package/client/dist/assets/dashboard-B14uYXkG.js +1 -0
- package/client/dist/assets/dashboard-BK_mez-z.js +1 -0
- package/client/dist/assets/dashboard-BfBir_6o.js +1 -0
- package/client/dist/assets/dashboard-Bt0cCzeG.js +1 -0
- package/client/dist/assets/dashboard-BwkjxI6H.js +1 -0
- package/client/dist/assets/{dashboard-C8WOEiPu.js → dashboard-C3bDKf7s.js} +1 -1
- package/client/dist/assets/dashboard-CdvjGfog.js +1 -0
- package/client/dist/assets/dashboard-fRrP0Kv0.js +1 -0
- package/client/dist/assets/dialog-SRw01_QU.js +45 -0
- package/client/dist/assets/dist-js-CJ_2XX1Y.js +1 -0
- package/client/dist/assets/formatDistanceToNow-DSj5DprF.js +1 -0
- package/client/dist/assets/getTimezoneOffsetInMilliseconds-YhD8rIEs.js +1 -0
- package/client/dist/assets/index-DwAmL7m5.css +2 -0
- package/client/dist/assets/index-EzrkUb8B.js +76 -0
- package/client/dist/assets/{jira-api-B1-MUehg.js → jira-api-CSCqGr-Q.js} +1 -1
- package/client/dist/assets/{jobs-CN_zv4Gr.js → jobs-B8rrzfyZ.js} +1 -1
- package/client/dist/assets/{jobs-DUlWSYq2.js → jobs-BS6RTurf.js} +1 -1
- package/client/dist/assets/{jobs-Bpks6doU.js → jobs-BTT0Bsks.js} +1 -1
- package/client/dist/assets/{jobs-Bgq-PZkC.js → jobs-CezU69AE.js} +1 -1
- package/client/dist/assets/{jobs-SwYS2M-B.js → jobs-ClFJae0s.js} +1 -1
- package/client/dist/assets/{jobs-BAls3bWq.js → jobs-DJSFIFVy.js} +1 -1
- package/client/dist/assets/{jobs-DhxKgWFq.js → jobs-DhgGr8vm.js} +1 -1
- package/client/dist/assets/{jobs-B_TeSQxb.js → jobs-DzMjUcaN.js} +1 -1
- package/client/dist/assets/mcp-6rMoZfEO.js +1 -0
- package/client/dist/assets/mcp-BD8sL5mP.js +1 -0
- package/client/dist/assets/mcp-BkcA3I5s.js +1 -0
- package/client/dist/assets/mcp-CCpSelOh.js +1 -0
- package/client/dist/assets/mcp-CUAN6ORK.js +1 -0
- package/client/dist/assets/mcp-CqbRxRTE.js +1 -0
- package/client/dist/assets/mcp-DP2YfVhO.js +1 -0
- package/client/dist/assets/mcp-DUVQamDz.js +1 -0
- package/client/dist/assets/narration-B-ynWTpn.js +1 -0
- package/client/dist/assets/narration-Bj_laZwg.js +1 -0
- package/client/dist/assets/narration-BxHpmMv_.js +1 -0
- package/client/dist/assets/narration-C1Px0-0j.js +1 -0
- package/client/dist/assets/narration-CAXhGbzM.js +1 -0
- package/client/dist/assets/narration-DQ1l_Kq6.js +1 -0
- package/client/dist/assets/narration-XISXwqZd.js +1 -0
- package/client/dist/assets/narration-kwzfglQo.js +1 -0
- package/client/dist/assets/nav-1-Hh2kOz.js +1 -0
- package/client/dist/assets/nav-B34QkESm.js +1 -0
- package/client/dist/assets/nav-Bap_UXAq.js +1 -0
- package/client/dist/assets/{nav-pYuXq7sf.js → nav-BwxXIB24.js} +1 -1
- package/client/dist/assets/nav-Ch-zHBG5.js +1 -0
- package/client/dist/assets/nav-CygC_WN-.js +1 -0
- package/client/dist/assets/{nav-iwjCg5FR.js → nav-KnnLQtOQ.js} +1 -1
- package/client/dist/assets/nav-m00uxYUG.js +1 -0
- package/client/dist/assets/packet-BQ2tXfJI.js +1 -0
- package/client/dist/assets/packet-BdpOJ1Cy.js +1 -0
- package/client/dist/assets/packet-C3Ievb9R.js +1 -0
- package/client/dist/assets/packet-C4zJIG0Y.js +1 -0
- package/client/dist/assets/packet-Cx-zRuWn.js +1 -0
- package/client/dist/assets/packet-DUTGX52M.js +1 -0
- package/client/dist/assets/packet-DhsKtUPB.js +1 -0
- package/client/dist/assets/packet-z3LJ8Zyy.js +1 -0
- package/client/dist/assets/project-repositories-DYhTvwQ1.js +1 -0
- package/client/dist/assets/provider-capabilities-B-wOHfLP.js +1 -0
- package/client/dist/assets/settings-BBt2xtDQ.js +1 -0
- package/client/dist/assets/settings-BJB9RKJj.js +1 -0
- package/client/dist/assets/settings-CHwQR2rB.js +1 -0
- package/client/dist/assets/settings-CrMi3kSi.js +1 -0
- package/client/dist/assets/settings-DKyhDGD1.js +1 -0
- package/client/dist/assets/settings-Deupji9c.js +1 -0
- package/client/dist/assets/settings-DqRMmwAM.js +1 -0
- package/client/dist/assets/settings-J4rj6vo-.js +1 -0
- package/client/dist/assets/setup-2xGgu4Sk.js +1 -0
- package/client/dist/assets/setup-Ba11l2JA.js +1 -0
- package/client/dist/assets/setup-BzW4kMok.js +1 -0
- package/client/dist/assets/setup-COzwYhkJ.js +1 -0
- package/client/dist/assets/setup-CRVULhtF.js +1 -0
- package/client/dist/assets/setup-DgvlxwpE.js +1 -0
- package/client/dist/assets/setup-Dw_wYeEY.js +1 -0
- package/client/dist/assets/setup-ZPtEeJPW.js +1 -0
- package/client/dist/assets/spending-BqbdPm_4.js +1 -0
- package/client/dist/assets/square-Ci-PFcFe.js +1 -0
- package/client/dist/assets/{terminal-BFRZnMPP.js → terminal-BAzpHtpY.js} +1 -1
- package/client/dist/assets/{terminal-IahE4LW9.js → terminal-C7dHeR3x.js} +1 -1
- package/client/dist/assets/{terminal-tITa81iz.js → terminal-CMla5Qsz.js} +1 -1
- package/client/dist/assets/{terminal-BZiIu3rf.js → terminal-CdNXH-n9.js} +1 -1
- package/client/dist/assets/{terminal-ScjyXNxQ.js → terminal-Cw5bZsmV.js} +1 -1
- package/client/dist/assets/{terminal-C2NAd7BW.js → terminal-DFUP4eNv.js} +1 -1
- package/client/dist/assets/{terminal-d7GadXbN.js → terminal-DGqljeK_.js} +1 -1
- package/client/dist/assets/{terminal-QpXZU2Bn.js → terminal-cU70NodO.js} +1 -1
- package/client/dist/assets/useDesktop-RSMTyy7V.js +1 -0
- package/client/dist/assets/useSharedWebSocket-BOdS5_Iz.js +2 -0
- package/client/dist/index.html +34 -35
- package/docs/README.md +3 -0
- package/docs/agent-live-steering.md +57 -0
- package/docs/ci-cd.md +57 -0
- package/docs/code-explorer.md +48 -0
- package/docs/codex.md +12 -1
- package/docs/features/detachable-mission-windows.md +59 -0
- package/docs/guide/de/integrations/5-mcp-server.md +13 -9
- package/docs/guide/de/integrations/6-agent-chat.md +1 -0
- package/docs/guide/en/integrations/5-mcp-server.md +13 -9
- package/docs/guide/en/integrations/6-agent-chat.md +1 -0
- package/docs/guide/es/integrations/5-mcp-server.md +13 -9
- package/docs/guide/es/integrations/6-agent-chat.md +1 -0
- package/docs/guide/fr/integrations/5-mcp-server.md +13 -9
- package/docs/guide/fr/integrations/6-agent-chat.md +1 -0
- package/docs/guide/it/integrations/5-mcp-server.md +13 -9
- package/docs/guide/it/integrations/6-agent-chat.md +1 -0
- package/docs/guide/ja/integrations/5-mcp-server.md +13 -9
- package/docs/guide/ja/integrations/6-agent-chat.md +1 -0
- package/docs/guide/pt/integrations/5-mcp-server.md +13 -9
- package/docs/guide/pt/integrations/6-agent-chat.md +1 -0
- package/docs/guide/zh/integrations/5-mcp-server.md +13 -9
- package/docs/guide/zh/integrations/6-agent-chat.md +1 -0
- package/docs/internals/README.md +1 -0
- package/docs/internals/browser-capture-performance.md +55 -3
- package/docs/internals/browser-login-popups.md +45 -0
- package/docs/internals/browser-native-retina-audit.md +39 -0
- package/docs/internals/ci-cd-audit.md +39 -0
- package/docs/internals/core-runtime-updates.md +68 -0
- package/docs/internals/embedded-browser-native-webview-evaluation.md +2 -0
- package/docs/internals/implementation-reliability-audit.md +93 -0
- package/docs/internals/interactive-jobs.md +110 -6
- package/docs/internals/mcp-mission-audit.md +76 -0
- package/docs/internals/project-builder.md +404 -42
- package/docs/internals/safe-pr-review-flow.md +52 -10
- package/docs/internals/startup-recovery-audit.md +58 -0
- package/docs/mcp.md +74 -41
- package/docs/mission-processes.md +59 -0
- package/docs/multi-repo-projects.md +51 -0
- package/docs/platforms/windows-parity.md +70 -0
- package/docs/platforms/windows.md +13 -11
- package/docs/running-pipelines.md +14 -0
- package/mcp-bridge/dist/specrails-mcp.js +7186 -0
- package/package.json +9 -4
- package/server/dist/agent-chat-manager.js +537 -68
- package/server/dist/agent-chat-router.js +87 -6
- package/server/dist/agent-context-resolver.js +129 -25
- package/server/dist/agent-defaults.js +19 -11
- package/server/dist/agent-input-store.js +206 -0
- package/server/dist/agent-mcp-config.js +54 -15
- package/server/dist/agent-operator-prompt.js +255 -65
- package/server/dist/agent-spec-framing.js +202 -0
- package/server/dist/agent-steering.js +218 -0
- package/server/dist/agent-store.js +153 -0
- package/server/dist/api-not-found.js +8 -0
- package/server/dist/background-process-control.js +164 -0
- package/server/dist/background-process-service.js +61 -0
- package/server/dist/background-process-store.js +242 -0
- package/server/dist/background-windows-bootstrap.js +238 -0
- package/server/dist/blueprint-chat-manager.js +476 -62
- package/server/dist/blueprint-commit.js +21 -1
- package/server/dist/blueprint-draft-parser.js +122 -17
- package/server/dist/blueprint-generation.js +238 -0
- package/server/dist/blueprint-operator-prompt.js +91 -63
- package/server/dist/blueprint-router.js +55 -3
- package/server/dist/blueprint-spec-fixtures.js +85 -0
- package/server/dist/blueprint-spec-quality.js +57 -16
- package/server/dist/blueprint-store.js +92 -1
- package/server/dist/browser-capture-manager.js +86 -30
- package/server/dist/browser-playwright.js +168 -45
- package/server/dist/browser-viewport.js +26 -0
- package/server/dist/chat-manager.js +23 -15
- package/server/dist/chromium-archive.cjs +83 -0
- package/server/dist/chromium-resolver.js +72 -64
- package/server/dist/code-activity.js +145 -0
- package/server/dist/code-explorer-router.js +714 -114
- package/server/dist/core-compat.js +28 -1
- package/server/dist/core-execution.js +111 -0
- package/server/dist/core-package.js +4 -16
- package/server/dist/core-runtime.js +202 -0
- package/server/dist/core-update-manager.js +161 -60
- package/server/dist/core-update-state.js +29 -0
- package/server/dist/db.js +66 -35
- package/server/dist/delivery-evidence.js +142 -39
- package/server/dist/desktop-db.js +239 -43
- package/server/dist/desktop-router.js +109 -9
- package/server/dist/dev-ports.js +8 -3
- package/server/dist/file-provenance.js +34 -22
- package/server/dist/file-story-manager.js +62 -64
- package/server/dist/file-story.js +118 -35
- package/server/dist/file-summary-generator.js +46 -5
- package/server/dist/file-summary-manager.js +355 -106
- package/server/dist/framework-manager.js +98 -110
- package/server/dist/framework-reseed.js +76 -10
- package/server/dist/git-diagnostics.js +2 -2
- package/server/dist/headroom-manager.js +38 -3
- package/server/dist/host-control.js +52 -0
- package/server/dist/index.js +43 -2
- package/server/dist/interactive-job-session.js +149 -16
- package/server/dist/internal-api.js +48 -0
- package/server/dist/jira/jira-materializer.js +1 -0
- package/server/dist/json-tolerant.js +174 -0
- package/server/dist/legacy-migration.js +4 -2
- package/server/dist/loop-command-catalog.js +83 -21
- package/server/dist/loop-constants.js +6 -1
- package/server/dist/loop-decider.js +11 -1
- package/server/dist/loop-executors.js +194 -45
- package/server/dist/loop-factory.js +11 -4
- package/server/dist/loop-graph.js +90 -4
- package/server/dist/loop-run-manager.js +301 -69
- package/server/dist/loop-shell-invocation.js +21 -0
- package/server/dist/loop-step-idle.js +55 -0
- package/server/dist/loop-templates.js +31 -7
- package/server/dist/mcp/agent-capability.js +37 -4
- package/server/dist/mcp/guide.js +149 -36
- package/server/dist/mcp/mcp-admin-router.js +20 -4
- package/server/dist/mcp/mcp-server.js +89 -43
- package/server/dist/mcp/mcp-tiers.js +6 -5
- package/server/dist/mcp/mcp-token.js +3 -1
- package/server/dist/mcp/resources.js +5 -4
- package/server/dist/mcp/tools/catalog.js +4 -0
- package/server/dist/mcp/tools/code.js +125 -14
- package/server/dist/mcp/tools/context.js +153 -0
- package/server/dist/mcp/tools/git.js +15 -5
- package/server/dist/mcp/tools/jobs.js +84 -71
- package/server/dist/mcp/tools/loops.js +17 -6
- package/server/dist/mcp/tools/meta.js +74 -27
- package/server/dist/mcp/tools/mission.js +29 -0
- package/server/dist/mcp/tools/projects.js +22 -6
- package/server/dist/mcp/tools/rails.js +48 -11
- package/server/dist/mcp/tools/specs.js +86 -14
- package/server/dist/mcp/tools/support.js +1 -1
- package/server/dist/mcp/tools/types.js +117 -43
- package/server/dist/mcp/tools/watch.js +144 -51
- package/server/dist/milestone-chain-store.js +164 -0
- package/server/dist/milestone-chain.js +487 -0
- package/server/dist/milestone-progress.js +348 -0
- package/server/dist/mobile/mobile-devices.js +10 -7
- package/server/dist/mobile/mobile-gateway.js +2 -0
- package/server/dist/mobile/mobile-missions.js +214 -0
- package/server/dist/mobile/mobile-redact.js +17 -3
- package/server/dist/mobile/mobile-router.js +46 -4
- package/server/dist/mobile/mobile-ws.js +49 -4
- package/server/dist/multi-repo-bases.js +56 -0
- package/server/dist/multi-repo-checkout.js +101 -0
- package/server/dist/multi-repo-delivery.js +100 -0
- package/server/dist/multi-repo-execution-store.js +65 -0
- package/server/dist/multi-repo-execution.js +453 -0
- package/server/dist/offline-assemble.js +10 -8
- package/server/dist/openspec-runtime-plugin-commands.json +18 -0
- package/server/dist/openspec-runtime-plugin.js +120 -0
- package/server/dist/plugins/codex-spawn.js +49 -0
- package/server/dist/plugins/serena/manifest.js +2 -2
- package/server/dist/plugins/serena/templates/instructions.md +16 -0
- package/server/dist/pr-publisher.js +1 -1
- package/server/dist/profiles-router.js +3 -18
- package/server/dist/project-code-discovery.js +142 -0
- package/server/dist/project-git.js +19 -21
- package/server/dist/project-registry.js +272 -20
- package/server/dist/project-repositories.js +157 -0
- package/server/dist/project-repository-provenance.js +31 -0
- package/server/dist/project-router-background-processes.js +105 -0
- package/server/dist/project-router-git.js +75 -16
- package/server/dist/project-router-jobs.js +2 -126
- package/server/dist/project-router-loop-runs.js +72 -2
- package/server/dist/project-router-tickets.js +53 -8
- package/server/dist/project-router.js +239 -38
- package/server/dist/provider-detection.js +18 -6
- package/server/dist/provider-limit.js +64 -0
- package/server/dist/providers/claude-adapter.js +26 -1
- package/server/dist/providers/claude-background-tasks.js +18 -0
- package/server/dist/providers/claude-live-session.js +383 -0
- package/server/dist/providers/codex-adapter.js +19 -0
- package/server/dist/providers/codex-live-session.js +545 -0
- package/server/dist/providers/gemini-adapter.js +1 -0
- package/server/dist/providers/live-session-types.js +12 -0
- package/server/dist/providers/live-session.js +13 -0
- package/server/dist/providers/terminal-result.js +24 -0
- package/server/dist/queue-manager.js +2 -0
- package/server/dist/rail-isolated-launch.js +141 -16
- package/server/dist/rail-pr-decision.js +364 -99
- package/server/dist/rail-pr-store.js +24 -10
- package/server/dist/rail-worktree-release.js +34 -2
- package/server/dist/rail-worktrees-store.js +4 -4
- package/server/dist/rails-router.js +249 -55
- package/server/dist/repo-lock.js +92 -17
- package/server/dist/review-packet.js +23 -13
- package/server/dist/schemas/file-summary.v1.json +59 -59
- package/server/dist/schemas/profile.v1.json +137 -137
- package/server/dist/setup-manager.js +52 -8
- package/server/dist/shell-integration/bash-shim.bash +45 -0
- package/server/dist/shell-integration/fish-shim.fish +36 -0
- package/server/dist/shell-integration/powershell-shim.ps1 +48 -0
- package/server/dist/shell-integration/zsh-shim.zsh +47 -0
- package/server/dist/smash-runner.js +1 -0
- package/server/dist/spawn-lifecycle.js +44 -27
- package/server/dist/spec-contract-prompt.js +143 -0
- package/server/dist/spec-draft-parser.js +6 -1
- package/server/dist/spec-models.js +1 -0
- package/server/dist/stuck-run-detector.js +1 -0
- package/server/dist/terminal-manager.js +21 -1
- package/server/dist/terminal-shell-integration.js +4 -1
- package/server/dist/transient-children.js +619 -230
- package/server/dist/util/cli-prompt.js +78 -5
- package/server/dist/util/sqlite-migrations.js +20 -0
- package/server/dist/util/win-spawn.js +18 -3
- package/server/dist/util/windows-provider-shell.js +29 -0
- package/server/dist/verification-sentinel.js +14 -0
- package/server/dist/vitest-setup.js +2 -0
- package/server/dist/windows-job-supervisor.js +133 -0
- package/server/dist/workspace-manager.js +9 -30
- package/server/dist/worktree-manager.js +17 -2
- package/server/dist/worktree-overlay.js +3 -1
- package/client/dist/assets/ActivityFeedPage-tAQcWs1R.js +0 -1
- package/client/dist/assets/AgentBrowserCapture-BX8BNae5.js +0 -1
- package/client/dist/assets/AgentModeAnalyticsPane-BVAqCL0K.js +0 -2
- package/client/dist/assets/AgentModeCodePane-9XfqQc9c.js +0 -2
- package/client/dist/assets/AgentModeJobsPane-BCjLsPhc.js +0 -1
- package/client/dist/assets/AgentsPage-C8J_42sm.js +0 -87
- package/client/dist/assets/AnalyticsPage-BtQuyfJm.js +0 -1
- package/client/dist/assets/CodePage-BZ2MwdCu.js +0 -2
- package/client/dist/assets/DesktopAnalyticsPage-Bh_29V4g.js +0 -1
- package/client/dist/assets/ExportDropdown-BrtfWzEG.js +0 -1
- package/client/dist/assets/InteractiveJobComposer-BCZiSfzc.js +0 -19
- package/client/dist/assets/JobDetailModal-NAJmuYIT.js +0 -1
- package/client/dist/assets/JobDetailPage-fE0ZycUX.js +0 -1
- package/client/dist/assets/JobsPage-z5M_HVJG.js +0 -1
- package/client/dist/assets/LoopBuilderPage-j1FQS47c.js +0 -7
- package/client/dist/assets/LoopPreviewModal-DhDJjdCe.js +0 -1
- package/client/dist/assets/LoopsPage-Ll7zORZb.js +0 -1
- package/client/dist/assets/MinimizedChatsContext-Bd3OqR_S.js +0 -1
- package/client/dist/assets/PluginsPage-DN4kGvJt.js +0 -2
- package/client/dist/assets/ProjectSettingsDialog-OgsgxjzF.js +0 -1
- package/client/dist/assets/ReviewPacketPage-DFog4sXn.js +0 -1
- package/client/dist/assets/TemplatePreviewModal-L2XxqiOD.js +0 -1
- package/client/dist/assets/TicketDetailModal-CsF-AWZX.js +0 -14
- package/client/dist/assets/Trans-Bsz9_gyq.js +0 -1
- package/client/dist/assets/agent-BQgKv_2F.js +0 -1
- package/client/dist/assets/agent-C1NRDJqL.js +0 -1
- package/client/dist/assets/agent-CLNleiy6.js +0 -1
- package/client/dist/assets/agent-CmceArkQ.js +0 -1
- package/client/dist/assets/agent-D3UxQ3SR.js +0 -1
- package/client/dist/assets/agent-DB1JeE64.js +0 -1
- package/client/dist/assets/agent-DXkHcQ-D.js +0 -1
- package/client/dist/assets/agent-DjJJJ2M8.js +0 -1
- package/client/dist/assets/auth-D_5bQv5W.js +0 -1
- package/client/dist/assets/ban-E4EaZe3a.js +0 -1
- package/client/dist/assets/bot-DzuuuC5R.js +0 -1
- package/client/dist/assets/browser-BgqHDMXP.js +0 -1
- package/client/dist/assets/browser-CsKJtrpv.js +0 -1
- package/client/dist/assets/browser-Cu58EpZU.js +0 -1
- package/client/dist/assets/browser-CvXOJm-j.js +0 -1
- package/client/dist/assets/browser-DOrFh1c7.js +0 -1
- package/client/dist/assets/browser-DQv_Jo7x.js +0 -1
- package/client/dist/assets/browser-LPgxvJu2.js +0 -1
- package/client/dist/assets/browser-eL-3MIEI.js +0 -1
- package/client/dist/assets/builder-0IWtosUm.js +0 -1
- package/client/dist/assets/builder-Bf2N6XWn.js +0 -1
- package/client/dist/assets/builder-BnqaaBkT.js +0 -1
- package/client/dist/assets/builder-BxxfGgAO.js +0 -1
- package/client/dist/assets/builder-C5IIgWsq.js +0 -1
- package/client/dist/assets/builder-CHuahItu.js +0 -1
- package/client/dist/assets/builder-CjWosXL_.js +0 -1
- package/client/dist/assets/builder-CpCIOoKj.js +0 -1
- package/client/dist/assets/code-CHPkOkDS.js +0 -1
- package/client/dist/assets/commands-6tYQih5m.js +0 -1
- package/client/dist/assets/commands-BK2QinXN.js +0 -1
- package/client/dist/assets/commands-C2Nca76h.js +0 -1
- package/client/dist/assets/commands-CKdLihNX.js +0 -1
- package/client/dist/assets/commands-CYIBTAlc.js +0 -1
- package/client/dist/assets/commands-CjUtYJIM.js +0 -1
- package/client/dist/assets/commands-Cq_u_8y9.js +0 -1
- package/client/dist/assets/commands-D_ZHYQug.js +0 -1
- package/client/dist/assets/common-6MLbD-jM.js +0 -1
- package/client/dist/assets/common-BKGEu4rm.js +0 -1
- package/client/dist/assets/common-BNbnv8cE.js +0 -1
- package/client/dist/assets/common-BqGWtDbt.js +0 -1
- package/client/dist/assets/common-DBtTzeYm.js +0 -1
- package/client/dist/assets/common-DEW3Nr8n.js +0 -1
- package/client/dist/assets/common-DHsv9N5d.js +0 -1
- package/client/dist/assets/common-mkrEcW-Y.js +0 -1
- package/client/dist/assets/dashboard-B9GhC5ku.js +0 -1
- package/client/dist/assets/dashboard-CBnv6zOI.js +0 -1
- package/client/dist/assets/dashboard-CaP0fU87.js +0 -1
- package/client/dist/assets/dashboard-D4ZZ4x84.js +0 -1
- package/client/dist/assets/dashboard-DmuTQBX8.js +0 -1
- package/client/dist/assets/dashboard-DynLYj3d.js +0 -1
- package/client/dist/assets/dashboard-zj4gGFJ1.js +0 -1
- package/client/dist/assets/dialog-CdWCb02j.js +0 -45
- package/client/dist/assets/dist-bVqfYmby.js +0 -1
- package/client/dist/assets/dist-js-CWeUdf5Z.js +0 -1
- package/client/dist/assets/formatDistanceToNow-DQCvMgUc.js +0 -1
- package/client/dist/assets/getTimezoneOffsetInMilliseconds-C16cxSe1.js +0 -1
- package/client/dist/assets/git-refresh-wTW0rO0Z.js +0 -1
- package/client/dist/assets/i18n-hnX2DPmo.js +0 -2
- package/client/dist/assets/index-DIhMEAVw.js +0 -66
- package/client/dist/assets/index-DwygcU7s.css +0 -2
- package/client/dist/assets/mcp-B8X8wBLu.js +0 -1
- package/client/dist/assets/mcp-C2AojZZN.js +0 -1
- package/client/dist/assets/mcp-D8BT78_L.js +0 -1
- package/client/dist/assets/mcp-Df5RWBNk.js +0 -1
- package/client/dist/assets/mcp-Dj7BgMQW.js +0 -1
- package/client/dist/assets/mcp-Q7iT7rcz.js +0 -1
- package/client/dist/assets/mcp-YlvNvJbW.js +0 -1
- package/client/dist/assets/mcp-mKFpTSRb.js +0 -1
- package/client/dist/assets/narration-4Z2VUhZw.js +0 -1
- package/client/dist/assets/narration-BGuiEDj3.js +0 -1
- package/client/dist/assets/narration-BuSoqRmv.js +0 -1
- package/client/dist/assets/narration-CB9t29fy.js +0 -1
- package/client/dist/assets/narration-CU3x8FHD.js +0 -1
- package/client/dist/assets/narration-CWeXhfP5.js +0 -1
- package/client/dist/assets/narration-DRaDnhT5.js +0 -1
- package/client/dist/assets/narration-DtEbQgCw.js +0 -1
- package/client/dist/assets/nav-ApJVg3xu.js +0 -1
- package/client/dist/assets/nav-BKNky1Bg.js +0 -1
- package/client/dist/assets/nav-C6vWWKYx.js +0 -1
- package/client/dist/assets/nav-CXJ6Aw89.js +0 -1
- package/client/dist/assets/nav-CsmFBIDf.js +0 -1
- package/client/dist/assets/nav-DjkmQJjd.js +0 -1
- package/client/dist/assets/packet-B1IxaL5f.js +0 -1
- package/client/dist/assets/packet-Bh5kkbiB.js +0 -1
- package/client/dist/assets/packet-By8fqYxW.js +0 -1
- package/client/dist/assets/packet-CZdvA44E.js +0 -1
- package/client/dist/assets/packet-CrjHRQCP.js +0 -1
- package/client/dist/assets/packet-Dwi2pTtx.js +0 -1
- package/client/dist/assets/packet-_-ZIa0IR.js +0 -1
- package/client/dist/assets/packet-n0W6E1Hn.js +0 -1
- package/client/dist/assets/play-DNlrawbS.js +0 -1
- package/client/dist/assets/provider-capabilities-Doej9lfa.js +0 -1
- package/client/dist/assets/settings-Bd0x4TBa.js +0 -1
- package/client/dist/assets/settings-C1-7cXzC.js +0 -1
- package/client/dist/assets/settings-CAFaT38b.js +0 -1
- package/client/dist/assets/settings-Ct3v1nW2.js +0 -1
- package/client/dist/assets/settings-DHh7_IF5.js +0 -1
- package/client/dist/assets/settings-DQXAmdcv.js +0 -1
- package/client/dist/assets/settings-guzlhpup.js +0 -1
- package/client/dist/assets/settings-sZfTPMEH.js +0 -1
- package/client/dist/assets/setup-3jWaoVUO.js +0 -1
- package/client/dist/assets/setup-BjgRneb2.js +0 -1
- package/client/dist/assets/setup-BzFIjv-f.js +0 -1
- package/client/dist/assets/setup-C4VfQsxE.js +0 -1
- package/client/dist/assets/setup-DfUkpDbN.js +0 -1
- package/client/dist/assets/setup-ECe90Pmu.js +0 -1
- package/client/dist/assets/setup-dlqC4Jmm.js +0 -1
- package/client/dist/assets/setup-jwfLMiM2.js +0 -1
- package/client/dist/assets/spending-EC9xqio-.js +0 -1
- package/client/dist/assets/useDesktop-BQVt8lFd.js +0 -1
|
@@ -74,6 +74,28 @@ route).
|
|
|
74
74
|
|
|
75
75
|
## Day-0 chat (no project exists)
|
|
76
76
|
|
|
77
|
+
**Decision cards (`BuilderDecisionCard`).** The interview prose keeps
|
|
78
|
+
asking the user to type "surprise me" and, once the five dimensions are
|
|
79
|
+
decided, to "approve the blueprint" — and the hero composer's Surprise-me
|
|
80
|
+
button only exists before the first turn (`showSurpriseMe`). Each ask gets ONE
|
|
81
|
+
premium card in the thread (same glass shell as `AgentSpecDraftCard`: accent
|
|
82
|
+
header band, identity chip, CTA pill, `motion` enter/exit, reduced-motion
|
|
83
|
+
safe) in two modes. **Offer** — clickable, rendered after the newest SETTLED
|
|
84
|
+
Builder reply by a deterministic rule (no prose detection): `phase === 'chat'`,
|
|
85
|
+
not busy, no stream buffer, last message is the assistant's, snapshot not
|
|
86
|
+
`generating`; `surprise` while the readiness `blueprint` step is not `done`,
|
|
87
|
+
`approve` once it is `done` and no M1 spec exists yet (specs step pending with
|
|
88
|
+
count 0). One click sends the canonical prompt (`prompts.surpriseMe` /
|
|
89
|
+
`prompts.approve`) through `session.surpriseMe()` / `session.approveBlueprint()`
|
|
90
|
+
tagged with an **intent**. **Settled** — the user turn that carried the intent
|
|
91
|
+
renders as the non-clickable "Decision taken · time" card IN PLACE of the raw
|
|
92
|
+
prompt bubble, so the decision stays fixed in the thread; the intent is
|
|
93
|
+
PERSISTED on the message row (`blueprint_messages.intent`, desktop-db
|
|
94
|
+
migration 24; `POST /send { intent: 'surprise' | 'approve' }`, validated,
|
|
95
|
+
anything else dropped; `GET /conversations/:id` returns it) so it survives a
|
|
96
|
+
resume and a locale switch. i18n `builder:decisionCard.*` + `prompts.approve`
|
|
97
|
+
×8.
|
|
98
|
+
|
|
77
99
|
The provider selector excludes adapters that cannot enforce the blueprint
|
|
78
100
|
generator's pure-output policy. A direct Kimi request is rejected before
|
|
79
101
|
`BlueprintChatManager` spawns. This does not prevent the approved blueprint
|
|
@@ -106,9 +128,159 @@ from declaring Kimi among the new project's target providers.
|
|
|
106
128
|
partial subset as the latest draft; `specsComplete: true` appears only on the
|
|
107
129
|
complete set. If generation cannot finish, the prior non-complete proposal
|
|
108
130
|
remains the last valid state and cannot be committed.
|
|
131
|
+
- **Lazy conversation row**: the client no longer POSTs a conversation when
|
|
132
|
+
the Builder opens. `useBuilderSession.ensureConversation()` creates the row
|
|
133
|
+
(with the composer's provider/model) on the FIRST send, single-flight, or
|
|
134
|
+
`resume()` rehydrates a persisted one — so opening and closing the Builder
|
|
135
|
+
never leaves empty orphan rows behind.
|
|
136
|
+
|
|
137
|
+
## Snapshot hardening (harden-project-builder-snapshots)
|
|
138
|
+
|
|
139
|
+
> Why: a real run (WebTetris, 8 specs, `specsComplete: true` claimed by the
|
|
140
|
+
> model) ended with a greyed-out *Create specs* button reading "Generation is
|
|
141
|
+
> not complete yet." The 8-spec block had been REJECTED by `JSON.parse` and
|
|
142
|
+
> dropped **silently** — `continue // malformed; drop silently` — so the panel
|
|
143
|
+
> kept the interview snapshot (`m1Specs: []`), the model never learned its
|
|
144
|
+
> block was unusable ("no reemito un nuevo snapshot"), and because the
|
|
145
|
+
> snapshot lived only in client memory (persisted rows were STRIPPED of the
|
|
146
|
+
> JSON) the whole batch was lost when the panel closed. Two grave bugs with
|
|
147
|
+
> one root: the snapshot pipeline had no feedback loop and no durability.
|
|
148
|
+
|
|
149
|
+
**1. Nothing is dropped silently (parser).** `parseBlueprintDraftBlocks`
|
|
150
|
+
(server + client mirror) now returns `rejected: BlueprintRejectedBlock[]`
|
|
151
|
+
(`{index, reason: 'invalid_json'|'missing_version'|'truncated', detail}`),
|
|
152
|
+
`repaired` and `truncated` next to the existing fields. Before rejecting, every
|
|
153
|
+
block runs through `parseJsonTolerant` (`server/json-tolerant.ts` ⇄
|
|
154
|
+
`client/src/lib/json-tolerant.ts`): strict `JSON.parse` first, then ONE
|
|
155
|
+
string-aware repair pass for the mistakes a model makes when hand-writing a
|
|
156
|
+
~20 KB payload — raw newlines/tabs/control chars inside strings, a stray inner
|
|
157
|
+
`"`, trailing commas, `//` / `/* */` comments outside strings, a nested
|
|
158
|
+
```` ```json ```` fence, prose around the object, invalid `\x` escapes — and
|
|
159
|
+
parses again (`repaired: true`, repair kinds listed). An UNTERMINATED trailing
|
|
160
|
+
fence in a settled reply (the output limit cut the block) is now reported as
|
|
161
|
+
`truncated` (with the number of spec titles that had started, via
|
|
162
|
+
`countStartedSpecs`) and CUT from the transcript — raw partial JSON never
|
|
163
|
+
reaches the chat. The `invalid_json` detail carries the parser message plus a
|
|
164
|
+
±40-char excerpt around the failing position so the model can fix it.
|
|
165
|
+
|
|
166
|
+
**2. The app tells the model (repair loop).** `BlueprintChatManager._runTurn`
|
|
167
|
+
audits every settled turn: `planSnapshotRepair(parse)` returns a repair
|
|
168
|
+
request when (a) no snapshot was accepted but a block was emitted (kind
|
|
169
|
+
`invalid_json` / `truncated`), or (b) the accepted raw snapshot declares
|
|
170
|
+
`specsComplete: true` and the deterministic M1 gate
|
|
171
|
+
(`auditRawBlueprintForM1`, the same `analyzeBuilderSpecBatch` the commit runs)
|
|
172
|
+
disagrees (kind `quality`, detail = one line per issue). Then it runs **ONE
|
|
173
|
+
automatic repair turn** on the SAME session (`--resume`, so the model has its
|
|
174
|
+
own context) with a byte-stable prompt from `blueprint-operator-prompt.ts`
|
|
175
|
+
`buildSnapshotRepairPrompt(kind, detail)` — "APP CHECK: … re-emit the
|
|
176
|
+
COMPLETE snapshot … reply with ONLY the block" (the `truncated` variant asks
|
|
177
|
+
to tighten descriptions so the block fits). `blueprint.repairing
|
|
178
|
+
{kind, attempt, manual}` is broadcast first; the repair turn streams like any
|
|
179
|
+
turn; ONE `blueprint.done` follows. Outcomes: repaired block accepted ⇒
|
|
180
|
+
`snapshot.status='accepted', repaired: true, repairAttempted: true`; still
|
|
181
|
+
unusable ⇒ `status='rejected'` with the freshest diagnostic (never
|
|
182
|
+
`blueprint.error` — the user's turn succeeded); a quality repair that still
|
|
183
|
+
fails the gate ⇒ the snapshot is delivered WITH `qualityIssues` so the UI can
|
|
184
|
+
list them. Bounded: a repair never nests another; no session id / provider
|
|
185
|
+
without `nativeResume` ⇒ the rejection is reported without a repair. Every
|
|
186
|
+
spawn records its own `agent_invocations` row (a repair bills too). Manual
|
|
187
|
+
repair: `POST /api/blueprint/conversations/:id/repair-snapshot` →
|
|
188
|
+
`BlueprintChatManager.repairSnapshot(id)` decides what to ask from the
|
|
189
|
+
PERSISTED state (pending rejection ⇒ JSON/truncated prompt; claimed-complete
|
|
190
|
+
snapshot failing the gate ⇒ quality prompt) so it works after a restart; 202
|
|
191
|
+
`{kind}` / 409 `streaming|nothing_to_repair|no_session` / 404. The operator
|
|
192
|
+
prompts (both `BUILDER_INSTRUCTIONS` and `BUILDER_SYSTEM_PROMPT`) gained the
|
|
193
|
+
JSON hygiene rules (escape `\n` / `\"`, no trailing commas, no nested
|
|
194
|
+
fences, nothing after the closing fence, "if the app reports a rejected or
|
|
195
|
+
cut-off block answer with ONLY the corrected snapshot").
|
|
196
|
+
|
|
197
|
+
**3. Durable snapshots (migration 23, `desktop.sqlite`).**
|
|
198
|
+
`blueprint_conversations` += `blueprint_json` (normalized accepted snapshot),
|
|
199
|
+
`raw_blueprint_json` (exact payload — the commit/readiness evidence),
|
|
200
|
+
`snapshot_updated_at`, `snapshot_issue_json` (the pending rejection until the
|
|
201
|
+
next accepted block), `committed_project_id` (set by the commit's
|
|
202
|
+
`markCommitted` IO hook right after `register`; the commit body accepts an
|
|
203
|
+
optional `conversationId`, unknown ids are ignored, a failing link never fails
|
|
204
|
+
the commit). `blueprint_messages` += `raw_content` — the model's UNSTRIPPED
|
|
205
|
+
reply whenever it carried a block (forensics; a later parser fix can re-read
|
|
206
|
+
an old rejected snapshot). A block-only reply persists `content=''` with the
|
|
207
|
+
raw payload; `GET /conversations/:id` hides empty rows and never exposes
|
|
208
|
+
`raw_content`. Store helpers: `saveBlueprintSnapshot`,
|
|
209
|
+
`saveBlueprintSnapshotIssue`, `getBlueprintSnapshot` (normalizes through
|
|
210
|
+
`coerceBlueprint`; corrupt JSON reads as null), `markBlueprintCommitted`,
|
|
211
|
+
`listResumableBlueprintConversations`.
|
|
212
|
+
|
|
213
|
+
**4. Resume ("Continue where you left off").** `GET
|
|
214
|
+
/api/blueprint/conversations?resumable=1` lists unfinished conversations
|
|
215
|
+
(never committed, ≥1 assistant reply) newest first with a snapshot summary
|
|
216
|
+
(`productName`, `platform`, `specCount`, `specsComplete`, `dimensionsFilled`,
|
|
217
|
+
`hasSnapshot`, `pendingIssue`, `messageCount`). `GET /conversations/:id` now
|
|
218
|
+
returns the transcript + `blueprint` + `rawBlueprint` + `snapshot`
|
|
219
|
+
(`accepted` with `claimsComplete`/`qualityIssues`, `rejected` with
|
|
220
|
+
reason/detail, or `none`) + `snapshotUpdatedAt`. Client:
|
|
221
|
+
`BuilderRecentBlueprints` (under the hero composer while the session is empty)
|
|
222
|
+
→ `session.resume(id)` rehydrates messages, snapshot pair, provider/model and
|
|
223
|
+
the provider session (later turns `--resume`); two-step inline discard →
|
|
224
|
+
`DELETE`. The exit confirm copy no longer threatens to discard the blueprint —
|
|
225
|
+
it says where to pick it up.
|
|
226
|
+
|
|
227
|
+
**5. Readiness, made legible (client).** `client/src/lib/blueprint-readiness.ts`
|
|
228
|
+
`deriveReadiness` turns the same deterministic report into three steps —
|
|
229
|
+
**blueprint** (5 dimensions) · **specs** (count within 5–10 and
|
|
230
|
+
`specsComplete`) · **audit** (issues excluding the two batch-level codes) —
|
|
231
|
+
with structured params, and `localizeQualityIssue(t, issue)` maps every
|
|
232
|
+
`(field, code)` to a `builder:quality.*` key (`{{n}}`, `{{heading}}`,
|
|
233
|
+
`{{label}}`, `{{min}}`/`{{max}}`/`{{count}}`, `{{criterion}}`, `{{other}}`;
|
|
234
|
+
unknown codes fall back to the English message). Both quality analyzers
|
|
235
|
+
(server `analyzeBuilderSpecBatch` and the client mirror) now attach `params`
|
|
236
|
+
to each issue. `BlueprintReadiness` (`client/src/components/project-builder/`)
|
|
237
|
+
replaces the old CTA + raw-English hint in BOTH surfaces (floating panel side
|
|
238
|
+
pane and the Agent-Mode workspace sidebar) and in the M2+ shell: the three
|
|
239
|
+
steps, the snapshot status (repairing pill / rejected card with reason +
|
|
240
|
+
diagnostic + **Ask for the snapshot again** / "repaired automatically" note),
|
|
241
|
+
the audit issues per spec with **Ask the Builder to fix these** (shown only
|
|
242
|
+
when the model claimed completion — the only case the app can repair), and the
|
|
243
|
+
primary CTA whose disabled hint names the FIRST blocker in plain language.
|
|
244
|
+
`useBuilderSession` exposes `snapshot: BuilderSnapshotState`
|
|
245
|
+
(`idle|repairing|accepted|rejected`), `readiness`, `generation`, `recent`,
|
|
246
|
+
`resume`, `discardRecent`, `repairSnapshot`, `conversationId`; a block-only
|
|
247
|
+
`blueprint.done` (empty `fullText`) appends no bubble; a legacy `done` without
|
|
248
|
+
`snapshot` still parses the settled text. `BlueprintPanel` shows a
|
|
249
|
+
Complete / In progress pill on the M1 header and a pulse while repairing.
|
|
250
|
+
|
|
251
|
+
**6. Live generation progress.** While a block streams in (hidden by the tail
|
|
252
|
+
cut — previously a static "Thinking…" for up to a minute) `describeStreamingSnapshot`
|
|
253
|
+
counts the spec titles started inside the open fence and
|
|
254
|
+
`BuilderGenerationProgress` renders "Writing the Milestone-1 specs… · spec N"
|
|
255
|
+
with a soft progress bar (specs ÷ cap, capped at 95% until the block closes),
|
|
256
|
+
switching to the repair label during a repair turn. Used in the day-0 thread
|
|
257
|
+
and the M2+ shell.
|
|
258
|
+
|
|
259
|
+
**M2+ (`MilestoneGenerateShell`)** rides `ChatManager`, which has no
|
|
260
|
+
app-driven repair turn; it gets the tolerant parser + rejection diagnostics +
|
|
261
|
+
the same readiness surface for free, and its repair button sends a localized
|
|
262
|
+
user message (`builder:prompts.repairSnapshot`) instead.
|
|
263
|
+
|
|
264
|
+
**WS contract.** `blueprint.done` += `snapshot` (see `BlueprintDoneMessage`);
|
|
265
|
+
new `blueprint.repairing`. i18n: `builder` namespace gained `generation.*`,
|
|
266
|
+
`readiness.*`, `snapshot.*`, `recent.*`, `quality.*` (×8, parity-tested).
|
|
109
267
|
|
|
110
268
|
## blueprint-draft protocol
|
|
111
269
|
|
|
270
|
+
**Fence tolerance (bug fix, 2026-09-04).** Models mirror the schema example
|
|
271
|
+
and Sonnet emitted the whole snapshot inside a ```` ```json ```` fence: the
|
|
272
|
+
parsers only knew ```` ```blueprint-draft ````, so the raw JSON landed in the
|
|
273
|
+
chat, no snapshot was accepted (no rejection either) and the panel stayed at
|
|
274
|
+
0/5 dimensions. Two-sided fix: the prompt's example is now fenced
|
|
275
|
+
`blueprint-draft` with an explicit rule ("the fence language is EXACTLY
|
|
276
|
+
`blueprint-draft` — a json or bare fence is NOT a snapshot"), and both parsers
|
|
277
|
+
run `promoteJsonBlueprintFences` first — a CLOSED ```` ```json ```` / bare
|
|
278
|
+
fence whose body is a `{…}` object with an integer `blueprintVersion` is
|
|
279
|
+
promoted to a blueprint-draft block (outside proper blocks only; ordinary json
|
|
280
|
+
fences and invalid payloads are untouched), and an OPEN one is reported
|
|
281
|
+
`truncated` / hidden from the live stream (`cutUnterminatedBlock`,
|
|
282
|
+
`describeStreamingSnapshot`).
|
|
283
|
+
|
|
112
284
|
Fenced ` ```blueprint-draft ` JSON blocks. FULL snapshots, LAST syntactically
|
|
113
285
|
valid block wins, streaming tail cut (unterminated trailing fence never
|
|
114
286
|
parsed/shown — `cutUnterminatedBlock`). Unknown keys dropped; missing or
|
|
@@ -145,7 +317,14 @@ files remain readable (and returns null for missing/corrupt input).
|
|
|
145
317
|
## Canonical rich-spec contract (M1 and generated M2+)
|
|
146
318
|
|
|
147
319
|
There is no Builder-specific “lite spec” format. Every detailed Builder spec
|
|
148
|
-
uses the normal Specrails contract
|
|
320
|
+
uses the normal Specrails contract, and since premium-milestone-progress the
|
|
321
|
+
prose that teaches it lives in ONE module — `server/spec-contract-prompt.ts`
|
|
322
|
+
(`premiumSpecContract(mode)`, `premiumSpecContractCompact(mode)`,
|
|
323
|
+
`PREMIUM_SCAFFOLD_EXAMPLE`, `SPEC_DEPTH_FLOORS`) — consumed by the Builder
|
|
324
|
+
operator prompt, `ChatManager._buildMilestoneSystemPrompt` (M2+) and the
|
|
325
|
+
agent's super-spec section, so the three authors never drift. `mode` selects
|
|
326
|
+
the grounding hook: `day0` (no repo — every module/path/route is labelled
|
|
327
|
+
*planned*) vs `verified` (only paths that were inspected).
|
|
149
328
|
|
|
150
329
|
1. `kind`: `scaffold`, `feature`, or `verification`
|
|
151
330
|
2. an English, action-oriented, unique `title`
|
|
@@ -153,28 +332,113 @@ uses the normal Specrails contract:
|
|
|
153
332
|
4. `description` with exactly these `##` headings, once and in this order:
|
|
154
333
|
`Problem Statement`, `Proposed Solution`, `Out of Scope`,
|
|
155
334
|
`Technical Considerations`, `Estimated Complexity`
|
|
156
|
-
5. a separate `acceptanceCriteria[]` containing
|
|
157
|
-
testable outcomes
|
|
335
|
+
5. a separate `acceptanceCriteria[]` containing **6–10** independently
|
|
336
|
+
testable outcomes (each ≥ 20 chars, covering the happy path, at least one
|
|
337
|
+
failure/edge case and an automated verification); `description` MUST NOT
|
|
338
|
+
contain `## Acceptance Criteria`
|
|
158
339
|
6. a catalog-valid `priority`, non-empty domain labels, and an optional
|
|
159
340
|
`dependsOnIndex` that points strictly backward (the M1 scaffold omits it)
|
|
160
341
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
342
|
+
**Depth floors (`SPEC_DEPTH_FLOORS`, enforced by the deterministic gate on
|
|
343
|
+
both sides):** Problem Statement ≥ 200 chars of narrative (who, what breaks
|
|
344
|
+
today, why now, what good looks like); Proposed Solution ≥ 500 chars — a
|
|
345
|
+
numbered user journey followed by the five `###` sub-blocks *User
|
|
346
|
+
experience · Data model · Interfaces & contracts · Planned modules · Key
|
|
347
|
+
decisions*; Out of Scope ≥ 3 bullets, each naming WHERE the exclusion goes
|
|
348
|
+
(a later milestone / never); Technical Considerations ≥ 5 labelled bullets
|
|
349
|
+
(**Architecture**, **Data & contracts**, **Failure handling & edge cases**,
|
|
350
|
+
**Security & privacy**, **Testing strategy**, **Dependencies**, **Risks &
|
|
351
|
+
mitigations**); Estimated Complexity = a reasoned estimate naming the main
|
|
352
|
+
uncertainty. `PREMIUM_SCAFFOLD_EXAMPLE` is the mandatory first spec written
|
|
353
|
+
at that depth; "shorter is a defect, not a style". The floors were raised
|
|
354
|
+
because the old minima ("at least two bullets", 4–10 criteria) became the
|
|
355
|
+
ceiling the model aimed at (design D6/D8). Test fixtures that need a
|
|
356
|
+
gate-valid spec use `server/blueprint-spec-fixtures.ts` ⇄
|
|
357
|
+
`client/src/lib/__tests__/premium-spec-fixture.ts`.
|
|
168
358
|
|
|
169
359
|
`server/blueprint-spec-quality.ts` is the shared deterministic authority. It
|
|
170
360
|
validates `specsComplete=true`, the complete-set size, all fields/sections
|
|
171
|
-
above (including
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
361
|
+
above (including the depth floors — issue codes `section_depth` carries the
|
|
362
|
+
heading + min chars, `section_bullets` the min, `criteria_count` the 6–10
|
|
363
|
+
bounds, `criterion_short` the 20-char floor), unique titles, the M1
|
|
364
|
+
first-item `kind='scaffold'` rule, and backward-only dependencies. Both
|
|
365
|
+
commit paths run it before any filesystem, registry, blueprint, milestone,
|
|
366
|
+
or ticket-store mutation and return a stable spec/field-oriented detail when
|
|
367
|
+
it rejects. A prompt is a generation aid, never the integrity boundary.
|
|
368
|
+
Validation receives the exact raw generated payload; normalized
|
|
369
|
+
compatibility views are not commit evidence.
|
|
370
|
+
|
|
371
|
+
### App-driven batched generation (premium-milestone-progress D7)
|
|
372
|
+
|
|
373
|
+
"Emit all 5–10 specs complete in ONE reply" capped every spec's depth by the
|
|
374
|
+
output budget (ten premium specs do not fit) and the old `truncated` repair
|
|
375
|
+
told the model to *tighten* — institutionalising thin specs. Generation is
|
|
376
|
+
now driven by the app, in turns on the SAME session (`server/blueprint-
|
|
377
|
+
generation.ts` + the drive closure in `BlueprintChatManager._runTurn`):
|
|
378
|
+
|
|
379
|
+
1. **Outline.** After approval the Builder emits ONE `blueprint-draft` FULL
|
|
380
|
+
snapshot with every spec's `kind/title/shortSummary/priority/labels/
|
|
381
|
+
dependsOnIndex` decided but `description: ""`, `acceptanceCriteria: []`,
|
|
382
|
+
`specsComplete: false`. `isOutlineSnapshot` (≥ `M1_SPECS_MIN` specs, every
|
|
383
|
+
body empty) on a resumable session (`capabilities.nativeResume` + a session
|
|
384
|
+
id) starts the drive; the outline is broadcast as the first
|
|
385
|
+
`blueprint.done { continuing: true }` frame so the panel lists the specs
|
|
386
|
+
immediately.
|
|
387
|
+
2. **Detail turns.** `APP CONTINUE` names `SPECS_PER_DETAIL_TURN = 2` specs
|
|
388
|
+
by index + title; the model answers with one fenced `spec-detail` block
|
|
389
|
+
per spec — `{ "index": n, "spec": { …complete premium spec… } }` — and
|
|
390
|
+
nothing else. `parseGenerationBlocks` extracts/strips them (tolerant JSON,
|
|
391
|
+
`truncated` on an open fence), `mergeSpecDetails` merges by index (an
|
|
392
|
+
omitted key keeps the outline's value, out-of-range indexes are ignored).
|
|
393
|
+
A range that is still unfilled gets ONE `APP CHECK` re-ask
|
|
394
|
+
(`buildDetailRepairPrompt`); still unfilled ⇒ the drive **halts**
|
|
395
|
+
(`blueprint.done { snapshot.generationHalted: true }`, `specsComplete`
|
|
396
|
+
forced false, the outline/partial snapshot persisted — nothing is lost).
|
|
397
|
+
3. **Audit turn.** `APP AUDIT` asks for one `spec-audit` block
|
|
398
|
+
`{ specsComplete, issues[], fixes[{ index, spec }] }`. Fixes merge like
|
|
399
|
+
details; a verdict with zero fixes still applies (`specsComplete`). A
|
|
400
|
+
verdict of `false` WITH issues gets ONE corrections turn
|
|
401
|
+
(`buildAuditIssuesPrompt` — `spec-detail` blocks for the affected specs
|
|
402
|
+
only); a reply without any block lets the deterministic gate judge.
|
|
403
|
+
4. **Quality repair.** The pre-existing repair tail runs unchanged after the
|
|
404
|
+
audit (`planSnapshotRepair` → `quality` when the model claims complete and
|
|
405
|
+
the gate disagrees); the repair reply may now be `spec-detail` patches
|
|
406
|
+
instead of a whole snapshot (`applyReply`).
|
|
407
|
+
|
|
408
|
+
Bounds: `MAX_GENERATION_TURNS = 8` (outline + 5 detail turns for 10 specs +
|
|
409
|
+
audit + one repair); every turn persists its snapshot (`saveBlueprintSnapshot`)
|
|
410
|
+
and records its own `agent_invocations` row; generation fences are stripped
|
|
411
|
+
from the transcript exactly like `blueprint-draft` blocks (the raw reply
|
|
412
|
+
survives in `raw_content`). Wire: `blueprint.generating { phase:
|
|
413
|
+
outline|details|audit|repair, from, to, total, turn, totalTurns }` announces
|
|
414
|
+
each phase; every intermediate `blueprint.done` carries `continuing: true` +
|
|
415
|
+
`snapshot.generation` (the client keeps `busy`, appends no bubble, refreshes
|
|
416
|
+
the panel); the final frame carries the descriptor without `continuing`.
|
|
417
|
+
**Resume:** `POST /conversations/:id/repair-snapshot` now also resumes a
|
|
418
|
+
halted drive — when no rejection is pending and the persisted snapshot still
|
|
419
|
+
has unfilled specs it answers `202 { kind: 'resume' }` and continues from
|
|
420
|
+
the next unfilled range (turn ordinal re-derived from what is already
|
|
421
|
+
written), which the readiness panel exposes as **Continue generating**
|
|
422
|
+
(`snapshot-halted` notice: "N of M specs written in full"). Providers without
|
|
423
|
+
`nativeResume` get the `GENERATION MODE: single response` line appended to
|
|
424
|
+
each user turn and keep the single-snapshot behaviour (never driven).
|
|
425
|
+
|
|
426
|
+
Client: `BuilderSnapshotState` gains `{ status: 'generating', generation }`
|
|
427
|
+
and `accepted.generationHalted`; `BuilderGenerationProgress` renders the
|
|
428
|
+
phase label ("Writing specs 3–4 of 8 in full…"), a `turn x/y` pill and a REAL
|
|
429
|
+
ratio from the descriptor (falls back to the streaming spec count for
|
|
430
|
+
single-response providers); `BuilderConversation` keeps that progress chip
|
|
431
|
+
between batched turns instead of the generic thinking chip; readiness
|
|
432
|
+
`specs` step params carry `written` (specs with a body). **Honest audit
|
|
433
|
+
during the drive:** an outline's empty bodies are NOT audit failures — while
|
|
434
|
+
`snapshot.status === 'generating'` `deriveReadiness(…, { generating })` puts
|
|
435
|
+
the specs + audit steps in the `writing` state ("2 of 8 written" / "after the
|
|
436
|
+
specs are written", spinner, batch hint) and lists NO issues; a halted partial
|
|
437
|
+
batch keeps only the issues of WRITTEN specs (the unwritten tail simply waits);
|
|
438
|
+
the panel's spec card shows "writing…" / "not written yet" instead of
|
|
439
|
+
"0 acceptance criteria" for an unwritten entry. i18n `builder:generation.*`,
|
|
440
|
+
`builder:snapshot.halted.*`, `builder:readiness.*.writing`,
|
|
441
|
+
`builder:panel.specWriting|specPendingBody` ×8.
|
|
178
442
|
|
|
179
443
|
## Orchestrated commit (register-project-LAST)
|
|
180
444
|
|
|
@@ -244,24 +508,120 @@ day-0 blueprint generation. Kimi cannot be selected for that generation turn.
|
|
|
244
508
|
Launching the already committed M1/M2+ tickets is ordinary Batch rail
|
|
245
509
|
execution and can use Kimi.
|
|
246
510
|
|
|
247
|
-
- **Launch Milestone
|
|
248
|
-
`
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
511
|
+
- **Launch Milestone N is SERVER-owned** (premium-milestone-progress D3):
|
|
512
|
+
`POST /api/projects/:id/blueprint/milestones/:n/launch { mode }`
|
|
513
|
+
(`client/src/lib/milestone-launch.ts` `launchMilestone` is one POST; the
|
|
514
|
+
old browser-local `MilestoneSequencerContext` + its `localStorage` plan are
|
|
515
|
+
GONE — `dropLegacySequentialPlans()` forgets the leftover key on load).
|
|
516
|
+
`server/milestone-chain.ts` `MilestoneChainManager` gathers the `M<n>`
|
|
517
|
+
`todo` tickets, chunks them (≤3, `chainRailName` → `M<n>` / `M<n> · k`),
|
|
518
|
+
persists ONE `milestone_launch_chains` row (`server/milestone-chain-store.ts`,
|
|
519
|
+
migration 58, partial unique index = one non-terminal chain per milestone;
|
|
520
|
+
CAS `updateChain`) and launches chunk 1 through the app's OWN rails launch
|
|
521
|
+
route over loopback (`server/internal-api.ts`, lifted from the MCP tools'
|
|
522
|
+
`apiCall`) so every existing guard applies and each 4xx becomes a typed
|
|
523
|
+
`pause_reason` (`launch_rejected:<error>`). **Sequential (default)** chains
|
|
524
|
+
the next chunk when the in-flight chunk's DELIVERY settles — the manager taps
|
|
525
|
+
the project's bound broadcast for `rail.pr_state` (the engine's
|
|
526
|
+
`onLoopRunFinished` fires BEFORE the delivery row leaves `building`, so it is
|
|
527
|
+
only the delivery-less shared-cwd fallback, recording `last_run_outcome` to
|
|
528
|
+
name the pause reason) — and **STACKS** it: chunk k+1 launches with
|
|
529
|
+
`baseBranch = chunk k's delivered branch` (the rails launch route's new
|
|
530
|
+
`baseBranch` param → `resolveIntegrationBranch({ explicit })`, recorded as
|
|
531
|
+
the delivery's `base_branch`, so a walking skeleton accumulates without
|
|
532
|
+
waiting for a merge). `no_changes` keeps the previous head; failure /
|
|
533
|
+
stall / stop / launch refusal / missing head / discarded head / lost run
|
|
534
|
+
PAUSE the chain (`chunk_failed | chunk_stalled | chunk_stopped |
|
|
535
|
+
launch_rejected:<e> | head_missing | head_discarded | run_lost`) — never
|
|
536
|
+
skip ahead. `POST …/blueprint/chains/:id/resume` **retries the chunk that failed**
|
|
537
|
+
(the row remembers it in `retry_chunk`, set by every chunk-failure pause:
|
|
538
|
+
`chunk_failed | chunk_stalled | chunk_stopped | provider_limit | run_lost`;
|
|
539
|
+
`launch_rejected` / `head_missing` keep launching the NEXT chunk) from the
|
|
540
|
+
current head (409 `head_missing` when the branch is gone) — run 10dedd5a
|
|
541
|
+
showed the old resume skipping to tickets 4–6 while 1–3 had failed. The
|
|
542
|
+
retry REUSES the failed attempt's rail when no undecided delivery sits on
|
|
543
|
+
it any more (`activeDeliveryForRail` null — e.g. after Discard), else takes
|
|
544
|
+
a fresh rail so the failed delivery stays reviewable; the launched entry
|
|
545
|
+
for that chunk is replaced (delivery rows keep the history). A NEW chain for
|
|
546
|
+
the same milestone likewise reuses a rail already named for the chunk
|
|
547
|
+
(`io.findRailByName` → `rails-store` `getRails`) when it holds no undecided
|
|
548
|
+
delivery, so relaunching M1 never piles up duplicate "M1 · 1" rails;
|
|
549
|
+
`…/cancel` stops the chain and leaves in-flight rails alone. Startup
|
|
550
|
+
recovery (`recoverOnStartup`, run once the HTTP server listens) replays a
|
|
551
|
+
chunk that settled while the server was down exactly once.
|
|
552
|
+
**Wave checkpoints (D9).** The row carries `auto_advance` (default 1 for
|
|
553
|
+
API callers; the UI sends the user's stored preference
|
|
554
|
+
`localStorage['specrails-desktop:milestone-auto-advance']`, default OFF)
|
|
555
|
+
and a non-terminal status `awaiting_approval`. When a chunk's delivery
|
|
556
|
+
settles successfully and auto-advance is off (and chunks remain), the
|
|
557
|
+
manager records the head and parks the chain at `awaiting_approval`
|
|
558
|
+
(`afterChunkSuccess`) instead of launching — a HEALTHY decision point, unlike
|
|
559
|
+
`paused` whose Resume retries the SAME chunk. `…/chains/:id/resume` launches
|
|
560
|
+
the NEXT chunk from `awaiting_approval` too; `PATCH …/chains/:id
|
|
561
|
+
{ autoAdvance }` (`setAutoAdvance`) flips the flag on any active chain and,
|
|
562
|
+
when turning it on at a checkpoint, launches immediately. `awaiting_approval`
|
|
563
|
+
counts as active (one non-terminal chain per milestone, cancellable) and is
|
|
564
|
+
ignored by startup recovery (waiting for the user is the point); a failure
|
|
565
|
+
always PAUSES regardless of the flag (checkpoints are reached only by
|
|
566
|
+
success). Client: `launchMilestone(projectId, n, mode, { autoAdvance })`,
|
|
567
|
+
`setChainAutoAdvance`, `readMilestoneAutoAdvance`/`saveMilestoneAutoAdvance`
|
|
568
|
+
(`milestone-launch.ts`); `chainAtCheckpoint` + `isMilestoneLaunchable`
|
|
569
|
+
excludes a checkpoint (`milestone-progress.ts`). **Parallel**
|
|
570
|
+
launches every chunk at once from the integration branch (row recorded
|
|
571
|
+
`completed` so the progress model still orders the rails). Kill switch
|
|
572
|
+
`SPECRAILS_MILESTONE_CHAIN=false` ⇒ parallel, no row. Merging a STACKED
|
|
573
|
+
chunk sweeps its merged ancestors (`sweepMergedChainAncestors` in
|
|
574
|
+
`rail-pr-decision.ts`: chain-local, `git merge-base --is-ancestor`, same
|
|
575
|
+
CAS + ticket effect + Jira hook) and merge-local integrates into the CHAIN's
|
|
576
|
+
integration branch, never the feature base (`mergeLocalTargetBranch`);
|
|
577
|
+
discarding a delivery a later chunk was built on pauses its chain
|
|
578
|
+
(`pauseChainsForDiscardedHead`) and the decision surfaces warn first
|
|
579
|
+
(`discardStackedNote` ×3 namespaces). Offered on the Builder done screen and
|
|
580
|
+
the sidebar entry with the Sequential | Parallel toggle.
|
|
581
|
+
- **Milestone progress is SERVER-derived** (premium-milestone-progress D2):
|
|
582
|
+
`server/milestone-progress.ts` `deriveMilestoneProgress` builds, per
|
|
583
|
+
milestone, counts by spec state (`total/done/onReview/inProgress/todo/failed`
|
|
584
|
+
— `failed` = specs back at `todo` whose NEWEST delivery unit failed), the
|
|
585
|
+
milestone's rails (active runs + non-terminal deliveries, chunk-ordered), the
|
|
586
|
+
chain snapshot and a derived `state`
|
|
587
|
+
(`done` ⇐ every spec done · `delivered` ⇐ nothing pending, ≥1 on_review ·
|
|
588
|
+
`running` ⇐ in-progress or a live chain · `committed` · stored status).
|
|
589
|
+
`GET /:id/blueprint` returns `{ blueprint, progress }`;
|
|
590
|
+
`MilestoneProgressBroadcaster` (tapped from the bound broadcast on
|
|
591
|
+
ticket/rail/delivery/run/chain messages, 150 ms debounce, memoized "no
|
|
592
|
+
blueprint") re-broadcasts `blueprint.milestone_progress` and persists
|
|
593
|
+
`status:'done'` once via `markMilestoneDone` (+ `blueprint.milestone_completed`).
|
|
594
|
+
Display ALWAYS uses the derived state — a delivered milestone reads
|
|
595
|
+
"8 of 8 delivered · 0 done", never done/complete.
|
|
259
596
|
- **Sidebar re-entry** (`BuilderSidebarEntry`, mounted in
|
|
260
597
|
`ProjectRightSidebar` + `AgentWorkspaceSidebar`): visible iff
|
|
261
|
-
`GET /api/projects/:id/blueprint`
|
|
262
|
-
(
|
|
263
|
-
|
|
264
|
-
|
|
598
|
+
`GET /api/projects/:id/blueprint` returns a blueprint (404 = hidden).
|
|
599
|
+
Reads `useMilestoneProgress(projectId)` (cached per project, live over WS —
|
|
600
|
+
NO board fetch on open) and renders one `MilestoneCard`
|
|
601
|
+
(`MilestoneProgressCard.tsx`: segmented bar done/in review/in progress/
|
|
602
|
+
failed/pending, honest counts, state pill, per-rail rows with decision pill +
|
|
603
|
+
elapsed + Review → `/review/:prDeliveryId`, chain row with k of n / waiting /
|
|
604
|
+
paused reason + Resume / Cancel, and at a wave checkpoint "Rail k of n
|
|
605
|
+
delivered — launch rail k+1?" with **Launch next rail** + the chain-level
|
|
606
|
+
**Continue automatically** switch (`MilestoneAutoAdvanceToggle`, PATCH; also
|
|
607
|
+
saves the preference) + Cancel; a running auto-off chain notes "stops after
|
|
608
|
+
this rail") per milestone in a 320 px flyout. Actions: Launch M1 (while
|
|
609
|
+
`isMilestoneLaunchable`) with the mode toggle and, for sequential mode, the
|
|
610
|
+
same auto-continue switch (stored preference, default OFF — the launch toast
|
|
611
|
+
then says "you'll be asked before each next rail") + Generate M<next> (first
|
|
612
|
+
`planned` milestone > 1). The Builder done screen shows the same live card
|
|
613
|
+
after Launch (`BuilderDoneMilestone`) and the auto-continue switch next to
|
|
614
|
+
Launch M1 — "Open the project" stays the exit. **Review in Mission mode:**
|
|
615
|
+
every Review button navigates to `/review/:prDeliveryId`, a Board ROUTE
|
|
616
|
+
that Mission mode never renders (the button "did nothing"); the route is
|
|
617
|
+
now a third `modalize` surface (`global-route-mode-transition.ts`
|
|
618
|
+
`reviewDeliveryIdForPath`) — `App.tsx` opens the SAME `ReviewPacketPage`
|
|
619
|
+
embedded in a mission modal (`prDeliveryId` + `onClose` props, no New
|
|
620
|
+
Mission reset), and switching back to Board routes to the page. App-level toasts
|
|
621
|
+
(`useMilestoneNotifications`): later chunk launched, chain paused
|
|
622
|
+
(+ Resume), wave checkpoint (once per rail: **Launch next rail** +
|
|
623
|
+
**Auto-continue** actions), milestone delivered (+ Review), milestone
|
|
624
|
+
complete. i18n `builder:milestoneProgress.chain.*` / `toast.*` ×8.
|
|
265
625
|
- **Board classification**: new Builder tickets use
|
|
266
626
|
`source='project-builder'` + `created_by='project-builder'`. For projects
|
|
267
627
|
created before that source existed, `DashboardPage` also treats
|
|
@@ -305,23 +665,25 @@ execution and can use Kimi.
|
|
|
305
665
|
retain priority, short summary, domain + `M<n>` labels, and prerequisites.
|
|
306
666
|
`blueprint.json` deliberately stores only the existing milestone skeleton
|
|
307
667
|
with `status='committed'` and advisory `ticketIds`; it has no detailed-M2-
|
|
308
|
-
per-milestone schema. On 201 the shell calls `onCommitted`,
|
|
309
|
-
|
|
668
|
+
per-milestone schema. On 201 the shell calls `onCommitted`, refreshes the live
|
|
669
|
+
milestone-progress cache (no-store refetch), closes, and the next CTA
|
|
310
670
|
resolves to the first later milestone still `planned`. Jira-connected
|
|
311
671
|
projects ride the existing machinery on the store mutation.
|
|
312
672
|
|
|
313
673
|
## Tests
|
|
314
674
|
|
|
315
675
|
`server/blueprint-{draft-parser,render,store,chat-manager,commit,router}.test.ts`,
|
|
316
|
-
`server/
|
|
317
|
-
`
|
|
676
|
+
`server/json-tolerant.test.ts`, `server/offline-assemble.test.ts`,
|
|
677
|
+
`server/project-router.test.ts`, `server/desktop-db.test.ts` (migration 23);
|
|
678
|
+
client `src/lib/__tests__/{blueprint-draft,blueprint-readiness,milestone-launch}.test.ts`,
|
|
318
679
|
`src/hooks/__tests__/useBuilderSession.test.ts`,
|
|
319
|
-
`src/components/__tests__/{ProjectBuilder,BuilderSidebarEntry}.test.tsx`,
|
|
320
|
-
`src/pages/__tests__/DashboardPage.test.tsx`.
|
|
680
|
+
`src/components/__tests__/{ProjectBuilder,BuilderSidebarEntry,MilestoneGenerateShell}.test.tsx`,
|
|
681
|
+
and `src/pages/__tests__/DashboardPage.test.tsx`.
|
|
321
682
|
Locale parity covers the `builder` namespace ×8.
|
|
322
683
|
|
|
323
684
|
## Deferred (v2)
|
|
324
685
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
blueprint via the day-0 Builder, a live side-panel draft
|
|
686
|
+
Minimize-to-dock for Builder conversations (no `projectId` to tag — resume is
|
|
687
|
+
now the hero list instead), an orphan-dir startup sweeper, non-GitHub remotes,
|
|
688
|
+
editing an existing blueprint via the day-0 Builder, a live side-panel draft
|
|
689
|
+
during M2 generation, an app-driven repair turn for M2+ (ChatManager path).
|
|
@@ -102,10 +102,13 @@ Fresh no-change cards offer explicit **Mark done** and **Refine** outcomes. Mark
|
|
|
102
102
|
them to the backlog. Existing-PR no-change cards only dismiss their borrowed follow-up state.
|
|
103
103
|
|
|
104
104
|
**`merge-local` (remote-less acceptance).** Legal only without a real PR. Both surfaces confirm
|
|
105
|
-
because it
|
|
106
|
-
checkout; only a complete conflict-free result may advance
|
|
107
|
-
HEAD integration branch.
|
|
108
|
-
|
|
105
|
+
because it advances the local integration branch. Exact delivered commits are first assembled away
|
|
106
|
+
from the user's checkout; only a complete conflict-free result may advance the still-at-the-original-
|
|
107
|
+
HEAD integration branch. Compatible staged, unstaged, untracked and ignored local files survive;
|
|
108
|
+
Git refuses an overwrite with a retryable `merge_local_blocked` response. If the user is on another
|
|
109
|
+
branch or detached HEAD, a temporary checkout claims the base without switching the user's checkout.
|
|
110
|
+
A base already held by another worktree remains untouched and reports `integration_branch_busy`.
|
|
111
|
+
Both temporary checkouts use non-force cleanup, including after a Git runner exception.
|
|
109
112
|
|
|
110
113
|
## Launch and recovery wiring (`server/rail-isolated-launch.ts`)
|
|
111
114
|
|
|
@@ -125,7 +128,9 @@ actionable.
|
|
|
125
128
|
proven clean) — run-created build caches (`__pycache__`, `node_modules`) release cleanly, while an
|
|
126
129
|
ignored path that APPEARS after settlement, a failed/oversized capture (>400 paths → null), a
|
|
127
130
|
legacy row without a snapshot, or conflicting per-branch snapshots all preserve the worktree
|
|
128
|
-
(`durableSettlementIgnoredPaths`, cleanup-and-checkout-tolerate-run-artifacts).
|
|
131
|
+
(`durableSettlementIgnoredPaths`, cleanup-and-checkout-tolerate-run-artifacts). Authorized ignored
|
|
132
|
+
roots are archived as well: the snapshot grants release authority for their names, never deletion
|
|
133
|
+
authority for user edits or additions under those directories. Authenticated overlay roots are atomically renamed to a unique
|
|
129
134
|
persistent same-filesystem quarantine batch and revalidated there; automatic cleanup never unlinks that
|
|
130
135
|
quarantine, so a raced replacement or open-descriptor write remains recoverable. The batch root is
|
|
131
136
|
persisted before the first move and pointers are never evicted while their bytes remain on disk. A recreated
|
|
@@ -333,7 +338,8 @@ attached and ready for Verify so that explicit decision can commit terminal tick
|
|
|
333
338
|
reopening the stale attachment. A valid reopen returns to draft or ready according to GitHub;
|
|
334
339
|
an exact merge racing the action terminalizes, while an exact still-closed result reports failure.
|
|
335
340
|
- **merge-local** — builds the complete merge in an isolated temporary checkout and advances the
|
|
336
|
-
|
|
341
|
+
revalidated base only after all branches succeed, preserving compatible local work. A missing
|
|
342
|
+
immutable delivery object never falls back to a branch that may have moved.
|
|
337
343
|
|
|
338
344
|
Terminal discard/merge/completion inserts `rail_pr_ticket_effects` in the same SQLite transaction
|
|
339
345
|
as its decision and snapshots each ticket's non-null outcome owner. Before crossing into ticket JSON
|
|
@@ -411,10 +417,14 @@ the `expectedDecision` they rendered.
|
|
|
411
417
|
result and offers the confirmed Commit & retry push action above. A `recovery_unavailable` row
|
|
412
418
|
replaces that primary action with Check again; a clone without the original worktree/object never
|
|
413
419
|
claims that the implementation failed or that local progress was discarded. Ordinary Checkout is restricted
|
|
414
|
-
to a delivery with an immutable SHA; the server repeats that guard under the repo lock
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
the repository lock, never the pre-lock snapshot.
|
|
420
|
+
to a reviewable delivery with an immutable SHA; the server repeats that guard under the repo lock
|
|
421
|
+
and refuses unreadable main-checkout status. Compatible dirty edits are preserved by Git's
|
|
422
|
+
non-force checkout with ignored-file overwrite protection. Checkout re-reads the current delivery
|
|
423
|
+
branch and immutable `delivery_sha` after acquiring the repository lock, never the pre-lock snapshot.
|
|
424
|
+
A single succeeded local unit with `finalSha` can be checked out before creating any PR; an
|
|
425
|
+
unassembled batch cannot expose just its first unit as the whole result. Cleanup warnings from
|
|
426
|
+
unrelated preserved unit worktrees remain visible without blocking an available assembled branch.
|
|
427
|
+
A same-named local/remote branch must equal that
|
|
418
428
|
SHA before switching, and the final branch plus HEAD are verified afterward; divergence is
|
|
419
429
|
preserved and a failed fast-forward can never be reported as success. Logs remain available after settle. Buttons
|
|
420
430
|
disable in flight and apply the authoritative HTTP snapshot immediately.
|
|
@@ -478,6 +488,38 @@ one-shot, consumed by the launch) with `TargetPrLaunchDialog` confirming number/
|
|
|
478
488
|
MCP `specrails_rails(launch, targetPrNumber)` with operator-prompt teaching ("user names a PR →
|
|
479
489
|
pass it; never create a duplicate"). Fork PR push support and multi-PR batches remain out of scope.
|
|
480
490
|
|
|
491
|
+
## Explicit base branch + stacked chunks (premium-milestone-progress)
|
|
492
|
+
|
|
493
|
+
The launch body accepts an optional `baseBranch`: a LOCAL branch to base the isolated worktree on
|
|
494
|
+
instead of the integration branch. Shape-validated (`isValidBranchName`, 400 `invalid_base_branch`),
|
|
495
|
+
isolation-gated (400 `base_branch_requires_isolation` when loops are off, the launch would run in
|
|
496
|
+
the shared checkout, or isolation is unavailable), and resolved right before allocation
|
|
497
|
+
(`git rev-parse --verify --quiet refs/heads/<branch>`, 400 `invalid_base_branch` otherwise — a chain
|
|
498
|
+
never stacks on a guess). `launchIsolatedRail` threads it as `resolveIntegrationBranch({ explicit })`,
|
|
499
|
+
so the delivery row records it as `base_branch` and `deliverRailAsPr` creates the PR STACKED on it
|
|
500
|
+
(`gh pr create --base <branch>`). Today's only driver is the milestone launch chain
|
|
501
|
+
(`server/milestone-chain.ts`): chunk k+1 of a sequential "Launch Milestone" launches with
|
|
502
|
+
`baseBranch = chunk k's delivered branch`, so a greenfield walking skeleton accumulates without waiting
|
|
503
|
+
for a merge. MCP `specrails_rails(launch, baseBranch)` exposes the same parameter.
|
|
504
|
+
|
|
505
|
+
Two decision-side consequences keep the stacked lineage honest. **Ancestor sweep** — after any
|
|
506
|
+
`merged` transition (merge-local or poll-merge), `sweepMergedChainAncestors` walks the chain(s) the
|
|
507
|
+
merged delivery belongs to and settles every still-undecided sibling (`on_review | pr_draft |
|
|
508
|
+
pr_ready | pr_closed`) whose delivered head (`delivery_sha`, else a unit's `finalSha`) is PROVABLY an
|
|
509
|
+
ancestor of the chain's integration branch (`git merge-base --is-ancestor`) as `merged` through the
|
|
510
|
+
same CAS (own operation lease, released BEFORE `finalizeTransition` so the broadcast is not deferred
|
|
511
|
+
to a lease owner that never comes) + ticket effect (`done`) + Jira `onRailMerged(…, null)`;
|
|
512
|
+
`status_detail` records `merged as part of <railKey>`. Chain-local by design — a non-ancestor sibling
|
|
513
|
+
is untouched. **Merge-local target** — a stacked delivery's `base_branch` is the previous chunk's
|
|
514
|
+
FEATURE branch, so `runMergeLocalLocked` resolves `mergeLocalTargetBranch` (the chain's
|
|
515
|
+
`integration_branch` when the delivery belongs to a chain, else `base_branch`) for both the checkout
|
|
516
|
+
guard (`wrong_branch`) and the merge itself. **Discard of a stacked head** — `pauseChainsForDiscardedHead`
|
|
517
|
+
pauses every active chain whose launched chunks include the discarded delivery (`head_discarded`, head
|
|
518
|
+
rewound to the previous chunk's branch, else the integration branch) and broadcasts
|
|
519
|
+
`milestone.chain_changed`; the rail strip, the agent card and the review packet render
|
|
520
|
+
`discardStackedNote` in the discard confirm when the delivery is such a head
|
|
521
|
+
(`useStackedHeadDeliveryIds`).
|
|
522
|
+
|
|
481
523
|
## Ticket lifecycle — `on_review`
|
|
482
524
|
|
|
483
525
|
New status in `TicketStatus` / `VALID_STATUSES` (`server/ticket-store.ts`), between
|