specrails-desktop 2.48.0 → 2.49.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 (121) hide show
  1. package/client/dist/assets/{ActivityFeedPage-CGq6QhP5.js → ActivityFeedPage-FdZBSmTU.js} +1 -1
  2. package/client/dist/assets/{AgentBrowserCapture-C4GZ-kdf.js → AgentBrowserCapture-Dr8kZotA.js} +1 -1
  3. package/client/dist/assets/{AgentModeAnalyticsPane-_IPmL0ZS.js → AgentModeAnalyticsPane-DXQSjYe-.js} +2 -2
  4. package/client/dist/assets/{AgentModeCodePane-B5WG5DOP.js → AgentModeCodePane-DcVV6EBy.js} +2 -2
  5. package/client/dist/assets/AgentModeJobsPane-a9f-g56c.js +1 -0
  6. package/client/dist/assets/AgentsPage-CTv4UVdp.js +87 -0
  7. package/client/dist/assets/{AnalyticsPage-BUMsKtsJ.js → AnalyticsPage-BbXPcz3a.js} +1 -1
  8. package/client/dist/assets/CodePage-CIigb_O0.js +3 -0
  9. package/client/dist/assets/DesktopAnalyticsPage-DZhN5jVh.js +1 -0
  10. package/client/dist/assets/{DocsDialog-ccQETHKB.js → DocsDialog-DzrqPxuj.js} +2 -2
  11. package/client/dist/assets/{DocsPage-DzEgplB3.js → DocsPage-B1AUuk0U.js} +1 -1
  12. package/client/dist/assets/{ExportDropdown-B8bBGopr.js → ExportDropdown-IPutNYvD.js} +1 -1
  13. package/client/dist/assets/InteractiveJobComposer-jg3xE_GX.js +19 -0
  14. package/client/dist/assets/{JobDetailModal-BYXmuTDe.js → JobDetailModal-BS-htwqZ.js} +1 -1
  15. package/client/dist/assets/JobDetailPage-CKYtQV5a.js +1 -0
  16. package/client/dist/assets/JobsPage-DFJm0bw0.js +1 -0
  17. package/client/dist/assets/{LoopBuilderPage-DQE-7Vby.js → LoopBuilderPage-ujkA2U0T.js} +2 -2
  18. package/client/dist/assets/LoopPreviewModal-BYOrnmcp.js +1 -0
  19. package/client/dist/assets/LoopsPage-DRcT1ikP.js +1 -0
  20. package/client/dist/assets/{MinimizedChatsContext-UpnqccPx.js → MinimizedChatsContext-CX1i2kQ8.js} +1 -1
  21. package/client/dist/assets/{PluginsPage-CjgVvmpW.js → PluginsPage-BR9ZTqxR.js} +2 -2
  22. package/client/dist/assets/{ProjectSettingsDialog-D6vBCoay.js → ProjectSettingsDialog-SAfCqXzC.js} +1 -1
  23. package/client/dist/assets/RepositoryDeliveries-DqmlG0Zs.js +5 -0
  24. package/client/dist/assets/{RepositoryScopeSelector-0Gz_fR1E.js → RepositoryScopeSelector-DG8KKPrm.js} +1 -1
  25. package/client/dist/assets/ReviewPacketPage-BoqahYNC.js +4 -0
  26. package/client/dist/assets/TemplatePreviewModal-CK5OoImj.js +1 -0
  27. package/client/dist/assets/TicketDetailModalContext-D2MEhRwJ.js +14 -0
  28. package/client/dist/assets/{Trans-9pHTy2fH.js → Trans-BLljsfHR.js} +1 -1
  29. package/client/dist/assets/agent-B76BhAyE.js +1 -0
  30. package/client/dist/assets/agent-BII69Nc0.js +1 -0
  31. package/client/dist/assets/agent-BIO4q6b0.js +1 -0
  32. package/client/dist/assets/agent-ButS6kwl.js +1 -0
  33. package/client/dist/assets/agent-CVeMLIu4.js +1 -0
  34. package/client/dist/assets/agent-CYFBDwFD.js +1 -0
  35. package/client/dist/assets/agent-D3jkMipn.js +1 -0
  36. package/client/dist/assets/agent-DSrflYFL.js +1 -0
  37. package/client/dist/assets/{formatDistanceToNow-w_2U5pm0.js → formatDistanceToNow-CbQZkD8M.js} +1 -1
  38. package/client/dist/assets/{getTimezoneOffsetInMilliseconds-BRymg1Kk.js → getTimezoneOffsetInMilliseconds-pBVK3rJC.js} +1 -1
  39. package/client/dist/assets/index-CjAPu3wb.js +80 -0
  40. package/client/dist/assets/index-UDzndadF.css +2 -0
  41. package/client/dist/assets/{jira-api-BqHbSdxK.js → jira-api-f-sYCIuX.js} +1 -1
  42. package/client/dist/assets/packet-BTRySHoQ.js +1 -0
  43. package/client/dist/assets/packet-BsRimSfZ.js +1 -0
  44. package/client/dist/assets/packet-C0pcMd6x.js +1 -0
  45. package/client/dist/assets/packet-C8qzvgrZ.js +1 -0
  46. package/client/dist/assets/packet-DF-iwqqY.js +1 -0
  47. package/client/dist/assets/packet-DQ_GnT2b.js +1 -0
  48. package/client/dist/assets/packet-Nb8BLI5h.js +1 -0
  49. package/client/dist/assets/packet-r7J14m89.js +1 -0
  50. package/client/dist/assets/{project-repositories-BsCs1PPA.js → project-repositories-DAjJbFhm.js} +1 -1
  51. package/client/dist/assets/{spending-hcgxHP4O.js → spending-DJBbdJyD.js} +1 -1
  52. package/client/dist/assets/{useDesktop-CAisjKWC.js → useDesktop-BJ8IRBRt.js} +1 -1
  53. package/client/dist/assets/useRuntimeRuns-Ch1KUc7k.js +1 -0
  54. package/client/dist/assets/{useSharedWebSocket-BHIfW4QT.js → useSharedWebSocket-BN3Lmehi.js} +2 -2
  55. package/client/dist/index.html +17 -17
  56. package/docs/guide/de/integrations/6-agent-chat.md +8 -0
  57. package/docs/guide/en/integrations/6-agent-chat.md +8 -0
  58. package/docs/guide/es/integrations/6-agent-chat.md +8 -0
  59. package/docs/guide/fr/integrations/6-agent-chat.md +8 -0
  60. package/docs/guide/it/integrations/6-agent-chat.md +8 -0
  61. package/docs/guide/ja/integrations/6-agent-chat.md +8 -0
  62. package/docs/guide/pt/integrations/6-agent-chat.md +8 -0
  63. package/docs/guide/zh/integrations/6-agent-chat.md +8 -0
  64. package/docs/internals/mission-rail-cards.md +130 -0
  65. package/docs/internals/review-packet.md +16 -3
  66. package/docs/mcp.md +24 -1
  67. package/package.json +1 -1
  68. package/server/dist/agent-chat-manager.js +97 -2
  69. package/server/dist/agent-chat-router.js +57 -1
  70. package/server/dist/agent-context-resolver.js +15 -0
  71. package/server/dist/agent-cwd-manager.js +1 -1
  72. package/server/dist/agent-failure-briefing.js +90 -0
  73. package/server/dist/agent-fence-promotion.js +8 -1
  74. package/server/dist/agent-operator-prompt.js +69 -1
  75. package/server/dist/agent-runtime-controls-router.js +17 -0
  76. package/server/dist/agent-store.js +40 -0
  77. package/server/dist/delivery-evidence.js +159 -0
  78. package/server/dist/desktop-db.js +11 -0
  79. package/server/dist/feature-flags.js +20 -0
  80. package/server/dist/mcp/guide.js +7 -0
  81. package/server/dist/mcp/tools/jobs.js +91 -4
  82. package/server/dist/mcp/tools/rails.js +2 -2
  83. package/server/dist/mission-run-notify.js +170 -0
  84. package/server/dist/rail-isolated-launch.js +47 -0
  85. package/server/dist/rail-launch-parser.js +182 -0
  86. package/server/dist/rail-pr-store.js +20 -0
  87. package/server/dist/rails-router.js +98 -5
  88. package/server/dist/review-packet.js +35 -2
  89. package/client/dist/assets/AgentModeJobsPane-w1N_HIXh.js +0 -1
  90. package/client/dist/assets/AgentsPage-CmwnnT46.js +0 -87
  91. package/client/dist/assets/CodePage-PjgywJPS.js +0 -3
  92. package/client/dist/assets/DesktopAnalyticsPage-BXY1pH4y.js +0 -1
  93. package/client/dist/assets/InteractiveJobComposer-CEwGkJ8F.js +0 -19
  94. package/client/dist/assets/JobDetailPage-CWp6MwJj.js +0 -1
  95. package/client/dist/assets/JobsPage-D1O1uyG8.js +0 -1
  96. package/client/dist/assets/LoopPreviewModal-Bk7zIXD5.js +0 -1
  97. package/client/dist/assets/LoopsPage-CqG7mx56.js +0 -1
  98. package/client/dist/assets/RepositoryDeliveries-C36ipKp_.js +0 -5
  99. package/client/dist/assets/ReviewPacketPage-Dr9RYs7q.js +0 -1
  100. package/client/dist/assets/TemplatePreviewModal-sCMHYFJo.js +0 -1
  101. package/client/dist/assets/TicketDetailModalContext-C5GgJlEt.js +0 -14
  102. package/client/dist/assets/agent-BdT0SB1D.js +0 -1
  103. package/client/dist/assets/agent-Bh9K7iEt.js +0 -1
  104. package/client/dist/assets/agent-C4ibdFk9.js +0 -1
  105. package/client/dist/assets/agent-CSFY22Zo.js +0 -1
  106. package/client/dist/assets/agent-DAnqxUn8.js +0 -1
  107. package/client/dist/assets/agent-DTQiu0Mi.js +0 -1
  108. package/client/dist/assets/agent-T54WuR_Y.js +0 -1
  109. package/client/dist/assets/agent-i75WDZOA.js +0 -1
  110. package/client/dist/assets/brain-DI9lHkKY.js +0 -1
  111. package/client/dist/assets/format-duration-NJVPOPBG.js +0 -1
  112. package/client/dist/assets/index-D0ScFZvR.css +0 -2
  113. package/client/dist/assets/index-DORCrgbI.js +0 -77
  114. package/client/dist/assets/packet-BNW8ODQw.js +0 -1
  115. package/client/dist/assets/packet-CXYvfdjy.js +0 -1
  116. package/client/dist/assets/packet-CtIlB8YE.js +0 -1
  117. package/client/dist/assets/packet-DA1SJkMH.js +0 -1
  118. package/client/dist/assets/packet-DYcWfBip.js +0 -1
  119. package/client/dist/assets/packet-DseX9yKC.js +0 -1
  120. package/client/dist/assets/packet-_m5aOy_d.js +0 -1
  121. package/client/dist/assets/packet-qOemUAm3.js +0 -1
