specrails-desktop 2.59.0 → 2.59.1
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/client/dist/assets/{ActivityFeedPage-DBFt8ZHP.js → ActivityFeedPage-vAY9C71W.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-DiQx0W67.js → AgentBrowserCapture-CpwjGXQm.js} +1 -1
- package/client/dist/assets/{AgentModeAnalyticsPane-BmDSXp_J.js → AgentModeAnalyticsPane-BHeoJVxQ.js} +2 -2
- package/client/dist/assets/{AgentModeCodePane-BLQLPSwr.js → AgentModeCodePane-B0Nf3YkN.js} +2 -2
- package/client/dist/assets/{AgentModeJobsPane-BbTXdKUa.js → AgentModeJobsPane-CpbIItY_.js} +1 -1
- package/client/dist/assets/{AgentsPage-Dei1jGR8.js → AgentsPage-BVJAFXn3.js} +1 -1
- package/client/dist/assets/{AnalyticsPage-BhkeGIGn.js → AnalyticsPage-DDWees-K.js} +1 -1
- package/client/dist/assets/{CodePage-B_9yYWUx.js → CodePage-CzV5tkvS.js} +1 -1
- package/client/dist/assets/{DesktopAnalyticsPage-DAcfEJac.js → DesktopAnalyticsPage-DfRHNK7i.js} +1 -1
- package/client/dist/assets/{DocsDialog-WCuiYgsc.js → DocsDialog-BxduBYbh.js} +1 -1
- package/client/dist/assets/{DocsPage-bGVYTMTU.js → DocsPage-DOVeRJn_.js} +1 -1
- package/client/dist/assets/{ExportDropdown-CsodZ-yb.js → ExportDropdown-BnKVV8UK.js} +1 -1
- package/client/dist/assets/{InteractiveJobComposer-D5fh_eVL.js → InteractiveJobComposer-Da9dvM-0.js} +1 -1
- package/client/dist/assets/{JobDetailModal-DlpXF61O.js → JobDetailModal-DhwKbZGe.js} +1 -1
- package/client/dist/assets/{JobDetailPage-2yR19_Lb.js → JobDetailPage-MtPE-E_1.js} +1 -1
- package/client/dist/assets/{JobsPage-CS9IR6Dq.js → JobsPage-0vdlbNik.js} +1 -1
- package/client/dist/assets/{LoopBuilderPage-BwiAtmOr.js → LoopBuilderPage-DCUHozXh.js} +1 -1
- package/client/dist/assets/{LoopPreviewModal--upwvgTv.js → LoopPreviewModal-Df-S8NGf.js} +1 -1
- package/client/dist/assets/{LoopWindowSurface-DRnUixtl.js → LoopWindowSurface-sdb7QbSo.js} +1 -1
- package/client/dist/assets/{LoopsPage-Xm3nRToQ.js → LoopsPage-CDfCC9Px.js} +1 -1
- package/client/dist/assets/{MinimizedChatsContext-D_8L4EWZ.js → MinimizedChatsContext-DKlPf54Y.js} +1 -1
- package/client/dist/assets/{PluginsPage-CAFQ1B4Q.js → PluginsPage-iL0G3rQQ.js} +1 -1
- package/client/dist/assets/{ProjectSettingsDialog-DYSO4v90.js → ProjectSettingsDialog-DOkjT7xv.js} +1 -1
- package/client/dist/assets/{RepositoryDeliveries-CbQmW7Qx.js → RepositoryDeliveries-CJVpOUpZ.js} +1 -1
- package/client/dist/assets/RepositoryScopeSelector-BeM-2gIZ.js +2 -0
- package/client/dist/assets/{ReviewPacketPage-SDBhYdQB.js → ReviewPacketPage-EBCim2w_.js} +1 -1
- package/client/dist/assets/{TemplatePreviewModal-DHBUds51.js → TemplatePreviewModal-1-92UGwN.js} +1 -1
- package/client/dist/assets/{TicketDetailModalContext-UAeNm7BB.js → TicketDetailModalContext-CkVXuHxU.js} +1 -1
- package/client/dist/assets/{Trans-DExd3Z00.js → Trans-CzhPjnjB.js} +1 -1
- package/client/dist/assets/{agent-DrM4qned.js → agent--8QAfoXL.js} +1 -1
- package/client/dist/assets/{agent-CW74ODDP.js → agent-3HxXkIWX.js} +1 -1
- package/client/dist/assets/{agent-BElLtdKP.js → agent-BjttZLIl.js} +1 -1
- package/client/dist/assets/{agent-C-m9VugX.js → agent-CpFCz32T.js} +1 -1
- package/client/dist/assets/{agent-KdIXq3w5.js → agent-DVnw6ybO.js} +1 -1
- package/client/dist/assets/{agent-CGSKxfZk.js → agent-Z5s_GTP0.js} +1 -1
- package/client/dist/assets/{agent-DeCAZDhL.js → agent-hv57-zyG.js} +1 -1
- package/client/dist/assets/{agent-jK8mCisM.js → agent-xGrZ_aN1.js} +1 -1
- package/client/dist/assets/{formatDistanceToNow-aw7H5ORC.js → formatDistanceToNow-DlQaXc7c.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-NyC7gZJX.js → getTimezoneOffsetInMilliseconds-DxUFGcVq.js} +1 -1
- package/client/dist/assets/index-Bfunvi6w.css +2 -0
- package/client/dist/assets/{index-JSTZVJfe.js → index-Czov72g7.js} +43 -43
- package/client/dist/assets/{jira-api-DlXaRwA3.js → jira-api-CbJvbRYx.js} +1 -1
- package/client/dist/assets/{project-repositories-DXhb9FP4.js → project-repositories-BkBD3rxx.js} +1 -1
- package/client/dist/assets/{spending-BdcyVkcd.js → spending-CYotH-9N.js} +1 -1
- package/client/dist/assets/{subscriptionUsage-DoWQv7l1.js → subscriptionUsage-BH73RO86.js} +1 -1
- package/client/dist/assets/{subscriptionUsage-DQ02J25O.js → subscriptionUsage-BWTeWlHx.js} +1 -1
- package/client/dist/assets/{subscriptionUsage-N9hB-ARf.js → subscriptionUsage-CBh82zVa.js} +1 -1
- package/client/dist/assets/{subscriptionUsage-DWF3zKp4.js → subscriptionUsage-CHcQ7lLe.js} +1 -1
- package/client/dist/assets/subscriptionUsage-CdlE5Mg1.js +1 -0
- package/client/dist/assets/{subscriptionUsage-Cf87DeS_.js → subscriptionUsage-DUPBmSQi.js} +1 -1
- package/client/dist/assets/{subscriptionUsage-DkIf6Zuh.js → subscriptionUsage-DrlwrJ17.js} +1 -1
- package/client/dist/assets/{subscriptionUsage-D6u7VBdz.js → subscriptionUsage-EFu0mIK3.js} +1 -1
- package/client/dist/assets/{useDesktop-D07TvesS.js → useDesktop-DpxVGfUC.js} +1 -1
- package/client/dist/assets/{useRuntimeRuns-pGLmtAC6.js → useRuntimeRuns-CfGRbhr9.js} +1 -1
- package/client/dist/assets/{useSharedWebSocket-TzKtPRp9.js → useSharedWebSocket-C17TGqT8.js} +2 -2
- package/client/dist/index.html +17 -17
- package/docs/internals/api-reference.md +1 -0
- package/docs/internals/core-runtime-updates.md +9 -2
- package/docs/internals/desktop-owned-workflows.md +23 -0
- package/docs/internals/mission-rail-cards.md +15 -0
- package/docs/internals/operations-runbook.md +6 -0
- package/docs/internals/safe-pr-review-flow.md +25 -7
- package/docs/internals/source-map.md +3 -1
- package/docs/internals/visual-design-audit.md +5 -0
- package/package.json +1 -1
- package/server/dist/core-update-manager.js +1 -1
- package/server/dist/db/migrations.js +9 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-bridge.js +114 -5
- package/server/dist/modules/delivery/runtime/multi-repo-execution.js +7 -5
- package/server/dist/modules/delivery/runtime/rail-isolated-launch.js +65 -30
- package/server/dist/modules/delivery/runtime/rail-pr-store.js +3 -3
- package/server/dist/modules/delivery/runtime/rail-relaunch.js +154 -0
- package/server/dist/modules/delivery/runtime/rails-router.js +109 -10
- package/server/dist/modules/execution/runtime/revision-seed.js +6 -2
- package/server/dist/modules/loops/runtime/loop-agent-defaults.json +1 -1
- package/server/dist/modules/loops/runtime/loop-definition-events.js +1 -1
- package/server/dist/modules/loops/runtime/loop-implement-recipe.js +8 -4
- package/server/dist/modules/loops/runtime/loop-run-manager.js +27 -17
- package/server/dist/modules/subscription-usage/adapters/claude.js +30 -20
- package/server/dist/modules/subscription-usage/application.js +2 -1
- package/server/dist/modules/subscription-usage/domain.js +35 -1
- package/server/dist/modules/subscription-usage/runtime/usage-service.js +1 -1
- package/server/dist/worktree-manager.js +114 -6
- package/server/dist/worktree-overlay.js +4 -4
- package/client/dist/assets/RepositoryScopeSelector-DKzvIGSZ.js +0 -2
- package/client/dist/assets/index-CahchzS5.css +0 -2
- package/client/dist/assets/subscriptionUsage-zRcRPHkP.js +0 -1
package/client/dist/index.html
CHANGED
|
@@ -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-
|
|
207
|
+
<script type="module" crossorigin src="/assets/index-Czov72g7.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">
|
|
@@ -213,7 +213,7 @@
|
|
|
213
213
|
<link rel="modulepreload" crossorigin href="/assets/tauri-shell-DE6LfGPw.js">
|
|
214
214
|
<link rel="modulepreload" crossorigin href="/assets/activity-DiyjsgQV.js">
|
|
215
215
|
<link rel="modulepreload" crossorigin href="/assets/addspec-BOvgmcgk.js">
|
|
216
|
-
<link rel="modulepreload" crossorigin href="/assets/agent-
|
|
216
|
+
<link rel="modulepreload" crossorigin href="/assets/agent-Z5s_GTP0.js">
|
|
217
217
|
<link rel="modulepreload" crossorigin href="/assets/agentRuntime-Bpx3yWHK.js">
|
|
218
218
|
<link rel="modulepreload" crossorigin href="/assets/agents-CTwbx-fb.js">
|
|
219
219
|
<link rel="modulepreload" crossorigin href="/assets/agentstudio-BWAKeGgB.js">
|
|
@@ -239,19 +239,19 @@
|
|
|
239
239
|
<link rel="modulepreload" crossorigin href="/assets/settings-Dbyd6gmj.js">
|
|
240
240
|
<link rel="modulepreload" crossorigin href="/assets/setup-CKGUxKr-.js">
|
|
241
241
|
<link rel="modulepreload" crossorigin href="/assets/specs-DfIdoU5L.js">
|
|
242
|
-
<link rel="modulepreload" crossorigin href="/assets/subscriptionUsage-
|
|
242
|
+
<link rel="modulepreload" crossorigin href="/assets/subscriptionUsage-CdlE5Mg1.js">
|
|
243
243
|
<link rel="modulepreload" crossorigin href="/assets/terminal-BAzpHtpY.js">
|
|
244
244
|
<link rel="modulepreload" crossorigin href="/assets/tickets-D41ZMQpf.js">
|
|
245
245
|
<link rel="modulepreload" crossorigin href="/assets/createLucideIcon-Bjbbp_KS.js">
|
|
246
|
-
<link rel="modulepreload" crossorigin href="/assets/useSharedWebSocket-
|
|
247
|
-
<link rel="modulepreload" crossorigin href="/assets/Trans-
|
|
248
|
-
<link rel="modulepreload" crossorigin href="/assets/useDesktop-
|
|
246
|
+
<link rel="modulepreload" crossorigin href="/assets/useSharedWebSocket-C17TGqT8.js">
|
|
247
|
+
<link rel="modulepreload" crossorigin href="/assets/Trans-CzhPjnjB.js">
|
|
248
|
+
<link rel="modulepreload" crossorigin href="/assets/useDesktop-DpxVGfUC.js">
|
|
249
249
|
<link rel="modulepreload" crossorigin href="/assets/provider-capabilities-lePfeT_Y.js">
|
|
250
250
|
<link rel="modulepreload" crossorigin href="/assets/save-OsUqVn99.js">
|
|
251
|
-
<link rel="modulepreload" crossorigin href="/assets/spending-
|
|
251
|
+
<link rel="modulepreload" crossorigin href="/assets/spending-CYotH-9N.js">
|
|
252
252
|
<link rel="modulepreload" crossorigin href="/assets/sparkles-DrY8hb0X.js">
|
|
253
253
|
<link rel="modulepreload" crossorigin href="/assets/x-CBMI9ejn.js">
|
|
254
|
-
<link rel="modulepreload" crossorigin href="/assets/MinimizedChatsContext-
|
|
254
|
+
<link rel="modulepreload" crossorigin href="/assets/MinimizedChatsContext-DKlPf54Y.js">
|
|
255
255
|
<link rel="modulepreload" crossorigin href="/assets/zap-CUOlvtT1.js">
|
|
256
256
|
<link rel="modulepreload" crossorigin href="/assets/file-text-BcLCfcWZ.js">
|
|
257
257
|
<link rel="modulepreload" crossorigin href="/assets/circle-x-CMYsN4ZH.js">
|
|
@@ -266,22 +266,22 @@
|
|
|
266
266
|
<link rel="modulepreload" crossorigin href="/assets/search-Cq_gE3Ik.js">
|
|
267
267
|
<link rel="modulepreload" crossorigin href="/assets/trash-2-BOU3WdZZ.js">
|
|
268
268
|
<link rel="modulepreload" crossorigin href="/assets/triangle-alert-H9T4jAmh.js">
|
|
269
|
-
<link rel="modulepreload" crossorigin href="/assets/jira-api-
|
|
269
|
+
<link rel="modulepreload" crossorigin href="/assets/jira-api-CbJvbRYx.js">
|
|
270
270
|
<link rel="modulepreload" crossorigin href="/assets/clsx-CjueKrWZ.js">
|
|
271
271
|
<link rel="modulepreload" crossorigin href="/assets/utils-BC4DHlz2.js">
|
|
272
272
|
<link rel="modulepreload" crossorigin href="/assets/dialog-CMgps2MY.js">
|
|
273
273
|
<link rel="modulepreload" crossorigin href="/assets/lib-CGdyxIWg.js">
|
|
274
|
-
<link rel="modulepreload" crossorigin href="/assets/getTimezoneOffsetInMilliseconds-
|
|
275
|
-
<link rel="modulepreload" crossorigin href="/assets/formatDistanceToNow-
|
|
274
|
+
<link rel="modulepreload" crossorigin href="/assets/getTimezoneOffsetInMilliseconds-DxUFGcVq.js">
|
|
275
|
+
<link rel="modulepreload" crossorigin href="/assets/formatDistanceToNow-DlQaXc7c.js">
|
|
276
276
|
<link rel="modulepreload" crossorigin href="/assets/button-D1xExSTF.js">
|
|
277
|
-
<link rel="modulepreload" crossorigin href="/assets/project-repositories-
|
|
278
|
-
<link rel="modulepreload" crossorigin href="/assets/RepositoryScopeSelector-
|
|
279
|
-
<link rel="modulepreload" crossorigin href="/assets/TicketDetailModalContext-
|
|
277
|
+
<link rel="modulepreload" crossorigin href="/assets/project-repositories-BkBD3rxx.js">
|
|
278
|
+
<link rel="modulepreload" crossorigin href="/assets/RepositoryScopeSelector-BeM-2gIZ.js">
|
|
279
|
+
<link rel="modulepreload" crossorigin href="/assets/TicketDetailModalContext-CkVXuHxU.js">
|
|
280
280
|
<link rel="modulepreload" crossorigin href="/assets/core-B3mKBIh5.js">
|
|
281
|
-
<link rel="modulepreload" crossorigin href="/assets/RepositoryDeliveries-
|
|
282
|
-
<link rel="modulepreload" crossorigin href="/assets/useRuntimeRuns-
|
|
281
|
+
<link rel="modulepreload" crossorigin href="/assets/RepositoryDeliveries-CJVpOUpZ.js">
|
|
282
|
+
<link rel="modulepreload" crossorigin href="/assets/useRuntimeRuns-CfGRbhr9.js">
|
|
283
283
|
<link rel="modulepreload" crossorigin href="/assets/event-Bt7f3wXe.js">
|
|
284
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
284
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Bfunvi6w.css">
|
|
285
285
|
</head>
|
|
286
286
|
<body>
|
|
287
287
|
<div id="specrails-splash" aria-hidden="false" role="status" aria-label="Loading Specrails">
|
|
@@ -446,6 +446,7 @@ A non-developer-friendly file tree + Monaco viewer with plain-language AI summar
|
|
|
446
446
|
| `PUT` | `/:railIndex/name` | Set the rail's display name. Body `{ name }` (string or `null` to clear back to the default label); 400 if longer than 60 characters |
|
|
447
447
|
| `POST` | `/:railIndex/launch` | Launch the rail. Body `{ mode?, loopId?, profileName?, aiEngine?, model?, reasoning_effort?, originConversationId?, originSurface?, interactive? }`. `mode` is `implement` / `freestyle` / `loop` (the removed Batch mode's `batch-implement` / `batch` are accepted as input aliases and normalized to `implement`); a factory `loopId` maps to its canonical rail mode (`factory:batch` resolves to `factory:implement`). `model` and `reasoning_effort` are validated against the effective adapter/model (Kimi effort is K3-only). Kimi + a loop containing a Decider returns a capability error before the first step. Claude alone advertises persistent in-job stdin; `interactive` remains wire-compatible. Returns `202 { jobId, railIndex, mode }`; a missing provider CLI fails before a job starts. |
|
|
448
448
|
| `POST` | `/:railIndex/stop` | Stop the rail's running job (cancels every job the rail registered) |
|
|
449
|
+
| `POST` | `/:railIndex/relaunch` | Relaunch a settled source. Body `{ sourceId: deliveryId or 'run:<runId>', originConversationId?, originSurface? }`. Uses its recorded specs and admitted options, restores empty assignments after preflight and atomically replaces an undelivered failed generation. Returns the same `202` launch response. Reused/removed rails, active/stale sources, missing specs/workflow and delivered work needing recovery return actionable errors. Request-side workflow overrides are ignored. |
|
|
449
450
|
|
|
450
451
|
---
|
|
451
452
|
|
|
@@ -13,13 +13,17 @@ framework is rejected rather than silently replacing an update.
|
|
|
13
13
|
|
|
14
14
|
## Persistence and publication
|
|
15
15
|
|
|
16
|
-
The release bundle pins Core 6.1
|
|
16
|
+
The release bundle pins Core 6.2.1 in `desktop-release.yml` and
|
|
17
17
|
`scripts/assemble-bundled-core.lock.json`. Core 6 is the Desktop-only engine: it
|
|
18
|
-
publishes integration contract 5.
|
|
18
|
+
publishes integration contract 5.1 (no standalone `update`) and ships only the
|
|
19
19
|
implement and retry workflows (Batch was folded into implement; Desktop no
|
|
20
20
|
longer requires `batch-implement` but stays compatible with Cores that still ship it). Update both pins together and check compatibility against
|
|
21
21
|
the staged published package; retained runs still use their original runtime.
|
|
22
22
|
|
|
23
|
+
The paired CI checkout uses the immutable Core 6.2.1 release commit
|
|
24
|
+
`ae52a5ff0ff9dce024008adf16115e7f04d1ef35`. Bundle smoke checks use Node 22.22.3,
|
|
25
|
+
matching Core's minimum supported Node version and the Desktop release runtime.
|
|
26
|
+
|
|
23
27
|
Desktop updates retain the complete npm installation, including dependencies,
|
|
24
28
|
under `~/.specrails/core/<version>/`. The registry home override applies to this
|
|
25
29
|
directory. Only the managed package corresponding to the active framework is
|
|
@@ -61,6 +65,9 @@ rollback restored already updated copies. **Finish updating** retries with the
|
|
|
61
65
|
retained package and works offline. Successful completion is emitted only after
|
|
62
66
|
all workspace refreshes and the final recovery-state write succeed.
|
|
63
67
|
|
|
68
|
+
Core 6 registry updates and same-version runtime repairs are offered using the
|
|
69
|
+
same supported-major policy as runtime selection.
|
|
70
|
+
|
|
64
71
|
## Version reporting
|
|
65
72
|
|
|
66
73
|
Global status distinguishes the selected runtime and its source, the active
|
|
@@ -54,6 +54,29 @@ real committed full verification and the reviewed/approved candidate before
|
|
|
54
54
|
publishing the recoverable write set. The final host check certifies the archived
|
|
55
55
|
candidate. Reviewer assertions remain AI evidence and cannot create verification.
|
|
56
56
|
|
|
57
|
+
Implement checks the host candidate hash after each correction. When verification
|
|
58
|
+
failed and the correction leaves that candidate unchanged, it ends unsuccessfully
|
|
59
|
+
with the corrector's explanation instead of rerunning the same failed checks.
|
|
60
|
+
Corrections after a passing verification may still refresh review evidence without
|
|
61
|
+
source changes. Failed completion reasons are surfaced in Desktop's job error.
|
|
62
|
+
|
|
63
|
+
The corrector receives the exact verification command, cwd, original exit code,
|
|
64
|
+
failure summary, full-evidence identifier and current candidate's review output
|
|
65
|
+
directly. The recipe explicitly clears review feedback when verification fails
|
|
66
|
+
or its candidate no longer matches, including before the first reviewer visit.
|
|
67
|
+
It reproduces the failure, distinguishes implementation defects from test
|
|
68
|
+
compatibility assumptions and external blockers, and may make a minimal test
|
|
69
|
+
compatibility repair inside the admitted workspace while preserving the tested
|
|
70
|
+
behavior and every required safety guard. An unchanged filename alone cannot
|
|
71
|
+
justify an unrelated-failure diagnosis. Focused checks retain their original exit
|
|
72
|
+
codes; repaired assertions need negative cases. Saved/customized definitions keep
|
|
73
|
+
their frozen prompts; future unedited recipes use these updated defaults.
|
|
74
|
+
|
|
75
|
+
The paired Core also supplies the official verify skill and real status/apply
|
|
76
|
+
context before a read-only reviewer turn, including custom `assess` roles. Missing
|
|
77
|
+
planning context still blocks invocation; host preparation does not approve the
|
|
78
|
+
candidate or replace the review's inspection and structured findings.
|
|
79
|
+
|
|
57
80
|
Admission freezes the graph, agents, Core package and repository checks. Resume
|
|
58
81
|
uses this snapshot. Generic forks copy an active change into a separate change,
|
|
59
82
|
carry scope-owned contracts forward, rebind `run.changeId`, and clear verification.
|
|
@@ -34,6 +34,20 @@ Mission mode (Agent Mode) replaces the routed dashboard, so `RailsBoard`, `Specs
|
|
|
34
34
|
|
|
35
35
|
## Protocol contract
|
|
36
36
|
|
|
37
|
+
Relaunch identifies the card's delivery or shared run with
|
|
38
|
+
`POST /rails/:railIndex/relaunch { sourceId, originConversationId?, originSurface? }`.
|
|
39
|
+
It uses the source's saved specs and launch options, even after terminal cleanup
|
|
40
|
+
emptied the rail. New attempts freeze these options in migration 71; historical
|
|
41
|
+
ones reconstruct only recorded loop/runtime/manifest fields. A missing workflow
|
|
42
|
+
requires a configured fresh launch. A rail reused for different specs is left
|
|
43
|
+
untouched; stale cards and delivered work that needs recovery are rejected.
|
|
44
|
+
Independent per-ticket siblings do not invalidate each other; newer overlapping
|
|
45
|
+
spec ownership does. A successful unit reporting no changes is not delivered work.
|
|
46
|
+
Undelivered failed generations are atomically superseded in fresh worktrees,
|
|
47
|
+
with allocation rollback and the original failed checkout retained. A synchronous
|
|
48
|
+
click guard prevents duplicate submissions; accepted relaunches stay disabled
|
|
49
|
+
until the new card appears, and rejected admission details remain inline.
|
|
50
|
+
|
|
37
51
|
```rail-launch
|
|
38
52
|
{
|
|
39
53
|
"version": 1,
|
|
@@ -85,6 +99,7 @@ WS messages: `agent_pr_decision` (existing, now carries the new fields), `agent_
|
|
|
85
99
|
|
|
86
100
|
- `client/src/features/missions/components/AgentMessage.tsx` — extraction step after spec-draft (flag `FEATURE_MISSION_RAIL_CARDS`); renders `AgentRailLaunchCard` per proposal, `AgentRailLaunchPending`, `AgentRailLaunchUnreadable`.
|
|
87
101
|
- `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`.
|
|
102
|
+
- Repository context (`/repositories`) is required before Play. The card shows launch targets and each spec's saved scope, including primary-only historical specs. **Edit → Save repositories** persists only `repositoryIds` through the pinned project's existing ticket PATCH; Cancel leaves it untouched. Saved scope and spec-list changes reconcile required targets while retaining extra launch targets and dropping workspace entries for removed repositories. Optional launch targets do not edit specs. Omitted required targets and missing/offline/context-only implementation targets block Play visibly. **Refresh** reloads assignments; failed loads expose **Retry loading**. The resolved launch scope and filtered workspace selection are frozen in the launched intent, and server admission remains authoritative.
|
|
88
103
|
- `client/src/features/rails/lib/rail-launch-intents.ts` — session overlay (`recordLocalIntent`, `intentFor`); `useRailLaunchProposals.ts` — undecided proposals for the dock; `AgentPrPinnedDock.tsx` — proposals pinned above PR cards.
|
|
89
104
|
- `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
105
|
- `agent-pr-pinning.ts` `isPrEnvelopePinned`; `agent-run-failure.ts` (`parseRunFailureRow`, `systemBriefingRunId`, `FOCUS_PR_CARD_EVENT`, `MISSION_OPEN_RUN_EVENT`, `requestMissionOpenRun`); `AgentRunFailureMarker.tsx`.
|
|
@@ -217,6 +217,12 @@ thresholds and publish separate reports. Native macOS, Windows Core assembly and
|
|
|
217
217
|
runtime portability jobs remain required. The final `test` job preserves the check
|
|
218
218
|
name used by branch protection and rejects any failed, skipped or cancelled lane.
|
|
219
219
|
|
|
220
|
+
The paired Core lane uses an immutable commit in `.github/workflows/ci.yml`.
|
|
221
|
+
When Desktop tests require new Core behavior, push that Core commit first and
|
|
222
|
+
update the pin in the same Desktop change. Validate the real bridge suites with
|
|
223
|
+
`SPECRAILS_CORE_SOURCE_DIR` selecting the built Core checkout; a missing checkout
|
|
224
|
+
skips these optional local tests and does not prove paired compatibility.
|
|
225
|
+
|
|
220
226
|
Push CI still runs on every branch: native release validation requires a trusted
|
|
221
227
|
push run for the exact branch and commit. PR and merge-queue runs remain enabled.
|
|
222
228
|
Concurrency cancels superseded runs within the same event/ref. No path filters
|
|
@@ -113,7 +113,12 @@ Both temporary checkouts use non-force cleanup, including after a Git runner exc
|
|
|
113
113
|
## Launch and recovery wiring (`server/modules/delivery/runtime/rail-isolated-launch.ts`)
|
|
114
114
|
|
|
115
115
|
- `prMode` is captured once. Admission is rechecked inside the per-repository allocation lock;
|
|
116
|
-
two requests cannot both create a generation or
|
|
116
|
+
two requests cannot both create a generation or claim the same continuation branch.
|
|
117
|
+
- Fresh launches allocate `<worktreesRoot>/<runId>/ticket-<id>` and a new collision-free
|
|
118
|
+
branch from the selected base. Failed attempts retain their dirty mounts and branches
|
|
119
|
+
for explicit recovery; pressing Launch never silently adopts their edits. Same-run
|
|
120
|
+
recovery uses the frozen mount. Recorded delivery/PR continuations find the exact
|
|
121
|
+
mounted branch and verify its recorded commit before execution.
|
|
117
122
|
- A continuation supersedes its prior generation and creates the new `building` row in one DB
|
|
118
123
|
transaction. Allocation failure closes the new row and restores the prior generation atomically,
|
|
119
124
|
recording the failed replacement id so every client can safely undo its tombstone for that pair.
|
|
@@ -121,6 +126,16 @@ Both temporary checkouts use non-force cleanup, including after a Git runner exc
|
|
|
121
126
|
verification distinguish changed, resumed, and no-change results.
|
|
122
127
|
- Per-unit settlement returns structured execution + delivery results. `onLoopRunFinished` receives
|
|
123
128
|
the engine outcome only; commit/status/ref/provenance/push failures cannot rewrite it.
|
|
129
|
+
- Before staging, settlement blocks active OpenSpec change directories introduced relative to
|
|
130
|
+
the frozen integration/base commit, including the run's own unarchived change. Base changes
|
|
131
|
+
and archives are allowed. It reports the paths as a blocked `commit_failed` delivery and
|
|
132
|
+
preserves the checkout; Core never archives unrelated changes on Desktop's behalf.
|
|
133
|
+
- Revision semantics require actual delivered branch/commit evidence. Open addenda on an
|
|
134
|
+
undelivered failed generation replace it atomically with a fresh full-spec launch, without
|
|
135
|
+
a revision briefing or branch adoption. Equivalent delta retries use readable, stable,
|
|
136
|
+
Core-compatible names derived from durable lineage/addendum bodies rather than run IDs.
|
|
137
|
+
All frozen addenda must be claimed before any agent starts; claim failure reopens earlier
|
|
138
|
+
sibling claims and rolls allocation back through the existing ownership-aware path.
|
|
124
139
|
- Clean, committed worktrees may be released only after a final live preflight proves tracked and
|
|
125
140
|
untracked status (apart from durably recorded Specrails overlay paths), plus exact worktree HEAD
|
|
126
141
|
and branch ref. Gitignored paths are release-safe only when covered by the IMMUTABLE settlement
|
|
@@ -219,9 +234,12 @@ at launch:
|
|
|
219
234
|
from the first root containing them when checkout lacks them. Windows: junction →
|
|
220
235
|
dereferencing-copy fallback.
|
|
221
236
|
- **Never on the PR**: the resume-safe manifest records authenticated overlay leaves against
|
|
222
|
-
any configured root
|
|
237
|
+
any configured root across supported provider namespaces, so switching provider retains
|
|
238
|
+
prior authenticated exclusions; settlement persists immutable fingerprints. Commit delivery deliberately
|
|
223
239
|
runs plain `git add -A`, audits the index, resets forbidden literal top-level paths, re-audits,
|
|
224
|
-
and makes those exclusions authoritative with `git commit --only`.
|
|
240
|
+
and makes those exclusions authoritative with `git commit --only`. Unexcluded staged symlinks
|
|
241
|
+
with absolute destinations outside the checkout block commit rather than leaking machine paths.
|
|
242
|
+
The writable manifest alone
|
|
225
243
|
never grants cleanup authority. Cleanup atomically renames each authenticated root into a unique
|
|
226
244
|
sibling quarantine, revalidates it after the move, discloses raced content there, and never
|
|
227
245
|
deletes or restores quarantined paths automatically over a possibly recreated source. A modified
|
|
@@ -371,9 +389,10 @@ diff collection), fully unit-tested.
|
|
|
371
389
|
nothing (`<type>/<ref>`). Every generated name passes `isValidBranchName`.
|
|
372
390
|
- **Batch branch** (N>1 assembled delivery) — `<type>/<primary-ref>-batch-<n>-tickets`.
|
|
373
391
|
- **Collisions** — bounded suffixing `-2`, `-3`… (20 attempts). At worktree allocation
|
|
374
|
-
(`rail-isolated-launch`) a
|
|
375
|
-
|
|
376
|
-
|
|
392
|
+
(`rail-isolated-launch`) a fresh run collision-suffixes even a prior branch for the same ticket;
|
|
393
|
+
only exact recorded delivery/PR continuations reuse a branch. Same-run recovery retains its
|
|
394
|
+
frozen checkout. Exhaustion falls back to the legacy `sr/<slug>/ticket-<id>` with fresh-branch
|
|
395
|
+
validation. The **integration
|
|
377
396
|
branch is never used** (reserved in the resolver + asserted in tests). `createWorktree` stays
|
|
378
397
|
generic: an optional preferred `branch` input with the legacy fallback when absent.
|
|
379
398
|
- **PR title** — `[<ref>]<type> - <change>` (e.g. `[SKILLS-101]feat - darkmode added`); the
|
|
@@ -781,4 +800,3 @@ typed, frozen delta that travels with the DELIVERY, never with the spec.
|
|
|
781
800
|
scope-creep detection, PR head-drift revalidation policy, partial-commit
|
|
782
801
|
recovery UX (the preparation-failure Discard fix above covers the stuck-rail
|
|
783
802
|
half of the incident).
|
|
784
|
-
|
|
@@ -5,7 +5,7 @@ architectural dependency declaration. Search a heading or filename and read only
|
|
|
5
5
|
the relevant source and tests. Feature prefixes group the legacy server files;
|
|
6
6
|
new bounded modules live under `server/modules/`.
|
|
7
7
|
|
|
8
|
-
Includes
|
|
8
|
+
Includes 1014 source/build files. Nearby tests are linked where names
|
|
9
9
|
match; integration suites may live elsewhere. Use `rg` to find other consumers.
|
|
10
10
|
|
|
11
11
|
## cli
|
|
@@ -717,6 +717,7 @@ match; integration suites may live elsewhere. Use `rg` to find other consumers.
|
|
|
717
717
|
|
|
718
718
|
## client/src/features/subscription-usage/components
|
|
719
719
|
|
|
720
|
+
- [EnterpriseSpendMeter.tsx](../../client/src/features/subscription-usage/components/EnterpriseSpendMeter.tsx)
|
|
720
721
|
- [SubscriptionUsageFooter.tsx](../../client/src/features/subscription-usage/components/SubscriptionUsageFooter.tsx)
|
|
721
722
|
- [SubscriptionUsagePanel.tsx](../../client/src/features/subscription-usage/components/SubscriptionUsagePanel.tsx)
|
|
722
723
|
- [SubscriptionUsageSection.tsx](../../client/src/features/subscription-usage/components/SubscriptionUsageSection.tsx)
|
|
@@ -1219,6 +1220,7 @@ match; integration suites may live elsewhere. Use `rg` to find other consumers.
|
|
|
1219
1220
|
- [rail-pr-recovery-git.ts](../../server/modules/delivery/runtime/rail-pr-recovery-git.ts) · [test](../../server/modules/delivery/runtime/rail-pr-recovery-git.test.ts)
|
|
1220
1221
|
- [rail-pr-store.ts](../../server/modules/delivery/runtime/rail-pr-store.ts) · [test](../../server/modules/delivery/runtime/rail-pr-store.test.ts)
|
|
1221
1222
|
- [rail-pr-ticket-effects.ts](../../server/modules/delivery/runtime/rail-pr-ticket-effects.ts) · [test](../../server/modules/delivery/runtime/rail-pr-ticket-effects.test.ts)
|
|
1223
|
+
- [rail-relaunch.ts](../../server/modules/delivery/runtime/rail-relaunch.ts) · [test](../../server/modules/delivery/runtime/rail-relaunch.test.ts)
|
|
1222
1224
|
- [rail-worktree-release.ts](../../server/modules/delivery/runtime/rail-worktree-release.ts) · [test](../../server/modules/delivery/runtime/rail-worktree-release.test.ts)
|
|
1223
1225
|
- [rail-worktrees-store.ts](../../server/modules/delivery/runtime/rail-worktrees-store.ts) · [test](../../server/modules/delivery/runtime/rail-worktrees-store.test.ts)
|
|
1224
1226
|
- [rails-router.ts](../../server/modules/delivery/runtime/rails-router.ts) · [test](../../server/modules/delivery/runtime/rails-router.test.ts)
|
|
@@ -16,6 +16,11 @@ The canon below is the **majority-existing code**, promoted to law. Reference im
|
|
|
16
16
|
- **Priority canon** (the ProposeSpecModal set — the only fully tokenized one): critical=`destructive`, high=`accent-warning`, medium=`accent-info`, low=`muted`.
|
|
17
17
|
- **Left sidebar title = accent-primary, right sidebars = accent-secondary** (existing rule; document, keep).
|
|
18
18
|
|
|
19
|
+
The left sidebar footer groups desktop tools behind a Settings gear and right
|
|
20
|
+
chevron disclosure, closed initially. Expanded tools appear vertically in this
|
|
21
|
+
order: Companion (when available), Analytics, Docs. The Settings icon and label
|
|
22
|
+
open the app settings modal; a separate chevron button toggles the group.
|
|
23
|
+
|
|
19
24
|
## 1.2 Icon scale (lucide, `w-N h-N` pair idiom — never `size={}` props)
|
|
20
25
|
| Context | Size | Margin |
|
|
21
26
|
|---|---|---|
|
package/package.json
CHANGED
|
@@ -73,7 +73,7 @@ class CoreUpdateManager {
|
|
|
73
73
|
this.latestVersion != null &&
|
|
74
74
|
currentVersion != null &&
|
|
75
75
|
((0, semver_lite_1.isNewer)(this.latestVersion, currentVersion) || selected.error !== null && this.latestVersion === currentVersion) &&
|
|
76
|
-
|
|
76
|
+
(0, core_package_1.isSupportedCoreVersion)(this.latestVersion);
|
|
77
77
|
return {
|
|
78
78
|
available: this.isAvailable(),
|
|
79
79
|
currentVersion,
|
|
@@ -1608,6 +1608,15 @@ const MIGRATIONS = [
|
|
|
1608
1608
|
(db) => {
|
|
1609
1609
|
db.exec(`UPDATE rails SET mode='implement' WHERE mode IN ('batch-implement','batch')`);
|
|
1610
1610
|
},
|
|
1611
|
+
// Migration 71: source-bound rail relaunch. NULL preserves historical rows;
|
|
1612
|
+
// new attempts freeze their admitted launch options before agent execution.
|
|
1613
|
+
(db) => {
|
|
1614
|
+
for (const table of ['rail_pr_deliveries', 'loop_runs']) {
|
|
1615
|
+
const columns = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
1616
|
+
if (!columns.some(column => column.name === 'launch_config_json'))
|
|
1617
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN launch_config_json TEXT`);
|
|
1618
|
+
}
|
|
1619
|
+
},
|
|
1611
1620
|
];
|
|
1612
1621
|
function applyMigrations(db) {
|
|
1613
1622
|
// Ensure the migrations table exists (migration 1 creates it, but we need
|
|
@@ -7,6 +7,7 @@ exports.readFrozenRuntimeHost = readFrozenRuntimeHost;
|
|
|
7
7
|
exports.runAgentRuntimeInvocation = runAgentRuntimeInvocation;
|
|
8
8
|
exports.assertRetainedForkSupport = assertRetainedForkSupport;
|
|
9
9
|
exports.runAgentRuntimeControl = runAgentRuntimeControl;
|
|
10
|
+
const agent_runtime_verification_suggestions_1 = require("./agent-runtime-verification-suggestions");
|
|
10
11
|
const agent_runtime_history_1 = require("./agent-runtime-history");
|
|
11
12
|
const agent_runtime_repositories_1 = require("./agent-runtime-repositories");
|
|
12
13
|
const node_util_1 = require("node:util");
|
|
@@ -67,6 +68,91 @@ function scopedHostChecks(checks, repositories = []) {
|
|
|
67
68
|
return [{ ...check, cwd }];
|
|
68
69
|
});
|
|
69
70
|
}
|
|
71
|
+
/** Discover only checks belonging to the admitted worktrees. Explicit host
|
|
72
|
+
* checks win; inferred commands are frozen per run, never saved to project settings. */
|
|
73
|
+
function completeHostChecks(config, repositories) {
|
|
74
|
+
for (const repository of repositories) {
|
|
75
|
+
if (config.verification.some(check => check.repositoryId === repository.id) || !repository.path)
|
|
76
|
+
continue;
|
|
77
|
+
const workspacePaths = (repository.scope?.length ? repository.scope.map(scope => (0, node_path_1.resolve)(repository.path, scope)) : [repository.path]).filter(workspace => (0, node_fs_1.existsSync)(workspace));
|
|
78
|
+
const root = (0, node_fs_1.realpathSync)(repository.path);
|
|
79
|
+
for (const workspace of workspacePaths) {
|
|
80
|
+
const inside = (0, node_path_1.relative)(root, (0, node_fs_1.realpathSync)(workspace));
|
|
81
|
+
if ((0, node_path_1.isAbsolute)(inside) || inside === '..' || inside.startsWith('..' + node_path_1.sep))
|
|
82
|
+
throw new Error('Verification workspace escapes its admitted repository');
|
|
83
|
+
}
|
|
84
|
+
const suggestions = (0, agent_runtime_verification_suggestions_1.suggestVerificationCommands)([{ id: repository.id, path: repository.path, workspacePaths }]);
|
|
85
|
+
config.verification.push(...suggestions.map(({ reason: _reason, ...check }) => check));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** Configured-only gates cannot obtain missing checks from an agent later. */
|
|
89
|
+
function requiresConfiguredChecks(value) {
|
|
90
|
+
if (!value || typeof value !== 'object')
|
|
91
|
+
return false;
|
|
92
|
+
const body = value;
|
|
93
|
+
return Object.values(body.nodes ?? {}).some(node => node.kind === 'verify' && node.params?.commands === 'configured' && !node.params.additionalCommandsFrom && node.params.unverified !== true)
|
|
94
|
+
|| Object.values(body.components ?? {}).some(requiresConfiguredChecks);
|
|
95
|
+
}
|
|
96
|
+
/** Keep TAP failure diagnostics and totals in the readable log. The unfiltered
|
|
97
|
+
* event stream remains available as runtime evidence. State is invocation-local. */
|
|
98
|
+
function verificationLogFilter() {
|
|
99
|
+
const streams = new Map();
|
|
100
|
+
return text => (0, node_util_1.stripVTControlCharacters)(text).split(/(?<=\n)/).flatMap(line => {
|
|
101
|
+
const match = line.match(/^(\[verification [^\]]+\] )?(.*?)(?:\n)?$/);
|
|
102
|
+
if (!match)
|
|
103
|
+
return [line];
|
|
104
|
+
const key = match[1] ?? '', body = match[2].trim();
|
|
105
|
+
const state = streams.get(key) ?? { tap: false, failed: false, yaml: false, inputLines: null };
|
|
106
|
+
streams.set(key, state);
|
|
107
|
+
if (/AssertionError\b.*\bInput:\s*$/.test(body))
|
|
108
|
+
state.inputLines = 0;
|
|
109
|
+
else if (state.inputLines !== null) {
|
|
110
|
+
// Node prints assert.match's entire source input as quoted concatenation
|
|
111
|
+
// lines. Bound only that block; stacks, expected values and other streams
|
|
112
|
+
// remain visible and original events remain intact.
|
|
113
|
+
if (/^["'`]/.test(body)) {
|
|
114
|
+
state.inputLines++;
|
|
115
|
+
if (state.inputLines > 2)
|
|
116
|
+
return state.inputLines === 3 ? [key + '[Assertion input shortened; inspect raw evidence]\n'] : [];
|
|
117
|
+
}
|
|
118
|
+
else if (!body)
|
|
119
|
+
return [];
|
|
120
|
+
else
|
|
121
|
+
state.inputLines = null;
|
|
122
|
+
}
|
|
123
|
+
if (/^[✔✓]\s/.test(body))
|
|
124
|
+
return [];
|
|
125
|
+
if (/^[✖✗]\s/.test(body)) {
|
|
126
|
+
state.failed = true;
|
|
127
|
+
return [line];
|
|
128
|
+
}
|
|
129
|
+
if (/^TAP version |^# Subtest:/.test(body)) {
|
|
130
|
+
state.tap = true;
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
if (/^(?:not )?ok \d+\b/.test(body)) {
|
|
134
|
+
state.tap = true;
|
|
135
|
+
state.failed = body.startsWith('not ok');
|
|
136
|
+
state.yaml = false;
|
|
137
|
+
return state.failed ? [line] : [];
|
|
138
|
+
}
|
|
139
|
+
if (!state.tap)
|
|
140
|
+
return [line];
|
|
141
|
+
if (body === '---') {
|
|
142
|
+
state.yaml = true;
|
|
143
|
+
return state.failed ? [line] : [];
|
|
144
|
+
}
|
|
145
|
+
if (body === '...') {
|
|
146
|
+
state.yaml = false;
|
|
147
|
+
return state.failed ? [line] : [];
|
|
148
|
+
}
|
|
149
|
+
if (state.yaml)
|
|
150
|
+
return state.failed ? [line] : [];
|
|
151
|
+
if (/^1\.\.\d+$/.test(body) || !body)
|
|
152
|
+
return [];
|
|
153
|
+
return [line];
|
|
154
|
+
}).map(line => line.length > 2_000 ? line.slice(0, 1_900) + '\n[Long diagnostic line shortened; inspect raw evidence]\n' : line).join('');
|
|
155
|
+
}
|
|
70
156
|
exports.RUNTIME_HOST_ENV_KEYS = [
|
|
71
157
|
'SPECRAILS_REPO_DIR', 'SPECRAILS_GIT_AUTO', 'SPECRAILS_REPO_MAP_PATH',
|
|
72
158
|
'SPECRAILS_EXECUTION_CONTEXT', 'SPECRAILS_EXECUTION_MANIFEST', 'SPECRAILS_REPOSITORIES',
|
|
@@ -126,6 +212,8 @@ async function runAgentRuntimeInvocation(options) {
|
|
|
126
212
|
repositoryIds: admittedContext.repositories.map(repo => repo.id), source: options.loopConfig ? 'loop-role' : source, providerOverride: options.loopConfig ? undefined : options.providerOverride,
|
|
127
213
|
});
|
|
128
214
|
config.verification = scopedHostChecks(config.verification, admittedContext.repositories);
|
|
215
|
+
if (definitionEngine)
|
|
216
|
+
completeHostChecks(config, admittedContext.repositories);
|
|
129
217
|
if (!options.loopConfig)
|
|
130
218
|
config.rolePrompts = { ...(await (0, agent_runtime_loader_1.loadCoreAgentRuntime)()).rolePromptDefaults(), ...(0, agent_runtime_settings_1.loadRuntimeRolePrompts)(), ...config.rolePrompts };
|
|
131
219
|
// Core rejects unknown connection keys: drop the desktop-only local-engine
|
|
@@ -146,6 +234,13 @@ async function runAgentRuntimeInvocation(options) {
|
|
|
146
234
|
: options.definitionPath ? JSON.parse((0, node_fs_1.readFileSync)(options.definitionPath, 'utf8')) : undefined : undefined;
|
|
147
235
|
if (definitionEngine && !draft)
|
|
148
236
|
throw new Error('A new Core workflow requires a definition');
|
|
237
|
+
if (requiresConfiguredChecks(draft)) {
|
|
238
|
+
const missing = admittedContext.repositories.filter(repository => !config.verification.some(check => check.repositoryId === repository.id));
|
|
239
|
+
if (missing.length)
|
|
240
|
+
throw new Error(`verification_checks_missing: No configured or detected verification commands for ${missing.map(repository => repository.name ?? repository.id).join(', ')}. Configure checks in project agent-runtime settings before starting this workflow.`);
|
|
241
|
+
if (config.verification.length > 100)
|
|
242
|
+
throw new Error('verification_checks_limit: This workflow supports at most 100 verification commands. Reduce the configured checks before starting it.');
|
|
243
|
+
}
|
|
149
244
|
const selectedOrigins = (0, agent_runtime_effective_config_1.bindWorkflowRoleSelections)(config, draft, options.workflowRoleBindings);
|
|
150
245
|
if (options.loopConfig && draft?.roles?.includes('loop-decider') && !config.roles?.['loop-decider'])
|
|
151
246
|
throw new Error('The loop must define its loop-decider agent');
|
|
@@ -241,6 +336,7 @@ async function runAgentRuntimeInvocation(options) {
|
|
|
241
336
|
let result;
|
|
242
337
|
let graph;
|
|
243
338
|
let stderr = '', summary = '', invalidProtocol = false, timedOut = false;
|
|
339
|
+
const stepFailures = new Map();
|
|
244
340
|
let timer;
|
|
245
341
|
const child = (0, node_child_process_1.spawn)((0, core_node_runtime_1.resolveCoreNodeRuntime)(), args, {
|
|
246
342
|
cwd: options.cwd, env: (0, win_spawn_1.windowsSpawnEnv)(options.env), shell: false, windowsHide: true,
|
|
@@ -260,6 +356,7 @@ async function runAgentRuntimeInvocation(options) {
|
|
|
260
356
|
callback?.();
|
|
261
357
|
}
|
|
262
358
|
catch { /* Logging cannot replay a workflow. */ } };
|
|
359
|
+
const readableVerification = verificationLogFilter();
|
|
263
360
|
const lines = (0, node_readline_1.createInterface)({ input: child.stdout });
|
|
264
361
|
lines.on('line', line => {
|
|
265
362
|
if (line.length > 2_000_000) {
|
|
@@ -310,8 +407,17 @@ async function runAgentRuntimeInvocation(options) {
|
|
|
310
407
|
}
|
|
311
408
|
else if (event.type === 'workflow-event') {
|
|
312
409
|
const payload = event.event;
|
|
313
|
-
|
|
314
|
-
|
|
410
|
+
const kind = payload?.type === 'step_succeeded' && payload.outcome === 'fail' ? 'verification_failed' : payload?.type;
|
|
411
|
+
const detail = payload?.error?.message ?? payload?.message;
|
|
412
|
+
const stepKey = JSON.stringify([payload?.scopeId ?? null, payload?.nodePath ?? payload?.stepId ?? null]);
|
|
413
|
+
if (payload?.type === 'step_failed' && typeof detail === 'string' && detail.trim()) {
|
|
414
|
+
stepFailures.delete(stepKey);
|
|
415
|
+
stepFailures.set(stepKey, detail.trim().slice(0, 4_000));
|
|
416
|
+
}
|
|
417
|
+
else if (payload?.type === 'step_succeeded')
|
|
418
|
+
stepFailures.delete(stepKey);
|
|
419
|
+
if (kind)
|
|
420
|
+
observe(() => options.onLine?.(`[runtime] ${kind}${payload.stepId ? ': ' + payload.stepId : ''}${detail ? ' — ' + detail : ''}\n`));
|
|
315
421
|
}
|
|
316
422
|
else if (event.type === 'agent-event') {
|
|
317
423
|
const payload = event.event;
|
|
@@ -328,7 +434,9 @@ async function runAgentRuntimeInvocation(options) {
|
|
|
328
434
|
}
|
|
329
435
|
}
|
|
330
436
|
else if (event.type === 'verification-output' && typeof event.text === 'string') {
|
|
331
|
-
|
|
437
|
+
const readable = readableVerification(String(event.text));
|
|
438
|
+
if (readable)
|
|
439
|
+
observe(() => options.onLine?.(readable));
|
|
332
440
|
}
|
|
333
441
|
else if (event.type === 'span') {
|
|
334
442
|
// Trace spans are telemetry; the raw line is already recorded for diagnostics.
|
|
@@ -364,6 +472,7 @@ async function runAgentRuntimeInvocation(options) {
|
|
|
364
472
|
const validPause = definitionEngine && code === 2 && result?.status === 'paused';
|
|
365
473
|
const failed = (!validPause && (code !== 0 || result?.status !== 'succeeded')) || invalidProtocol || timedOut || Boolean(observerError) || Boolean(acceptanceBlocked);
|
|
366
474
|
const runtimeError = typeof result?.error === 'string' ? result.error : result?.error?.message;
|
|
475
|
+
const completionError = Array.isArray(result?.completion?.reasons) ? result.completion.reasons.filter(reason => typeof reason === 'string' && reason.trim()).join('; ').slice(0, 4_000) : '';
|
|
367
476
|
const runtimeStatus = invalidProtocol || timedOut || observerError || (result?.status === 'paused' && !validPause) || (result?.status === 'succeeded' && code !== 0) ? 'failed' : acceptanceBlocked ? 'blocked' : ['succeeded', 'paused', 'failed', 'blocked', 'cancelled'].includes(result?.status ?? '') ? result.status : 'failed';
|
|
368
477
|
const errorText = observerError ?? (timedOut ? 'Programmatic workflow timed out; inspect its checkpoint before recovery'
|
|
369
478
|
: invalidProtocol ? 'Core returned an invalid runtime event stream'
|
|
@@ -371,9 +480,9 @@ async function runAgentRuntimeInvocation(options) {
|
|
|
371
480
|
: runtimeError ?? (result?.status === 'paused' ? (typeof result.pendingQuestion?.question === 'string' && result.pendingQuestion.question.trim()
|
|
372
481
|
? `Workflow awaits an answer in Agent Runtime settings: ${result.pendingQuestion.question.trim().slice(0, 500)}`
|
|
373
482
|
: 'Workflow awaits approval in Agent Runtime settings')
|
|
374
|
-
: failed ? stderr || 'Core exited without a successful programmatic workflow result' : undefined));
|
|
483
|
+
: failed ? completionError || [...stepFailures.values()].at(-1) || stderr || 'Core exited without a successful programmatic workflow result' : undefined));
|
|
375
484
|
try {
|
|
376
|
-
(0, agent_runtime_history_1.writeRuntimeHistory)(options.contextPath, invalidProtocol || timedOut || observerError || !result ? { status: 'failed', error: errorText } : { ...result });
|
|
485
|
+
(0, agent_runtime_history_1.writeRuntimeHistory)(options.contextPath, invalidProtocol || timedOut || observerError || !result ? { status: 'failed', error: errorText } : { ...result, ...(failed && errorText ? { error: errorText } : {}) });
|
|
377
486
|
}
|
|
378
487
|
catch { /* Projection failure cannot replay a completed execution. */ }
|
|
379
488
|
resolve({
|
|
@@ -231,7 +231,7 @@ async function launchMultiRepositoryRail(input, io = {}) {
|
|
|
231
231
|
integrationBranches.set(repo.id, target.branch);
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
|
-
const previousParentId = input.revision?.ofDeliveryId ?? input.repositoryContinuation?.deliveryId;
|
|
234
|
+
const previousParentId = input.revision?.ofDeliveryId ?? input.repositoryContinuation?.deliveryId ?? input.retryOfDelivery?.deliveryId;
|
|
235
235
|
const previousParent = previousParentId ? (0, rail_pr_store_1.getPrDelivery)(ctx.db, previousParentId) : undefined;
|
|
236
236
|
const previousManifest = (0, multi_repo_execution_store_1.readExecutionManifest)(previousParent?.execution_manifest);
|
|
237
237
|
const previousChildren = previousParentId ? listRepositoryDeliveries(ctx.db, previousParentId) : [];
|
|
@@ -268,12 +268,13 @@ async function launchMultiRepositoryRail(input, io = {}) {
|
|
|
268
268
|
artifactRepositoryId, selectedRepositoryIds: selected.map((repo) => repo.id), repositories: [],
|
|
269
269
|
};
|
|
270
270
|
const generation = (0, rail_pr_store_1.createPrDeliveryGeneration)(ctx.db, {
|
|
271
|
+
launchConfig: input.launchConfig,
|
|
271
272
|
id: parentId, railIndex: input.railIndex, loopId: input.loopId, railKey: `${input.railIndex}-${input.loopId}`,
|
|
272
273
|
ticketIds: input.ticketIds, baseBranch: input.baseBranch ?? '', loopName: input.loopName,
|
|
273
274
|
originSurface: input.originSurface ?? 'dashboard', originConversationId: input.originConversationId,
|
|
274
275
|
specSnapshot: (0, rail_isolated_launch_1.buildSpecSnapshot)(ctx, input.ticketIds),
|
|
275
276
|
...(input.revision ? { revisionNote: input.revision.note, revisionOf: input.revision.ofDeliveryId } : {}),
|
|
276
|
-
}, input.revision ? { id: input.revision.ofDeliveryId, decision: input.revision.decision } : input.repositoryContinuation ? { id: input.repositoryContinuation.deliveryId, decision: input.repositoryContinuation.decision } : null);
|
|
277
|
+
}, input.revision ? { id: input.revision.ofDeliveryId, decision: input.revision.decision } : input.repositoryContinuation ? { id: input.repositoryContinuation.deliveryId, decision: input.repositoryContinuation.decision } : input.retryOfDelivery ? { id: input.retryOfDelivery.deliveryId, decision: input.retryOfDelivery.decision } : null);
|
|
277
278
|
for (const child of previousChildren) {
|
|
278
279
|
if (!['merged', 'completed', 'discarded', 'superseded'].includes(child.decision))
|
|
279
280
|
(0, rail_pr_store_1.transitionDecision)(ctx.db, child.id, child.decision, 'superseded');
|
|
@@ -315,14 +316,14 @@ async function launchMultiRepositoryRail(input, io = {}) {
|
|
|
315
316
|
const childSettlement = deferred();
|
|
316
317
|
const previous = previousChildren.find((row) => row.repository_id === repository.id);
|
|
317
318
|
const previousRepository = previousManifest?.repositories.find((row) => row.repositoryId === repository.id);
|
|
318
|
-
const previousBaseBranch = previous && ['merged', 'completed'].includes(previous.decision)
|
|
319
|
+
const previousBaseBranch = input.retryOfDelivery ? undefined : previous && ['merged', 'completed'].includes(previous.decision)
|
|
319
320
|
? previousRepository?.integrationBranch ?? previous.base_branch
|
|
320
321
|
: previous?.base_branch;
|
|
321
322
|
const oldSnapshot = previous ? (0, rail_pr_store_1.toPrDeliverySnapshot)(previous) : null;
|
|
322
323
|
const revisionBranches = new Map();
|
|
323
|
-
if (oldSnapshot && !['merged', 'completed', 'discarded'].includes(oldSnapshot.decision) && !previous?.pr_url) {
|
|
324
|
+
if (!input.retryOfDelivery && oldSnapshot && !['merged', 'completed', 'discarded'].includes(oldSnapshot.decision) && !previous?.pr_url) {
|
|
324
325
|
for (const unit of oldSnapshot.branches) {
|
|
325
|
-
if (unit.finalSha)
|
|
326
|
+
if (unit.succeeded && unit.finalSha && unit.implementationOutcome !== 'failed' && unit.deliveryOutcome !== 'blocked')
|
|
326
327
|
revisionBranches.set(unit.ticketId, { branch: unit.branch, sha: unit.finalSha });
|
|
327
328
|
}
|
|
328
329
|
}
|
|
@@ -355,6 +356,7 @@ async function launchMultiRepositoryRail(input, io = {}) {
|
|
|
355
356
|
...input, ctx: childCtx, scope: effectiveScope, loopGraph: graph,
|
|
356
357
|
runtimeStateProject: ctx.project,
|
|
357
358
|
revision: input.revision && previous ? { ...input.revision, ofDeliveryId: previous.id, decision: previous.decision } : undefined,
|
|
359
|
+
retryOfDelivery: input.retryOfDelivery && previous ? { deliveryId: previous.id, decision: previous.decision } : undefined,
|
|
358
360
|
requiredPrContinuation: continuation,
|
|
359
361
|
explicitPrTarget: continuation?.prNumber ? { prNumber: continuation.prNumber } : input.explicitPrTarget,
|
|
360
362
|
originConversationId: undefined,
|