@@ -204,7 +204,7 @@
204
204
  .specrails-splash__mark .pill { opacity: 1; }
205
205
  }
206
206
  </style>
207
- <script type="module" crossorigin src="/assets/index-DORCrgbI.js"></script>
207
+ <script type="module" crossorigin src="/assets/index-CjAPu3wb.js"></script>
208
208
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-CNC7AqOf.js">
209
209
  <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-zu2_FqZY.js">
210
210
  <link rel="modulepreload" crossorigin href="/assets/react-dom-DiWzBDP9.js">
@@ -212,7 +212,7 @@
212
212
  <link rel="modulepreload" crossorigin href="/assets/chunk-KS7C4IRE-DEKu087k.js">
213
213
  <link rel="modulepreload" crossorigin href="/assets/activity-DiyjsgQV.js">
214
214
  <link rel="modulepreload" crossorigin href="/assets/addspec-BOvgmcgk.js">
215
- <link rel="modulepreload" crossorigin href="/assets/agent-Bh9K7iEt.js">
215
+ <link rel="modulepreload" crossorigin href="/assets/agent-BIO4q6b0.js">
216
216
  <link rel="modulepreload" crossorigin href="/assets/agentRuntime-bAAjw1vw.js">
217
217
  <link rel="modulepreload" crossorigin href="/assets/agents-CTwbx-fb.js">
218
218
  <link rel="modulepreload" crossorigin href="/assets/agentstudio-CN2pokQu.js">
@@ -234,22 +234,22 @@
234
234
  <link rel="modulepreload" crossorigin href="/assets/mcp-CUAN6ORK.js">
235
235
  <link rel="modulepreload" crossorigin href="/assets/narration-n3hOA7LY.js">
236
236
  <link rel="modulepreload" crossorigin href="/assets/nav-Ch-zHBG5.js">
237
- <link rel="modulepreload" crossorigin href="/assets/packet-_m5aOy_d.js">
237
+ <link rel="modulepreload" crossorigin href="/assets/packet-BTRySHoQ.js">
238
238
  <link rel="modulepreload" crossorigin href="/assets/settings-HZSu-K4l.js">
239
239
  <link rel="modulepreload" crossorigin href="/assets/setup-DpqzaEEA.js">
240
240
  <link rel="modulepreload" crossorigin href="/assets/specs-DII0C8gw.js">
241
241
  <link rel="modulepreload" crossorigin href="/assets/terminal-BAzpHtpY.js">
242
242
  <link rel="modulepreload" crossorigin href="/assets/tickets-W6pBmbrX.js">
243
243
  <link rel="modulepreload" crossorigin href="/assets/createLucideIcon-Bjbbp_KS.js">
244
- <link rel="modulepreload" crossorigin href="/assets/useSharedWebSocket-BHIfW4QT.js">
245
- <link rel="modulepreload" crossorigin href="/assets/Trans-9pHTy2fH.js">
246
- <link rel="modulepreload" crossorigin href="/assets/useDesktop-CAisjKWC.js">
244
+ <link rel="modulepreload" crossorigin href="/assets/useSharedWebSocket-BN3Lmehi.js">
245
+ <link rel="modulepreload" crossorigin href="/assets/Trans-BLljsfHR.js">
246
+ <link rel="modulepreload" crossorigin href="/assets/useDesktop-BJ8IRBRt.js">
247
247
  <link rel="modulepreload" crossorigin href="/assets/provider-capabilities-Vi1ZGvAK.js">
248
248
  <link rel="modulepreload" crossorigin href="/assets/save-B6wGWwBR.js">
249
- <link rel="modulepreload" crossorigin href="/assets/spending-hcgxHP4O.js">
249
+ <link rel="modulepreload" crossorigin href="/assets/spending-DJBbdJyD.js">
250
250
  <link rel="modulepreload" crossorigin href="/assets/sparkles-DrY8hb0X.js">
251
251
  <link rel="modulepreload" crossorigin href="/assets/x-CBMI9ejn.js">
252
- <link rel="modulepreload" crossorigin href="/assets/MinimizedChatsContext-UpnqccPx.js">
252
+ <link rel="modulepreload" crossorigin href="/assets/MinimizedChatsContext-CX1i2kQ8.js">
253
253
  <link rel="modulepreload" crossorigin href="/assets/circle-x-CMYsN4ZH.js">
254
254
  <link rel="modulepreload" crossorigin href="/assets/download-CFHyyHcw.js">
255
255
  <link rel="modulepreload" crossorigin href="/assets/file-text-CFT-xXKN.js">
@@ -267,18 +267,18 @@
267
267
  <link rel="modulepreload" crossorigin href="/assets/utils-BC4DHlz2.js">
268
268
  <link rel="modulepreload" crossorigin href="/assets/dialog-xXB2q5HJ.js">
269
269
  <link rel="modulepreload" crossorigin href="/assets/lib-CGdyxIWg.js">
270
- <link rel="modulepreload" crossorigin href="/assets/getTimezoneOffsetInMilliseconds-BRymg1Kk.js">
271
- <link rel="modulepreload" crossorigin href="/assets/formatDistanceToNow-w_2U5pm0.js">
270
+ <link rel="modulepreload" crossorigin href="/assets/getTimezoneOffsetInMilliseconds-pBVK3rJC.js">
271
+ <link rel="modulepreload" crossorigin href="/assets/formatDistanceToNow-CbQZkD8M.js">
272
272
  <link rel="modulepreload" crossorigin href="/assets/button-Ccc9Dc-D.js">
273
- <link rel="modulepreload" crossorigin href="/assets/jira-api-BqHbSdxK.js">
274
- <link rel="modulepreload" crossorigin href="/assets/project-repositories-BsCs1PPA.js">
275
- <link rel="modulepreload" crossorigin href="/assets/RepositoryScopeSelector-0Gz_fR1E.js">
276
- <link rel="modulepreload" crossorigin href="/assets/TicketDetailModalContext-C5GgJlEt.js">
273
+ <link rel="modulepreload" crossorigin href="/assets/jira-api-f-sYCIuX.js">
274
+ <link rel="modulepreload" crossorigin href="/assets/project-repositories-DAjJbFhm.js">
275
+ <link rel="modulepreload" crossorigin href="/assets/RepositoryScopeSelector-DG8KKPrm.js">
276
+ <link rel="modulepreload" crossorigin href="/assets/TicketDetailModalContext-D2MEhRwJ.js">
277
277
  <link rel="modulepreload" crossorigin href="/assets/core-B3mKBIh5.js">
278
- <link rel="modulepreload" crossorigin href="/assets/RepositoryDeliveries-C36ipKp_.js">
279
- <link rel="modulepreload" crossorigin href="/assets/format-duration-NJVPOPBG.js">
278
+ <link rel="modulepreload" crossorigin href="/assets/RepositoryDeliveries-DqmlG0Zs.js">
279
+ <link rel="modulepreload" crossorigin href="/assets/useRuntimeRuns-Ch1KUc7k.js">
280
280
  <link rel="modulepreload" crossorigin href="/assets/event-Bt7f3wXe.js">
281
- <link rel="stylesheet" crossorigin href="/assets/index-D0ScFZvR.css">
281
+ <link rel="stylesheet" crossorigin href="/assets/index-UDzndadF.css">
282
282
  </head>
283
283
  <body>
284
284
  <div id="specrails-splash" aria-hidden="false" role="status" aria-label="Loading Specrails">
@@ -70,6 +70,14 @@ Antworten strömen flüssig herein und landen bereits formatiert (Überschriften
70
70
  - **Missionen wechseln.** Der Missions-Selektor neben dem Projekt-Selektor listet deine Unterhaltungen von neu nach alt — ein pulsierender Punkt markiert die, an denen der Agent noch arbeitet, und ein kleines Badge zählt die Nachrichten in der Warteschlange. Die Suche erscheint, sobald die Liste mehr als acht Einträge hat, und das Papierkorb-Symbol jeder Zeile löscht eine Mission nach einer kurzen Inline-Bestätigung — auch eine laufende (es warnt zuerst und stoppt dann den Agenten).
71
71
  - **Eine Mission von überall finden.** Drücke **⌘K** (oder den **Suchen**-Button in der Seitenleiste im Agent-Modus) und tippe irgendein Fragment, an das du dich erinnerst — ein Wort aus dem Titel *oder* aus dem, was darin gesagt wurde. Missionen treffen sofort per Titel aus dem Speicher, einen Herzschlag später kommen die Inhaltstreffer mit hervorgehobener Passage dazu, plus angepinntes Projekt und letzte Aktivität. Im Agent-Modus sind Missionen die erste Gruppe; auf dem Board stehen sie direkt nach den Projekten. Enter öffnet die Mission.
72
72
 
73
+ ## Launch-Karten und Run-Karten
74
+
75
+ - **Der Agent schlägt vor, du drückst Play.** Bitte ihn, Specs auf ein Rail zu legen („bereite #12 und #14 auf einem Rail vor“), und statt es zu beschreiben, gibt er dir eine **Launch-Karte**: das Rail (oder ein neues), die Specs, Engine, Modell, Aufwand, Profil, Loop, optional Ziel-PR und Basis-Branch — alles mit seiner Empfehlung vorausgefüllt und alles editierbar. Ändere, was du willst, und drücke **Play**. Sagst du „starte es jetzt“, startet er direkt.
76
+ - **Eine Karte begleitet den Run.** Nach Play friert der Vorschlag als „Gestartet → Rail N“ ein und eine **Run-Karte** übernimmt: Status, verstrichene Zeit, was die Pipeline gerade tut, Log-Button und Stopp. Ist die Arbeit bereit zur Prüfung, wird dieselbe Karte zur bekannten PR-Karte (PR erstellen, lokal integrieren, verwerfen).
77
+ - **Fehler melden sich.** Schlägt ein Run fehl oder bleibt er hängen, zeigt die Karte den Grund im Klartext und bietet die echten Optionen — Fortsetzen, Wiederherstellen, Freigeben, Neu starten, Verwerfen — und der Agent postet ein kurzes Briefing, was passiert ist und was er empfiehlt. Er setzt nie von allein fort oder startet neu; du entscheidest über die Karte.
78
+ - **Ohne Git keine PR-Phase.** Ein Projekt ohne Git-Repository (oder ohne Commits) bekommt trotzdem eine Run-Karte — Fortschritt, Log, Fehler und Wiederherstellung —, aber es gibt keinen Branch und keinen PR-Schritt; die Karte sagt das.
79
+ - **`@rail-N`.** Tippe `@` und wähle ein Rail, um es in deiner Nachricht zu referenzieren, mit seinen Specs und ob es frei, belegt oder in Entscheidung ist.
80
+
73
81
  ## Ein paar Dinge, die du wissen solltest
74
82
 
75
83
  - **Ausführen und Autonom kosten Geld**, weil sie KI laufen lassen. Der Agent hebt kostenverursachende Aktionen hervor, bevor er sie ausführt; belasse die Stufe bei Beobachten oder Bearbeiten, wenn du nur schauen und aufräumen willst.
@@ -75,6 +75,14 @@ Replies stream in smoothly and land formatted (headings, tables, lists), each wi
75
75
  - **Switch missions.** The mission selector next to the project picker lists your conversations newest-first — a pulsing dot marks the ones where the agent is still working, and a small badge counts queued messages. Search appears when the list grows past eight, and the trash icon on each row deletes a mission after a quick inline confirm — even a running one (it warns first, then stops the agent).
76
76
  - **Find a mission from anywhere.** Press **⌘K** (or the sidebar **Search** button in Agent Mode) and type any fragment you remember — a word from the title *or* from what was said inside. Missions match instantly by title from memory, and a heartbeat later the matches by content arrive with the matching passage highlighted, plus the pinned project and when it was last active. In Agent Mode missions are the first group; on the board they sit right after Projects. Enter opens the mission.
77
77
 
78
+ ## Rail launch cards and run cards
79
+
80
+ - **Propose, then you press Play.** Ask the agent to put specs on a rail ("prepare #12 and #14 for a rail") and, instead of describing it, it hands you a **launch card**: the rail (or a brand-new one), the specs, engine, model, effort, profile, loop, optional target PR and base branch — all pre-filled with its recommendation and all editable. Change what you like and press **Play**. Say "launch it now" and it launches directly instead.
81
+ - **One card follows the run.** After Play the proposal freezes into a "Launched → Rail N" stub and a **run card** takes over: status, elapsed time, what the pipeline is doing, a log button and Stop. When the work is ready to review, the same card becomes the familiar PR card (create the PR, integrate locally, discard).
82
+ - **Failures speak up.** If a run fails or stalls, the card shows the reason in plain text and offers the real options — Resume, Recover, Approve, Relaunch, Discard — and the agent posts a short briefing explaining what happened and what it recommends. It never resumes or relaunches on its own; you decide from the card.
83
+ - **No git, no PR phase.** A project without a git repository (or without commits) still gets a run card — progress, log, failure and recovery — but there is no branch and no PR step; the card says so.
84
+ - **`@rail-N`.** Type `@` and pick a rail to reference it in your message, with its specs and whether it is free, busy or awaiting a decision.
85
+
78
86
  ## A few things to know
79
87
 
80
88
  - **Operate and Autonomous cost money** because they run AI. The agent surfaces cost-incurring actions before doing them; keep the level at Observe or Edit if you just want to look and tidy.
@@ -70,6 +70,14 @@ Las respuestas llegan fluidas y ya formateadas (encabezados, tablas, listas), ca
70
70
  - **Cambia de misión.** El selector de misiones junto al selector de proyecto lista tus conversaciones de más reciente a más antigua — un punto pulsante marca aquellas donde el agente sigue trabajando, y una pequeña insignia cuenta los mensajes en cola. La búsqueda aparece cuando la lista supera las ocho, y el icono de papelera de cada fila elimina una misión tras una confirmación rápida en línea — incluso una en marcha (avisa primero y luego detiene al agente).
71
71
  - **Encuentra una misión desde cualquier sitio.** Pulsa **⌘K** (o el botón **Buscar** del sidebar en modo Agente) y escribe cualquier fragmento que recuerdes: una palabra del título *o* de lo que se dijo dentro. Las misiones coinciden al instante por título desde memoria y, un latido después, llegan las coincidencias por contenido con el pasaje resaltado, más el proyecto anclado y cuándo tuvo actividad por última vez. En modo Agente las misiones son el primer grupo; en el tablero van justo después de Proyectos. Enter abre la misión.
72
72
 
73
+ ## Cards de lanzamiento y cards de ejecución
74
+
75
+ - **El agente propone, tú pulsas Play.** Pídele que ponga specs en un rail ("prepara #12 y #14 en un rail") y, en vez de describirlo, te entrega una **card de lanzamiento**: el rail (o uno nuevo), las specs, motor, modelo, esfuerzo, perfil, loop, PR destino y rama base opcionales — todo prerrellenado con su recomendación y todo editable. Cambia lo que quieras y pulsa **Play**. Si dices "lánzalo ya", lo lanza directamente.
76
+ - **Una sola card sigue la ejecución.** Tras Play la propuesta queda congelada como "Lanzado → Rail N" y una **card de ejecución** toma el relevo: estado, tiempo transcurrido, qué está haciendo el pipeline, botón de log y Detener. Cuando el trabajo está listo para revisar, esa misma card se convierte en la card de PR de siempre (crear PR, integrar en local, descartar).
77
+ - **Los fallos hablan.** Si una ejecución falla o se atasca, la card muestra el motivo en texto claro y ofrece las opciones reales — Reanudar, Recuperar, Aprobar, Relanzar, Descartar — y el agente publica un breve informe explicando qué pasó y qué recomienda. Nunca reanuda ni relanza por su cuenta; decides tú desde la card.
78
+ - **Sin git no hay fase de PR.** Un proyecto sin repositorio git (o sin commits) también tiene card de ejecución — progreso, log, fallo y recuperación — pero no hay rama ni paso de PR; la card lo indica.
79
+ - **`@rail-N`.** Escribe `@` y elige un rail para referenciarlo en tu mensaje, con sus specs y si está libre, ocupado o pendiente de decisión.
80
+
73
81
  ## Algunas cosas que conviene saber
74
82
 
75
83
  - **Opera y Autónomo cuestan dinero** porque ejecutan IA. El agente saca a la luz las acciones que generan coste antes de hacerlas; mantén el nivel en Observa o Edita si solo quieres mirar y ordenar.
@@ -70,6 +70,14 @@ Les réponses arrivent en flux fluide et s'affichent déjà mises en forme (titr
70
70
  - **Changez de mission.** Le sélecteur de missions, à côté du sélecteur de projet, liste vos conversations de la plus récente à la plus ancienne — un point clignotant signale celles où l'agent travaille encore, et un petit badge compte les messages en file. La recherche apparaît dès que la liste dépasse huit entrées, et l'icône corbeille de chaque ligne supprime une mission après une confirmation rapide en ligne — même une mission en cours (elle prévient d'abord, puis arrête l'agent).
71
71
  - **Retrouvez une mission de n'importe où.** Appuyez sur **⌘K** (ou le bouton **Rechercher** de la barre latérale en mode Agent) et tapez n'importe quel fragment dont vous vous souvenez — un mot du titre *ou* de ce qui a été dit dedans. Les missions correspondent instantanément par titre depuis la mémoire, puis un instant plus tard arrivent les correspondances par contenu avec le passage surligné, le projet épinglé et la date de dernière activité. En mode Agent, les missions forment le premier groupe ; sur le tableau, elles suivent les Projets. Entrée ouvre la mission.
72
72
 
73
+ ## Cartes de lancement et cartes d'exécution
74
+
75
+ - **L'agent propose, vous appuyez sur Play.** Demandez-lui de placer des specs sur un rail (« prépare #12 et #14 sur un rail ») et, au lieu de le décrire, il vous remet une **carte de lancement** : le rail (ou un nouveau), les specs, moteur, modèle, effort, profil, loop, PR cible et branche de base optionnelles — tout prérempli avec sa recommandation et tout modifiable. Changez ce que vous voulez et appuyez sur **Play**. Dites « lance-le maintenant » et il lance directement.
76
+ - **Une seule carte suit l'exécution.** Après Play, la proposition se fige en « Lancé → Rail N » et une **carte d'exécution** prend le relais : état, temps écoulé, ce que fait le pipeline, bouton de log et Arrêter. Quand le travail est prêt à relire, la même carte devient la carte PR habituelle (créer la PR, intégrer en local, abandonner).
77
+ - **Les échecs parlent.** Si une exécution échoue ou se bloque, la carte affiche la raison en clair et propose les vraies options — Reprendre, Récupérer, Approuver, Relancer, Abandonner — et l'agent publie un court compte rendu expliquant ce qui s'est passé et ce qu'il recommande. Il ne reprend ni ne relance jamais seul ; vous décidez depuis la carte.
78
+ - **Sans git, pas de phase PR.** Un projet sans dépôt git (ou sans commits) a quand même une carte d'exécution — progression, log, échec et récupération — mais il n'y a ni branche ni étape PR ; la carte le dit.
79
+ - **`@rail-N`.** Tapez `@` et choisissez un rail pour le référencer dans votre message, avec ses specs et son état : libre, occupé ou en attente de décision.
80
+
73
81
  ## Quelques points à savoir
74
82
 
75
83
  - **Opérer et Autonome coûtent de l'argent** car ils exécutent de l'IA. L'agent met en avant les actions génératrices de coût avant de les réaliser ; gardez le niveau sur Observer ou Modifier si vous voulez juste regarder et ranger.
@@ -70,6 +70,14 @@ Le risposte arrivano fluide e già formattate (titoli, tabelle, elenchi), ciascu
70
70
  - **Cambia missione.** Il selettore di missioni accanto al selettore di progetto elenca le conversazioni dalla più recente alla più vecchia — un punto pulsante segnala quelle in cui l'agente sta ancora lavorando, e un piccolo badge conta i messaggi in coda. La ricerca compare quando la lista supera le otto voci, e l'icona del cestino su ogni riga elimina una missione dopo una rapida conferma in linea — anche una in corso (prima avvisa, poi ferma l'agente).
71
71
  - **Trova una missione da ovunque.** Premi **⌘K** (o il pulsante **Cerca** della barra laterale in modalità Agente) e digita un frammento qualsiasi che ricordi — una parola del titolo *o* di ciò che è stato detto dentro. Le missioni corrispondono all'istante per titolo dalla memoria e, un battito dopo, arrivano le corrispondenze per contenuto con il passaggio evidenziato, più il progetto fissato e l'ultima attività. In modalità Agente le missioni sono il primo gruppo; sulla board stanno subito dopo i Progetti. Invio apre la missione.
72
72
 
73
+ ## Card di lancio e card di esecuzione
74
+
75
+ - **L'agente propone, tu premi Play.** Chiedigli di mettere delle spec su un rail ("prepara #12 e #14 su un rail") e, invece di descriverlo, ti consegna una **card di lancio**: il rail (o uno nuovo), le spec, engine, modello, sforzo, profilo, loop, PR di destinazione e branch base opzionali — tutto precompilato con la sua raccomandazione e tutto modificabile. Cambia ciò che vuoi e premi **Play**. Se dici "lancialo adesso", lancia direttamente.
76
+ - **Una sola card segue l'esecuzione.** Dopo Play la proposta si congela come "Lanciato → Rail N" e una **card di esecuzione** prende il testimone: stato, tempo trascorso, cosa sta facendo la pipeline, pulsante log e Stop. Quando il lavoro è pronto per la revisione, la stessa card diventa la solita card PR (crea PR, integra in locale, scarta).
77
+ - **I fallimenti parlano.** Se un'esecuzione fallisce o si blocca, la card mostra il motivo in chiaro e offre le opzioni reali — Riprendi, Recupera, Approva, Rilancia, Scarta — e l'agente pubblica un breve resoconto di cosa è successo e cosa consiglia. Non riprende né rilancia mai da solo; decidi tu dalla card.
78
+ - **Senza git niente fase PR.** Un progetto senza repository git (o senza commit) ha comunque una card di esecuzione — progresso, log, fallimento e recupero — ma non c'è branch né passaggio PR; la card lo dice.
79
+ - **`@rail-N`.** Digita `@` e scegli un rail per citarlo nel messaggio, con le sue spec e se è libero, occupato o in attesa di decisione.
80
+
73
81
  ## Alcune cose da sapere
74
82
 
75
83
  - **Opera e Autonomo costano denaro** perché eseguono IA. L'agente mette in evidenza le azioni che generano costi prima di eseguirle; tieni il livello su Osserva o Modifica se vuoi solo guardare e mettere in ordine.
@@ -69,6 +69,14 @@ pure-output action は spawn 前に拒否されます。詳細は
69
69
  - **ミッションの切り替え。** プロジェクトセレクターの隣にあるミッションセレクターは、会話を新しい順に一覧表示します — 点滅するドットはエージェントがまだ作業中の会話を示し、小さなバッジはキュー内のメッセージ数を表します。リストが 8 件を超えると検索欄が現れます。各行のゴミ箱アイコンは、行内のクイック確認のあとにミッションを削除します — 実行中のものでも可能です(先に警告し、その後エージェントを停止します)。
70
70
  - **どこからでもミッションを探す。** **⌘K**(またはエージェントモードのサイドバーの **検索** ボタン)を押して、覚えている断片を入力してください——タイトルの単語でも、*中で話された内容* でも構いません。ミッションはまずメモリからタイトルで即座に一致し、一拍おいて内容による一致がハイライト付きで届き、固定されたプロジェクトと最終アクティビティも表示されます。エージェントモードではミッションが最初のグループ、ボードではプロジェクトの直後に並びます。Enter でミッションを開きます。
71
71
 
72
+ ## 起動カードと実行カード
73
+
74
+ - **エージェントが提案し、あなたが Play を押す。** spec を rail に載せるよう頼むと(「#12 と #14 を rail に準備して」)、文章で説明する代わりに**起動カード**を渡してきます。rail(または新規 rail)、spec、エンジン、モデル、推論の深さ、プロファイル、loop、任意の対象 PR とベースブランチが、推奨値で事前入力され、すべて編集できます。好きに変えて **Play** を押してください。「今すぐ起動して」と言えば直接起動します。
75
+ - **1 枚のカードが実行を追いかける。** Play の後、提案は「起動済み → Rail N」として固定され、**実行カード**が引き継ぎます。状態、経過時間、パイプラインが今していること、ログボタン、停止。作業がレビュー可能になると、同じカードがおなじみの PR カード(PR 作成、ローカル統合、破棄)になります。
76
+ - **失敗は黙らない。** 実行が失敗または停止すると、カードが理由を平文で示し、本当に取れる選択肢——再開、復旧、承認、再起動、破棄——を提示し、エージェントは何が起きたか・何を勧めるかを短く報告します。勝手に再開や再起動はしません。決めるのはカード上のあなたです。
77
+ - **git がなければ PR フェーズもない。** git リポジトリ(またはコミット)のないプロジェクトにも実行カードはあります——進捗、ログ、失敗と復旧——ただしブランチも PR ステップもありません。カードにそう書かれます。
78
+ - **`@rail-N`。** `@` を入力して rail を選ぶと、その spec と状態(空き・使用中・決定待ち)付きでメッセージから参照できます。
79
+
72
80
  ## 知っておくとよいこと
73
81
 
74
82
  - **操作と自律は料金が発生します**。AI を実行するからです。エージェントは料金の発生する操作を実行する前に前面に示します。眺めて整理したいだけなら、レベルは観察か編集にしておいてください。
@@ -69,6 +69,14 @@ As respostas chegam de forma fluida e já formatadas (títulos, tabelas, listas)
69
69
  - **Troque de missão.** O seletor de missões ao lado do seletor de projeto lista suas conversas da mais recente para a mais antiga — um ponto pulsante marca aquelas em que o agente ainda está trabalhando, e um pequeno selo conta as mensagens na fila. A busca aparece quando a lista passa de oito, e o ícone de lixeira de cada linha exclui uma missão após uma confirmação rápida em linha — mesmo uma em andamento (ele avisa primeiro e depois para o agente).
70
70
  - **Encontre uma missão de qualquer lugar.** Pressione **⌘K** (ou o botão **Pesquisar** da barra lateral no modo Agente) e digite qualquer fragmento de que se lembre — uma palavra do título *ou* do que foi dito lá dentro. As missões coincidem na hora por título a partir da memória e, um instante depois, chegam as coincidências por conteúdo com o trecho destacado, mais o projeto fixado e quando teve atividade pela última vez. No modo Agente as missões são o primeiro grupo; no quadro ficam logo depois de Projetos. Enter abre a missão.
71
71
 
72
+ ## Cards de lançamento e cards de execução
73
+
74
+ - **O agente propõe, você aperta Play.** Peça para colocar specs em um rail ("prepara #12 e #14 em um rail") e, em vez de descrever, ele entrega um **card de lançamento**: o rail (ou um novo), as specs, engine, modelo, esforço, perfil, loop, PR de destino e branch base opcionais — tudo pré-preenchido com a recomendação dele e tudo editável. Mude o que quiser e aperte **Play**. Diga "lança agora" e ele lança direto.
75
+ - **Um único card acompanha a execução.** Depois do Play a proposta congela como "Lançado → Rail N" e um **card de execução** assume: status, tempo decorrido, o que o pipeline está fazendo, botão de log e Parar. Quando o trabalho está pronto para revisar, o mesmo card vira o card de PR de sempre (criar PR, integrar localmente, descartar).
76
+ - **Falhas falam.** Se uma execução falha ou trava, o card mostra o motivo em texto claro e oferece as opções reais — Retomar, Recuperar, Aprovar, Relançar, Descartar — e o agente publica um breve relato explicando o que aconteceu e o que recomenda. Ele nunca retoma nem relança sozinho; você decide pelo card.
77
+ - **Sem git, sem fase de PR.** Um projeto sem repositório git (ou sem commits) também recebe um card de execução — progresso, log, falha e recuperação — mas não há branch nem etapa de PR; o card avisa.
78
+ - **`@rail-N`.** Digite `@` e escolha um rail para referenciá-lo na mensagem, com suas specs e se está livre, ocupado ou aguardando decisão.
79
+
72
80
  ## Algumas coisas para saber
73
81
 
74
82
  - **Opera e Autônomo custam dinheiro** porque executam IA. O agente destaca as ações que geram custo antes de fazê-las; mantenha o nível em Observa ou Edita se você só quer olhar e organizar.
@@ -68,6 +68,14 @@ spawn 前拒绝;详见 [Kimi matrix](../../../kimi.md)。
68
68
  - **切换任务。** 项目选择器旁的任务选择器按从新到旧列出你的对话——跳动的圆点标记代理仍在工作的对话,小徽章显示排队消息的数量。列表超过八条时会出现搜索框;每行的垃圾桶图标在快速的行内确认后删除任务——即使是正在运行的任务(会先警告,然后停止代理)。
69
69
  - **随时随地找到任务。** 按 **⌘K**(或代理模式下侧边栏的 **搜索** 按钮),输入你记得的任何片段——标题中的一个词,*或者* 对话里说过的内容。任务会立刻按标题从内存中匹配,紧接着按内容匹配的结果也会到达,并高亮匹配段落,同时显示固定的项目和最近活动时间。在代理模式下任务是第一组;在看板中紧跟在项目之后。按 Enter 打开任务。
70
70
 
71
+ ## 启动卡片与运行卡片
72
+
73
+ - **代理提议,你按 Play。** 让它把 spec 放到某条 rail 上("把 #12 和 #14 准备到一条 rail"),它不会用文字描述,而是给你一张**启动卡片**:rail(或新建一条)、spec、引擎、模型、推理强度、profile、loop,以及可选的目标 PR 和基础分支——全部按它的建议预填,全部可改。改好后按 **Play**。若你说"现在就启动",它会直接启动。
74
+ - **一张卡片跟随整个运行。** 按下 Play 后,提议冻结为"已启动 → Rail N",一张**运行卡片**接管:状态、已用时间、流水线正在做什么、日志按钮和停止。工作可供审阅时,这张卡片会变成熟悉的 PR 卡片(创建 PR、本地集成、丢弃)。
75
+ - **失败会说话。** 运行失败或卡住时,卡片以明文显示原因并提供真实选项——恢复、修复、批准、重新启动、丢弃——代理还会发布一段简短说明,解释发生了什么并给出建议。它绝不会自行恢复或重启;由你在卡片上决定。
76
+ - **没有 git 就没有 PR 阶段。** 没有 git 仓库(或没有提交)的项目同样有运行卡片——进度、日志、失败与恢复——但没有分支也没有 PR 步骤;卡片会明确说明。
77
+ - **`@rail-N`。** 输入 `@` 并选择一条 rail 即可在消息中引用它,附带其 spec 以及空闲、忙碌或待决定的状态。
78
+
71
79
  ## 一些需要知道的事
72
80
 
73
81
  - **操作和自主会产生费用**,因为它们运行 AI。智能体会在执行会产生费用的操作前把它们摆到明面上;如果你只想查看和整理,就把级别保持在观察或编辑。
@@ -0,0 +1,130 @@
1
+ # Mission rail cards — as-built record
2
+
3
+ > OpenSpec change `mission-rail-cards` (2026-09-18), branch `feat/mission-rail-cards`. Companion to the CLAUDE.md section of the same name. Everything below is what the code on disk does; "deferred" means it does not.
4
+
5
+ ## The problem
6
+
7
+ Mission mode (Agent Mode) replaces the routed dashboard, so `RailsBoard`, `SpecsBoard`, `RailRow` (with `RailPrDecisionStrip` and `AgentRuntimeRuns`) are never mounted there. The only object a mission had for an implementation run was the PR-decision card. On 2026-09-18 three gaps were verified:
8
+
9
+ 1. **No git ⇒ no object.** The shared-cwd launch path in `server/rails-router.ts` (`isolationUnavailable = 'no-git' | 'no-commits'`) never received `originConversationId`; no `rail_pr_deliveries` row, no card, only a `202 { isolationUnavailable }` the agent rarely mentioned.
10
+ 2. **Failure was mute.** `discarded` swallowed `statusDetail` (gated on `deliveryBlocked`); `implementation_failed` offered Discard only; no WS event wrote into the conversation or started a turn; `/agent-runtime/runs` (resume / approve / recover) was absent from MCP.
11
+ 3. **Assignment was prose.** Rails were not a palette entity; assigning specs meant dictating rail / engine / profile / loop in sentences, so users went to Board mode.
12
+
13
+ ## The card lifecycle
14
+
15
+ ```
16
+ agent turn user run settle
17
+ ┌──────────────┐ Play ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐
18
+ │ rail-launch │ ────────▶ │ launched │ ─────▶ │ running │ ─────▶ │ settled │
19
+ │ (editable) │ POST │ stub in msg │ card │ status pill │ │ ok → completed / │
20
+ │ Dismiss ─┐ │ /launch │ + run card │ │ elapsed │ │ delivery phase │
21
+ └──────────┼───┘ └──────────────┘ │ log · Stop │ │ fail → failure block │
22
+ ▼ └──────────────┘ │ Resume/Recover/ │
23
+ dismissed stub │ │ Approve/Relaunch/ │
24
+ │ failure │ Discard/Dismiss │
25
+ ▼ └──────────────────────┘
26
+ notifyMissionRunFailure
27
+ card update + run-failure row
28
+ + ONE automatic briefing turn
29
+ ```
30
+
31
+ - **Proposal** — an assistant message with one or more fenced `rail-launch` blocks. Each block is one `AgentRailLaunchCard`; undecided proposals are pinned in the dock above the composer.
32
+ - **Launched** — Play succeeded (202). The proposal freezes into a "Launched → Rail N" stub (persisted on the message row), and the run card takes over.
33
+ - **Running / settled / delivery** — the SAME envelope (`PrDecisionCardEnvelope`) carries `phase`, `runtime` and `hasDelivery`. With git, the delivery phase is the pre-existing PR-decision card. Without git, the card is run-only and ends at `completed` / `implementation_failed` / `discarded`.
34
+
35
+ ## Protocol contract
36
+
37
+ ```rail-launch
38
+ {
39
+ "version": 1,
40
+ "railIndex": 1,
41
+ "newRail": null,
42
+ "ticketIds": [12, 14],
43
+ "mode": "implement",
44
+ "loopId": "factory:implement",
45
+ "aiEngine": "claude",
46
+ "model": "opus",
47
+ "reasoningEffort": "high",
48
+ "profileName": "fast",
49
+ "targetPrNumber": null,
50
+ "baseBranch": null,
51
+ "railName": "Auth",
52
+ "rationale": "Both specs touch the auth flow; one rail keeps the diff coherent."
53
+ }
54
+ ```
55
+
56
+ Parser pair `server/rail-launch-parser.ts` ⇄ `client/src/lib/rail-launch-draft.ts` (byte-identical except the mirror note; `server/rail-launch-parser.test.ts` enforces parity). `extractRailLaunchProposals(content, streaming)` returns `{ body, proposals, rejected, pending, truncated, repaired }`:
57
+
58
+ - tolerant JSON repair (`json-tolerant`) before rejecting; `repaired` flags it;
59
+ - unknown keys dropped; `ticketIds` accepts `12`, `"12"`, `"#12"` (also under `specs` / `tickets`); `mode` accepts `batch` for `batch-implement`, `newRail: true` for an unnamed rail; `railIndex` is ignored when `newRail` is set;
60
+ - rejection reasons: `invalid_json | not_object | unsupported_version | no_tickets | invalid_mode`, each with a ≤160-char excerpt — never silent;
61
+ - while streaming an open fence is cut from the body (`pending`); once settled an unreadable open fence is cut and reported `truncated`;
62
+ - every valid block is kept in order: a batch proposal is N blocks, N cards.
63
+
64
+ `agent-fence-promotion.ts` (server and client copies) re-tags a generic ```` ```json ```` fence whose body is a launch shape (ticket list plus a rail/launch key), checked after the spec-draft shape so a draft never becomes a launch.
65
+
66
+ ## Server chokepoints and surfaces
67
+
68
+ | Piece | Where | Notes |
69
+ |---|---|---|
70
+ | Operator prompt | `server/agent-operator-prompt.ts` `RAIL_LAUNCH_CARD_SECTION`, `RAIL_LAUNCH_CARD_SYSTEM_CLAUSE`, `buildOperatorSystemPrompt()` | Propose = block; launch directly only on an explicit "now"; failure briefing reply ≤ 6 lines + one action, no mutation. Gated on `isMissionRailCardsEnabled()`. |
71
+ | Message intent | desktop-db migration 29 (`agent_messages.intent`, column-guarded); `agent-store.ts` `setAgentMessageIntent`; `PATCH /api/agent/conversations/:id/messages/:mid/intent` | JSON ARRAY of decisions keyed by `proposalIndex`; append-only; 409 `already_decided`; 403 when the feature is off. Rows expose `intents[]`. |
72
+ | Envelope | `server/types.ts` `PrDecisionCardEnvelope` (+ `MissionRunPhase`, `MissionRunRuntime`, `MissionRunFailure`) | `hasDelivery`, `phase`, `railName`, `runtime` — all optional; `decision` vocabulary unchanged. |
73
+ | Run-only cards | `server/mission-run-notify.ts` `runCardId` / `isRunCardId` / `buildRunCardEnvelope` / `postRunCard` / `settleRunCard` / `failureForLoopOutcome` / `runtimeFromSummary` | Synthetic `prDeliveryId = 'run:<runId>'`, `hasDelivery:false`. Wired in the shared-cwd branch of `rails-router.ts` (origin stored on `railLoopRuns` meta; 202 returns `runIds`). |
74
+ | Run-only dismiss | `POST /rails/pr-decision { prDeliveryId:'run:…', action:'dismiss', expectedDecision, conversationId }` → `AgentChatManager.dismissRunCard` | Other actions ⇒ 400 `run_card_dismiss_only`; unknown card ⇒ 404 `run_card_not_found`. |
75
+ | Failure trigger | `notifyMissionRunFailure` (`mission-run-notify.ts`) | Envelope update with `runtime.failure` → `AgentChatManager.postRunFailureRow` (one `system` row per run, WS `agent_run_failure`) → `AgentChatManager.startSystemTurn` (queueId `mission-failure:<runId>`, gated `isMissionFailureTurnEnabled()`). Callers: shared-cwd run promise, isolated settle (`implementation_failed`), `closeFailedGeneration` (`launch_failed`). Cancel updates the card, no turn. No origin ⇒ no-op. |
76
+ | Briefing | `server/agent-failure-briefing.ts` `buildFailureBriefing`, `failureBriefingRef`, `FAILURE_CODE_LABELS`, `FAILURE_BRIEFING_MAX_TAIL` | Fixed text: rail, specs, failure code + detail, bounded verify tail, recovery options, "do not relaunch by yourself". Persisted as a `user` row whose `context_refs[0].kind === 'system-briefing'`. |
77
+ | Settle snapshots | `rail-isolated-launch.ts` settle, `rail-pr-store.ts` `toPrDecisionCardEnvelope`, `agent-runtime-controls-router.ts` `runtimeRunSummary()` | `phase` + `runtime` on `implementation_failed` and launch-failed `discarded`; `statusDetail` as text. |
78
+ | Rails availability | `GET /:projectId/rails` | `availability: 'free' \| 'busy' \| 'pending_decision' \| 'on_review'` per rail. |
79
+ | MCP | `server/mcp/tools/jobs.ts` `runtime_runs`, `runtime_evidence` (read) · `runtime_resume`, `runtime_recover` (ai-spawn) · `runtime_approve`, `runtime_settle`, `runtime_dismiss` (write) · `runtime_cancel` (destructive); `mcp/guide.ts` recovery section | Over the existing `/agent-runtime/runs` routes; approve/recover are `resume` bodies. |
80
+ | Palette | `server/agent-chat-router.ts` `CONTEXT_KINDS` += `rail`; `server/agent-context-resolver.ts` `formatRail` | `@rail-N` reference serialization. |
81
+
82
+ WS messages: `agent_pr_decision` (existing, now carries the new fields), `agent_run_failure` (new, app-global, `AgentRunFailureMessage`).
83
+
84
+ ## Client components
85
+
86
+ - `client/src/components/agent-chat/AgentMessage.tsx` — extraction step after spec-draft (flag `FEATURE_MISSION_RAIL_CARDS`); renders `AgentRailLaunchCard` per proposal, `AgentRailLaunchPending`, `AgentRailLaunchUnreadable`.
87
+ - `AgentRailLaunchCard.tsx` — live reconciliation (`/rails` availability, `/tickets`, `/profiles`, `useProviderDetection`, model/effort catalogs, loops), New rail, Play flow (`POST /rails` → `PUT /tickets` → `POST /launch` with `originConversationId` + `originSurface:'agent-chat'` → `notifyGitChanged` → intent PATCH), inline 400/409, launched/dismissed stubs, `FOCUS_PR_CARD_EVENT`.
88
+ - `client/src/lib/rail-launch-intents.ts` — session overlay (`recordLocalIntent`, `intentFor`); `useRailLaunchProposals.ts` — undecided proposals for the dock; `AgentPrPinnedDock.tsx` — proposals pinned above PR cards.
89
+ - `AgentPrDecisionCard.tsx` — run phase header (`deriveMissionRunStatus`, live `useRuntimeRuns` first), run-only rendering for `hasDelivery === false`, `RunFailureBlock`, Resume / Approve / Recover / Relaunch, focus-bus scroll + flash.
90
+ - `agent-pr-pinning.ts` `isPrEnvelopePinned`; `agent-run-failure.ts` (`parseRunFailureRow`, `systemBriefingRunId`, `FOCUS_PR_CARD_EVENT`, `MISSION_OPEN_RUN_EVENT`, `requestMissionOpenRun`); `AgentRunFailureMarker.tsx`.
91
+ - `client/src/context/AgentChatContext.tsx` — appends the `run-failure` row live on `agent_run_failure`; `AgentWorkspaceContext.tsx` / `AgentModeJobsPane.tsx` — `specrails:mission-open-run` opens the Jobs pane + `JobDetailModal`; `hooks/useOsNotifications.ts` — stays in Mission mode.
92
+ - Palette: `client/src/lib/agent-context-palette.ts` (`rail` kind, `railsFromResponse`, `railChip`), `AgentComposer.tsx`, `AgentComposerEditor.tsx`, `AgentContextPalette.tsx`.
93
+
94
+ ## Honesty rules
95
+
96
+ - Play is a user action: it never rides the agent tier ladder, and the server re-validates every launch (409s render inline; the card never pretends success).
97
+ - The card shows only real state: live runtime data wins over the settle snapshot; a run-only card says plainly that no PR phase exists; failure detail is text, never hover-only; nothing is estimated.
98
+ - The automatic failure turn is bounded: one per run (durable queueId), queued behind a live turn, accounted like any turn, forbidden from mutating in that turn, switchable off independently.
99
+ - A decision is final per proposal index; reload renders the frozen stub, never a second Play.
100
+
101
+ ## Flags and rollback
102
+
103
+ | Flag | Default | Off means |
104
+ |---|---|---|
105
+ | `SPECRAILS_MISSION_RAIL_CARDS` | on | legacy prompt verbs, intent route 403, no run-only cards, no failure trigger |
106
+ | `VITE_FEATURE_MISSION_RAIL_CARDS` | on | `rail-launch` fences ignored, PR card renders exactly as before |
107
+ | `SPECRAILS_MISSION_FAILURE_TURN` | on | card + `run-failure` row still update, no automatic turn |
108
+
109
+ Migration 29 and the envelope fields are additive; persisted intents are inert when the feature is off.
110
+
111
+ ## Deferred
112
+
113
+ - `job.stuck` → failure trigger: the stuck detector has no origin link (needs the rails meta map).
114
+ - QueueManager `/spawn` jobs: no origin, no card (unchanged).
115
+ - Per-step envelope updates while running: only launch → running and settle are emitted; the client reads live runtime meanwhile.
116
+ - One grouped card for `launch_all`: v1 emits N blocks.
117
+ - Translated `rationale` — the agent writes it in the conversation language.
118
+
119
+ ## Test map
120
+
121
+ | Area | Tests |
122
+ |---|---|
123
+ | Parser + promotion + mirror parity | `server/rail-launch-parser.test.ts` |
124
+ | Intent store/route, `dismissRunCard` | `server/agent-message-intent.test.ts` |
125
+ | Run-only cards, availability, run-card dismiss route | `server/rails-router.test.ts`, `server/mcp/tools/rails-origin.test.ts` |
126
+ | Failure trigger / briefing / system turn | `server/mission-run-notify.test.ts`, `server/agent-failure-briefing.test.ts`, `server/agent-chat-mission-turn.test.ts`, `server/rail-isolated-launch.test.ts` |
127
+ | MCP runtime actions | `server/mcp/tools/jobs-runtime.test.ts` |
128
+ | Launch card, proposals hook, stubs | `client/src/components/agent-chat/__tests__/agent-rail-launch-card.test.tsx` |
129
+ | Run card phases / failure block / pinning / marker | `mission-run-card.test.tsx`, `mission-run-pinning.test.ts`, `agent-run-failure.test.ts`, `agent-run-failure-marker.test.tsx` |
130
+ | Palette rails, Mission-mode notifications | `client/src/lib/__tests__/agent-context-palette-rails.test.ts`, `client/src/hooks/__tests__/useOsNotifications.mission-mode.test.ts` |
@@ -109,9 +109,11 @@ holds no optimistic state; a raced answer surfaces the neutral
109
109
 
110
110
  The "What you asked" / "What was done" slots are spec markdown and render
111
111
  through `PacketMarkdown` (react-markdown + GFM, raw HTML skipped, images
112
- dropped, inline code as quiet chips). `solution` is the tight digest; when
113
- the composer clamps it, `solutionOverflow` carries the full text and the
114
- page shows it behind a "Read the full solution" disclosure. The server
112
+ dropped, inline code as quiet chips, `unfoldInlineNumberedList` for specs
113
+ written as one "1. … 2. …" paragraph). "What was done" leads with the
114
+ durable outcome pill and the measured churn; the spec's proposed solution
115
+ (`solutionOverflow ?? solution`) sits below in a collapsed "Planned approach
116
+ (from the spec)" disclosure, labelled as the request, never as a report. The server
115
117
  digests each headed section as `**Label**` + a blank line + the content, so
116
118
  a numbered journey survives as a list in the packet AND in the PR body.
117
119
  - "Discuss this delivery" deliberately routes into the existing agent chat
@@ -218,3 +220,14 @@ their pre-change behaviour, with no toggle at all.
218
220
  toggle and render exactly as before.
219
221
 
220
222
  Any flag off leaves the existing decision strip byte-identical.
223
+
224
+
225
+ ## 2026-09-19 — "What was done" tells facts, not the plan
226
+
227
+ Observed: the section rendered the spec's *Proposed Solution* digest as raw markdown (`**Proposed Solution** — …`, backticks) under the heading "What was done" — i.e. the PLAN presented as a REPORT. Fix (client only, `ReviewPacketPage.tsx`): each ticket row now leads with the durable per-unit outcome pill (`outcome.*`, derived from `implementationOutcome`/`deliveryOutcome`/`changed`, never from prose), the measured churn line now also states test files touched (`churn.testFiles`/`noTestFiles`), and the spec digest moved into a collapsed `<details>` labelled **Planned approach (from the spec)** with an explicit caveat that it is not a report of what the AI did. Both narratives (`problem`, `solution`) render through `ReactMarkdown` (`PacketMarkdown`) so headings, bold and inline code never leak raw. i18n `packet:planned.*`, `outcome.*`, `churn.testFiles*` ×8. Server composition unchanged.
228
+
229
+ ## 2026-09-19 — Programmatic-runtime evidence (host-run verification + reviewer verdict)
230
+
231
+ Observed: a delivery built by the programmatic agent runtime (core-host) rendered **"The AI did not report any verification"** and **"No reviewer score"** although the host had run `npm test` (exit 0, output persisted) and the reviewer returned a structured verdict (score 92). The harvest only knew the legacy loop channels — the `VERIFICATION:` prose sentinel and `openspec/changes/**/confidence-score.json` — neither of which that runtime produces. Fix (`server/delivery-evidence.ts` `readRuntimeEvidence`, wired from `rail-isolated-launch.ts` via the new `EvidenceHarvestUnit.runtimeDir` = `<workspace>/.specrails/pipeline/<runId>`): read `state.json` `verification.commands[]` (+ each `verification/evidence/<id>.json` for exit code, duration, bounded output and `origin`), `acceptance.checks[]` / `acceptance.findings[]`, and the reviewer step output in `agent-workflow/<runId>/checkpoint.json` (`approved`, `score`, `aspects`, `issues`, `summary`). Every field is bounded; a malformed file degrades to nothing.
232
+
233
+ Tier placement (`review-packet.ts` `buildProof`): commands whose `origin` includes `host` are **app-verified** (`proof.hostCommandPassed|Failed` with exit code + seconds + output tail) — the runtime executed them and recorded the exit code, which is a measurement, not a claim; non-host acceptance checks, the reviewer summary and findings are **ai-reported** (`proof.acceptanceCheckPassed|Failed`, `proof.reviewerSummary`, `proof.reviewerFinding`); the structured verdict fills `confidence` (score/aspects/issues→flags) ONLY when no file-based score exists, so the **reviewer-score** tier and the human-review band work unchanged. `proof.noVerificationReported` is emitted only when no channel reported anything. Older deliveries heal lazily: `GET …/packet` runs `healRuntimeEvidence` over units lacking `runtime` and persists the result (`updatePrDeliverySettleEvidence`), so a packet stops claiming silence once the pipeline dir exists. Still never a numeric test-count claim: the "70 passed" line rides inside the labelled raw output, not as a number the app asserts.
package/docs/mcp.md CHANGED
@@ -40,7 +40,7 @@ reading one description. The tools are:
40
40
  | `specrails_context` | Compact live briefing: project/providers, backlog, rails/runs/deliveries, Git/worktrees, blueprint; source and availability per section |
41
41
  | `specrails_specs` | The spec/ticket backlog: list, get, create, update, delete, drafts, AI generate, AI-edit, Contract Refine, SMASH, per-ticket spend |
42
42
  | `specrails_rails` | Configure/launch dynamic rails (`create_rail`, up to 12), inspect PR candidates/review packets; `launch_all` reports outcomes and isolation availability per rail |
43
- | `specrails_jobs` | Inspect paginated job events, phase breakdowns, queues and background processes; stop jobs |
43
+ | `specrails_jobs` | Inspect paginated job events, phase breakdowns, queues and background processes; stop jobs; read and drive programmatic runtime runs (`runtime_*`) |
44
44
  | `specrails_chat` | Explore / sidebar chat conversations and turns |
45
45
  | `specrails_agents` | Provider-scoped agent profiles and catalog; explicit profiles are validated for the selected provider |
46
46
  | `specrails_plugins` | The per-project plugin marketplace (install / verify / uninstall) |
@@ -107,6 +107,29 @@ Project Builder generation, Loop Decider, Code Explorer AI transforms, and
107
107
  Agent Studio automation are rejected before spawn. Destructive gates run
108
108
  before any mutation.
109
109
 
110
+ ## Runtime runs and rail availability (mission-rail-cards)
111
+
112
+ `specrails_jobs` exposes the programmatic runtime of a job (the same state the
113
+ app's Resume / Approve / Recover controls use), so an agent can explain a
114
+ failure and act only on the user's confirmation:
115
+
116
+ | Action | Tier | What it does |
117
+ |---|---|---|
118
+ | `runtime_runs` | read | Runtime state of `jobId` (or every run when omitted): status, current step, `canResume`, `recoverableSteps`, `pendingApproval` |
119
+ | `runtime_evidence` | read | Durable runtime evidence of `jobId` |
120
+ | `runtime_resume` | ai-spawn | Continue a resumable run (optional `approve` / `recover` / instruction body) |
121
+ | `runtime_recover` | ai-spawn | Shorthand for `runtime_resume` with `recover: [stepIds]` |
122
+ | `runtime_approve` | write | Shorthand for `runtime_resume` with `approve: [stepId]` |
123
+ | `runtime_settle` | write | Prepare delivery for a succeeded-but-unsettled run |
124
+ | `runtime_dismiss` | write | Dismiss a runtime continuation card |
125
+ | `runtime_cancel` | destructive | Cancel an active runtime continuation |
126
+
127
+ `specrails_rails(list)` rails carry `availability` — `free`, `busy` (active job
128
+ or loop run), `pending_decision` (an undecided PR delivery) or `on_review` (a
129
+ spec parked on review). Inside the app's own agent chat, a launch proposal is a
130
+ fenced `rail-launch` block the user plays from an editable card; the tool's
131
+ `launch` action is reserved for an explicit "launch now".
132
+
110
133
  ## The four permission tiers
111
134
 
112
135
  Every tool declares one tier. The server refuses any tool whose tier is not
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specrails-desktop",
3
- "version": "2.48.0",
3
+ "version": "2.49.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,6 +20,7 @@ const live_session_types_1 = require("./providers/live-session-types");
20
20
  const cli_prompt_1 = require("./util/cli-prompt");
21
21
  const result_event_1 = require("./result-event");
22
22
  const desktop_db_1 = require("./desktop-db");
23
+ const feature_flags_1 = require("./feature-flags");
23
24
  const agent_cwd_manager_1 = require("./agent-cwd-manager");
24
25
  const agent_operator_prompt_1 = require("./agent-operator-prompt");
25
26
  const agent_mcp_config_1 = require("./agent-mcp-config");
@@ -436,8 +437,8 @@ class AgentChatManager {
436
437
  : '';
437
438
  const prompt = `${contextPrefix}\n\n${projectSnapshot}\n\n${inputReceiptNote}${userWithAttachments}`;
438
439
  const systemPrompt = hasAttachments
439
- ? `${agent_operator_prompt_1.OPERATOR_SYSTEM_PROMPT}\n\n${attachment_manager_1.USER_ATTACHMENT_SYSTEM_NOTE}`
440
- : agent_operator_prompt_1.OPERATOR_SYSTEM_PROMPT;
440
+ ? `${(0, agent_operator_prompt_1.buildOperatorSystemPrompt)()}\n\n${attachment_manager_1.USER_ATTACHMENT_SYSTEM_NOTE}`
441
+ : (0, agent_operator_prompt_1.buildOperatorSystemPrompt)();
441
442
  const effectivePrompt = (useResume) => {
442
443
  const freshPrompt = !useResume && historyBlock ? `${contextPrefix}\n\n${historyBlock}\n\n${prompt}` : prompt;
443
444
  return adapter.capabilities.systemPromptArg ? freshPrompt : `${systemPrompt}\n\n---\n\n${freshPrompt}`;
@@ -1226,6 +1227,100 @@ class AgentChatManager {
1226
1227
  console.error(`[agent-chat] updatePrDecisionCard failed (${conversationId}):`, err);
1227
1228
  }
1228
1229
  }
1230
+ /**
1231
+ * mission-rail-cards: persist the compact failure marker row for a run into
1232
+ * its origin conversation (once per run — a repeat signal only updates the
1233
+ * card) and broadcast `agent_run_failure`. Returns the row id, or null when
1234
+ * skipped (duplicate / conversation gone / disposed). NEVER throws.
1235
+ */
1236
+ postRunFailureRow(conversationId, row) {
1237
+ if (this._disposed)
1238
+ return null;
1239
+ try {
1240
+ if (!(0, agent_store_1.getAgentConversation)(this._db, conversationId))
1241
+ return null;
1242
+ const existing = (0, agent_store_1.findAgentSystemMessages)(this._db, conversationId, (content) => {
1243
+ try {
1244
+ const parsed = JSON.parse(content);
1245
+ return parsed.kind === 'run-failure' && parsed.runId === row.runId;
1246
+ }
1247
+ catch {
1248
+ return false;
1249
+ }
1250
+ });
1251
+ if (existing.length > 0)
1252
+ return null;
1253
+ const message = (0, agent_store_1.addAgentMessage)(this._db, { conversationId, role: 'system', content: JSON.stringify(row) });
1254
+ this._broadcast({ type: 'agent_run_failure', conversationId, messageId: message.id, ...row, timestamp: new Date().toISOString() });
1255
+ return message.id;
1256
+ }
1257
+ catch (err) {
1258
+ console.error(`[agent-chat] postRunFailureRow failed (${conversationId}):`, err);
1259
+ return null;
1260
+ }
1261
+ }
1262
+ /**
1263
+ * mission-rail-cards: start ONE automatic, app-authored turn (the run-failure
1264
+ * briefing). Reuses `sendMessage` end to end — queued behind a live turn,
1265
+ * accounted like any turn, subject to the conversation's provider/model/tier.
1266
+ * Durable dedup: the queue id is derived from the run id, so a second signal
1267
+ * for the same run is an idempotent no-op (`created:false`) even across a
1268
+ * restart. Flag-gated; NEVER throws.
1269
+ */
1270
+ async startSystemTurn(conversationId, text, opts) {
1271
+ if (this._disposed || !(0, feature_flags_1.isMissionFailureTurnEnabled)())
1272
+ return 'skipped';
1273
+ const key = `${conversationId}:${opts.runId}`;
1274
+ if (this._systemTurnRuns.has(key))
1275
+ return 'skipped';
1276
+ if (!(0, agent_store_1.getAgentConversation)(this._db, conversationId))
1277
+ return 'skipped';
1278
+ this._systemTurnRuns.add(key);
1279
+ try {
1280
+ await this.sendMessage(conversationId, text, {
1281
+ queueId: `mission-failure:${opts.runId}`,
1282
+ ...(opts.ref ? { contextRefs: [opts.ref] } : {}),
1283
+ });
1284
+ return 'started';
1285
+ }
1286
+ catch (err) {
1287
+ console.error(`[agent-chat] startSystemTurn failed (${conversationId}):`, err);
1288
+ return 'skipped';
1289
+ }
1290
+ }
1291
+ _systemTurnRuns = new Set();
1292
+ /**
1293
+ * Dismiss a RUN-ONLY card (mission-rail-cards: a shared-cwd launch keyed on a
1294
+ * synthetic `run:<runId>` id — there is no delivery row for the decision
1295
+ * endpoint to transition). Marks the persisted envelope `discarded` (phase
1296
+ * settled, operation dismiss) and re-broadcasts so every open panel unpins
1297
+ * it. Returns false when no such card exists in that conversation.
1298
+ */
1299
+ dismissRunCard(conversationId, prDeliveryId) {
1300
+ if (this._disposed)
1301
+ return false;
1302
+ const existing = this._findPrDecisionCards(conversationId, prDeliveryId);
1303
+ if (existing.length === 0)
1304
+ return false;
1305
+ let envelope;
1306
+ try {
1307
+ envelope = JSON.parse(existing[existing.length - 1].content);
1308
+ }
1309
+ catch {
1310
+ return false;
1311
+ }
1312
+ if (envelope.hasDelivery !== false)
1313
+ return false;
1314
+ this.updatePrDecisionCard(conversationId, {
1315
+ ...envelope,
1316
+ decision: 'discarded',
1317
+ phase: 'settled',
1318
+ operation: 'dismiss',
1319
+ statusCode: envelope.statusCode ?? 'dismissed',
1320
+ updatedAt: new Date().toISOString(),
1321
+ });
1322
+ return true;
1323
+ }
1229
1324
  _findPrDecisionCards(conversationId, prDeliveryId) {
1230
1325
  return (0, agent_store_1.findAgentSystemMessages)(this._db, conversationId, (content) => {
1231
1326
  try {