specrails-desktop 2.59.2 → 2.60.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.
- package/client/dist/assets/{ActivityFeedPage-vAY9C71W.js → ActivityFeedPage-3J7fR8yB.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-CpwjGXQm.js → AgentBrowserCapture-D4a3wYM1.js} +1 -1
- package/client/dist/assets/{AgentModeAnalyticsPane-BHeoJVxQ.js → AgentModeAnalyticsPane-Dps30tk9.js} +2 -2
- package/client/dist/assets/{AgentModeCodePane-B0Nf3YkN.js → AgentModeCodePane-CvLimey8.js} +2 -2
- package/client/dist/assets/{AgentModeJobsPane-CpbIItY_.js → AgentModeJobsPane-25tQlLpF.js} +1 -1
- package/client/dist/assets/{AgentsPage-BVJAFXn3.js → AgentsPage-CUDp_sfw.js} +2 -2
- package/client/dist/assets/{AnalyticsPage-DDWees-K.js → AnalyticsPage-DXWMGn8b.js} +1 -1
- package/client/dist/assets/{CodePage-CzV5tkvS.js → CodePage-D981zRHy.js} +1 -1
- package/client/dist/assets/{DesktopAnalyticsPage-DfRHNK7i.js → DesktopAnalyticsPage-BkFBq0E6.js} +1 -1
- package/client/dist/assets/{DocsDialog-BxduBYbh.js → DocsDialog-BL7uSbIJ.js} +1 -1
- package/client/dist/assets/{DocsPage-DOVeRJn_.js → DocsPage-CsDLbmZX.js} +1 -1
- package/client/dist/assets/{ExportDropdown-BnKVV8UK.js → ExportDropdown-B4Olg1Fk.js} +1 -1
- package/client/dist/assets/{InteractiveJobComposer-Da9dvM-0.js → InteractiveJobComposer-Z6fl4791.js} +2 -2
- package/client/dist/assets/JobDetailModal-DS_Owxs6.js +1 -0
- package/client/dist/assets/JobDetailPage-1TUQ9vlE.js +1 -0
- package/client/dist/assets/{JobsPage-0vdlbNik.js → JobsPage-BSrl8P7u.js} +1 -1
- package/client/dist/assets/LoopBuilderPage-Dh5uk2Qy.js +1 -0
- package/client/dist/assets/{LoopPreviewModal-Df-S8NGf.js → LoopPreviewModal-CH1r-KhU.js} +1 -1
- package/client/dist/assets/{LoopWindowSurface-sdb7QbSo.js → LoopWindowSurface-CiZdI6aw.js} +1 -1
- package/client/dist/assets/{LoopsPage-CDfCC9Px.js → LoopsPage-BwFujdGi.js} +1 -1
- package/client/dist/assets/{MinimizedChatsContext-DKlPf54Y.js → MinimizedChatsContext-BD2bsRBe.js} +1 -1
- package/client/dist/assets/PluginsPage-B8f49Mvn.js +2 -0
- package/client/dist/assets/{ProjectSettingsDialog-DOkjT7xv.js → ProjectSettingsDialog-D7q-O6h3.js} +1 -1
- package/client/dist/assets/{RepositoryDeliveries-CJVpOUpZ.js → RepositoryDeliveries-NNorcGzT.js} +1 -1
- package/client/dist/assets/{RepositoryScopeSelector-BeM-2gIZ.js → RepositoryScopeSelector-D-jb4bI_.js} +1 -1
- package/client/dist/assets/{ReviewPacketPage-EBCim2w_.js → ReviewPacketPage-DpZSzF-Q.js} +1 -1
- package/client/dist/assets/{TemplatePreviewModal-1-92UGwN.js → TemplatePreviewModal-5CZdAZ0G.js} +1 -1
- package/client/dist/assets/{TicketDetailModalContext-CkVXuHxU.js → TicketDetailModalContext-BHmtj1As.js} +1 -1
- package/client/dist/assets/{Trans-CzhPjnjB.js → Trans-wmTUDVyC.js} +1 -1
- package/client/dist/assets/{agentRuntime-TXPQowKJ.js → agentRuntime-7Ydcvr6P.js} +1 -1
- package/client/dist/assets/{agentRuntime-Bpx3yWHK.js → agentRuntime-BHVw9orA.js} +1 -1
- package/client/dist/assets/{agentRuntime-15Pkk6we.js → agentRuntime-DCPDaQcV.js} +1 -1
- package/client/dist/assets/{agentRuntime-C78n13To.js → agentRuntime-DKTw4Ajg.js} +1 -1
- package/client/dist/assets/{agentRuntime-CCI7XF4c.js → agentRuntime-DVhij_i_.js} +1 -1
- package/client/dist/assets/{agentRuntime-Ddqvp2bP.js → agentRuntime-Doss-T8o.js} +1 -1
- package/client/dist/assets/{agentRuntime-CKDHe2-a.js → agentRuntime-eZSMUlkM.js} +1 -1
- package/client/dist/assets/{agentRuntime-hqZRy08E.js → agentRuntime-qSSU7fQk.js} +1 -1
- package/client/dist/assets/agents-B6ZsnARo.js +1 -0
- package/client/dist/assets/agents-BORdhe0K.js +1 -0
- package/client/dist/assets/agents-C6aq9Oqs.js +1 -0
- package/client/dist/assets/agents-Cbsc4bcX.js +1 -0
- package/client/dist/assets/agents-D0uYR6hq.js +1 -0
- package/client/dist/assets/agents-Doe1sHY5.js +1 -0
- package/client/dist/assets/agents-DyNi_I9m.js +1 -0
- package/client/dist/assets/agents-StKxiTg0.js +1 -0
- package/client/dist/assets/agentstudio-7cYCXn-L.js +1 -0
- package/client/dist/assets/{agentstudio-QdAJIDef.js → agentstudio-BHWQh8G3.js} +1 -1
- package/client/dist/assets/agentstudio-BjnWHyRs.js +1 -0
- package/client/dist/assets/{agentstudio-fLXpRGIi.js → agentstudio-BkV8vcK4.js} +1 -1
- package/client/dist/assets/{agentstudio-BWAKeGgB.js → agentstudio-BzugqB_N.js} +1 -1
- package/client/dist/assets/{agentstudio-Cja8Sjcp.js → agentstudio-CWbp7KvH.js} +1 -1
- package/client/dist/assets/agentstudio-DG6plf5o.js +1 -0
- package/client/dist/assets/agentstudio-DkZVF-Cc.js +1 -0
- package/client/dist/assets/{formatDistanceToNow-DlQaXc7c.js → formatDistanceToNow-Dax4zqPF.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-DxUFGcVq.js → getTimezoneOffsetInMilliseconds-dpIo42JZ.js} +1 -1
- package/client/dist/assets/{index-Czov72g7.js → index-g8S62tpS.js} +7 -7
- package/client/dist/assets/jira-B6OYw4CJ.js +1 -0
- package/client/dist/assets/jira-BTF6RaOe.js +1 -0
- package/client/dist/assets/jira-CEetIB2q.js +1 -0
- package/client/dist/assets/jira-CIzsZLw5.js +1 -0
- package/client/dist/assets/{jira-BOoaUXbb.js → jira-Cq6yvN4q.js} +1 -1
- package/client/dist/assets/jira-Daw9tmpI.js +1 -0
- package/client/dist/assets/jira-FXr7u2lh.js +1 -0
- package/client/dist/assets/jira-LaNQn7yl.js +1 -0
- package/client/dist/assets/{jira-api-CbJvbRYx.js → jira-api-CLrIvXFm.js} +1 -1
- package/client/dist/assets/{jobs-DZAbh1m2.js → jobs-ARwD85B9.js} +1 -1
- package/client/dist/assets/{jobs-BD47Ov11.js → jobs-BMF1w4WT.js} +1 -1
- package/client/dist/assets/{jobs-tWjcDF_6.js → jobs-CBfzGLwm.js} +1 -1
- package/client/dist/assets/{jobs-CSrRx5lD.js → jobs-CPjiluBY.js} +1 -1
- package/client/dist/assets/{jobs-epP7gt8W.js → jobs-CQQd58pY.js} +1 -1
- package/client/dist/assets/{jobs-1G3wodhl.js → jobs-De3EqaO0.js} +1 -1
- package/client/dist/assets/{jobs-BLOoVRRl.js → jobs-Du9-Ei9a.js} +1 -1
- package/client/dist/assets/{jobs-B4pu8TdF.js → jobs-qMHbnxwC.js} +1 -1
- package/client/dist/assets/{project-repositories-BkBD3rxx.js → project-repositories-BkyT8AjU.js} +1 -1
- package/client/dist/assets/{spending-CYotH-9N.js → spending-Oov7p52A.js} +1 -1
- package/client/dist/assets/{useDesktop-DpxVGfUC.js → useDesktop-DpiS8Uov.js} +1 -1
- package/client/dist/assets/{useRuntimeRuns-CfGRbhr9.js → useRuntimeRuns-DBm1mul0.js} +1 -1
- package/client/dist/assets/{useSharedWebSocket-C17TGqT8.js → useSharedWebSocket-BZxKaVNT.js} +2 -2
- package/client/dist/index.html +19 -19
- package/docs/getting-started.md +2 -2
- package/docs/guide/de/agents/1-meet-the-agents.md +1 -1
- package/docs/guide/de/agents/2-profiles-and-the-balanced-default.md +1 -1
- package/docs/guide/de/agents/3-customizing-models-per-agent.md +1 -1
- package/docs/guide/de/agents/4-custom-agents-catalog.md +3 -3
- package/docs/guide/en/agents/1-meet-the-agents.md +1 -1
- package/docs/guide/en/agents/2-profiles-and-the-balanced-default.md +1 -1
- package/docs/guide/en/agents/3-customizing-models-per-agent.md +1 -1
- package/docs/guide/en/agents/4-custom-agents-catalog.md +3 -3
- package/docs/guide/es/agents/1-meet-the-agents.md +1 -1
- package/docs/guide/es/agents/2-profiles-and-the-balanced-default.md +1 -1
- package/docs/guide/es/agents/3-customizing-models-per-agent.md +1 -1
- package/docs/guide/es/agents/4-custom-agents-catalog.md +3 -3
- package/docs/guide/fr/agents/1-meet-the-agents.md +1 -1
- package/docs/guide/fr/agents/2-profiles-and-the-balanced-default.md +1 -1
- package/docs/guide/fr/agents/3-customizing-models-per-agent.md +1 -1
- package/docs/guide/fr/agents/4-custom-agents-catalog.md +3 -3
- package/docs/guide/it/agents/1-meet-the-agents.md +1 -1
- package/docs/guide/it/agents/2-profiles-and-the-balanced-default.md +1 -1
- package/docs/guide/it/agents/3-customizing-models-per-agent.md +1 -1
- package/docs/guide/it/agents/4-custom-agents-catalog.md +3 -3
- package/docs/guide/ja/agents/1-meet-the-agents.md +1 -1
- package/docs/guide/ja/agents/2-profiles-and-the-balanced-default.md +1 -1
- package/docs/guide/ja/agents/3-customizing-models-per-agent.md +1 -1
- package/docs/guide/ja/agents/4-custom-agents-catalog.md +3 -3
- package/docs/guide/pt/agents/1-meet-the-agents.md +1 -1
- package/docs/guide/pt/agents/2-profiles-and-the-balanced-default.md +1 -1
- package/docs/guide/pt/agents/3-customizing-models-per-agent.md +1 -1
- package/docs/guide/pt/agents/4-custom-agents-catalog.md +3 -3
- package/docs/guide/zh/agents/1-meet-the-agents.md +1 -1
- package/docs/guide/zh/agents/2-profiles-and-the-balanced-default.md +1 -1
- package/docs/guide/zh/agents/3-customizing-models-per-agent.md +1 -1
- package/docs/guide/zh/agents/4-custom-agents-catalog.md +3 -3
- package/docs/internals/adding-a-provider.md +7 -6
- package/docs/internals/configuration.md +45 -0
- package/docs/internals/core-runtime-updates.md +19 -0
- package/docs/internals/loop-step-log-explorer.md +27 -0
- package/docs/internals/mission-rail-cards.md +7 -0
- package/docs/internals/profiles.md +17 -10
- package/docs/internals/review-packet.md +1 -1
- package/docs/internals/safe-pr-review-flow.md +9 -0
- package/docs/internals/source-map.md +6 -2
- package/docs/jira-integration-plan.md +7 -4
- package/docs/running-pipelines.md +2 -2
- package/package.json +1 -1
- package/server/dist/core-compat.js +7 -3
- package/server/dist/core-execution.js +5 -18
- package/server/dist/jira/jira-db.js +33 -6
- package/server/dist/jira/jira-materializer.js +11 -9
- package/server/dist/jira/jira-status-resolver.js +64 -22
- package/server/dist/jira/jira-sync-manager.js +40 -1
- package/server/dist/jira-router.js +8 -41
- package/server/dist/mcp/guide.js +4 -3
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-bridge.js +22 -70
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-controls.js +7 -4
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings.js +24 -1
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-verification-log.js +252 -0
- package/server/dist/modules/agents/runtime/profiles-router.js +74 -36
- package/server/dist/modules/delivery/runtime/rail-relaunch.js +10 -2
- package/server/dist/modules/execution/runtime/queue-manager.js +7 -9
- package/server/dist/modules/loops/runtime/loop-agents.js +2 -5
- package/server/dist/modules/loops/runtime/loop-command-catalog.js +5 -5
- package/server/dist/modules/loops/runtime/loop-definition.js +1 -1
- package/server/dist/modules/loops/runtime/loop-executors.js +0 -2
- package/server/dist/modules/loops/runtime/loop-implement-recipe.js +5 -4
- package/server/dist/modules/loops/runtime/loop-run-manager.js +5 -3
- package/server/dist/modules/loops/runtime/loops-router.js +3 -1
- package/server/dist/path-resolver.js +41 -20
- package/server/dist/project-env.js +22 -6
- package/server/dist/providers/gemini-agent-ack.js +13 -10
- package/server/dist/setup-manager.js +28 -43
- package/server/dist/util/checkout-path.js +22 -0
- package/server/dist/workspace-manager.js +13 -71
- package/server/dist/worktree-node-modules.js +70 -34
- package/client/dist/assets/JobDetailModal-DhwKbZGe.js +0 -1
- package/client/dist/assets/JobDetailPage-MtPE-E_1.js +0 -1
- package/client/dist/assets/LoopBuilderPage-DCUHozXh.js +0 -1
- package/client/dist/assets/PluginsPage-iL0G3rQQ.js +0 -2
- package/client/dist/assets/agents-CA5eT1d0.js +0 -1
- package/client/dist/assets/agents-CAgy-5uS.js +0 -1
- package/client/dist/assets/agents-CNm6Qzlv.js +0 -1
- package/client/dist/assets/agents-CTwbx-fb.js +0 -1
- package/client/dist/assets/agents-DEGV1Vao.js +0 -1
- package/client/dist/assets/agents-HsDF1aQU.js +0 -1
- package/client/dist/assets/agents-IQjdA-z9.js +0 -1
- package/client/dist/assets/agents-RkGoteXh.js +0 -1
- package/client/dist/assets/agentstudio-B2CTtQgZ.js +0 -1
- package/client/dist/assets/agentstudio-DtfPcr99.js +0 -1
- package/client/dist/assets/agentstudio-X70BKf6u.js +0 -1
- package/client/dist/assets/agentstudio-qXKqVnWS.js +0 -1
- package/client/dist/assets/jira-BNZu8f9O.js +0 -1
- package/client/dist/assets/jira-BYVN4L2u.js +0 -1
- package/client/dist/assets/jira-CKJg3HZd.js +0 -1
- package/client/dist/assets/jira-DDDsZ4ub.js +0 -1
- package/client/dist/assets/jira-DXwYjVE4.js +0 -1
- package/client/dist/assets/jira-Dn6U2nRW.js +0 -1
- package/client/dist/assets/jira-j7hVolR5.js +0 -1
- package/server/dist/modules/loops/runtime/loop-agent-defaults.json +0 -6
|
@@ -61,7 +61,7 @@ export const exampleAdapter: ProviderAdapter = {
|
|
|
61
61
|
buildArgs: (action: SpawnAction, opts: SpawnOptions): string[] => { /* per-action argv */ },
|
|
62
62
|
parseStreamLine: (line: string): AdapterEvent | null => { /* line → canonical event */ },
|
|
63
63
|
extractResult: (events): NormalisedResult => { /* events → tokens/cost/session */ },
|
|
64
|
-
baselineAgents: () => ['sr-architect', 'sr-developer', 'sr-reviewer'],
|
|
64
|
+
baselineAgents: () => ['sr-architect', 'sr-developer', 'sr-reviewer'], // role identifiers, not installed files
|
|
65
65
|
detectInstalled: async (): Promise<DetectionResult> => { /* `which` + `--version` */ },
|
|
66
66
|
// Optional — see "(Optional) prepareHeadlessSpawn" below. Gemini uses it to
|
|
67
67
|
// pre-acknowledge project subagents so they load in headless `gemini -p` rails.
|
|
@@ -79,11 +79,12 @@ the optional
|
|
|
79
79
|
`prepareHeadlessSpawn` hook, `systemPromptArg: false` system-prompt folding
|
|
80
80
|
(via the `GEMINI_SYSTEM_MD` env), native OTEL with `nativeCostUsd: false`,
|
|
81
81
|
and a per-action `buildArgs` switch that throws defensively on the
|
|
82
|
-
`chat-stream` action it doesn't support. Note the
|
|
83
|
-
three
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
`chat-stream` action it doesn't support. Note the baseline is the
|
|
83
|
+
three role identifiers `['sr-architect', 'sr-developer', 'sr-reviewer']`;
|
|
84
|
+
they name roles the Core runtime defines, not files your scaffold installs,
|
|
85
|
+
and installation detection is never derived from them. `ProfileManager`
|
|
86
|
+
validation requires exactly your `baselineAgents()` to be present in every
|
|
87
|
+
profile chain, so don't add identifiers the runtime won't actually route.
|
|
87
88
|
|
|
88
89
|
The `SpawnAction` union also includes **`chat-stream`** — used by the
|
|
89
90
|
Explore persistent-stdin fast-path and interactive jobs. Only providers
|
|
@@ -82,6 +82,31 @@ Project settings apply to a single project. Open them from the project's **Setti
|
|
|
82
82
|
|
|
83
83
|
---
|
|
84
84
|
|
|
85
|
+
## Jira status mapping and pending updates
|
|
86
|
+
|
|
87
|
+
In the project's Jira connector, map **To Do**, **In Progress**, **On Review**,
|
|
88
|
+
**Done** and **Cancelled** to the real Jira statuses you want to use. Automatic
|
|
89
|
+
uses Jira's status categories; an explicit selection uses the selected status
|
|
90
|
+
identity, even when its category differs. The API accepts status names or status
|
|
91
|
+
IDs, never transition IDs. Both
|
|
92
|
+
outbound transitions and inbound polling honor the mapping. When several logical
|
|
93
|
+
states share a destination, On Review retains precedence; other ambiguities use
|
|
94
|
+
the category-derived state. Prefer distinct destinations when possible.
|
|
95
|
+
|
|
96
|
+
The pending-updates panel identifies the issue and requested destination. After
|
|
97
|
+
correcting a mapping or a Jira workflow restriction, retry the current failed
|
|
98
|
+
operation. A failed transition replaced by a later status intention is retained
|
|
99
|
+
as `superseded` history and removed from attention counts; retrying it cannot
|
|
100
|
+
revert newer work. Comments and field updates remain independently retryable.
|
|
101
|
+
Older pending retries are also suppressed when a newer status transition has
|
|
102
|
+
already completed. Normal pending operations retain their per-issue FIFO order.
|
|
103
|
+
|
|
104
|
+
Jira still determines which transitions the signed-in account can apply. A
|
|
105
|
+
missing reverse transition cannot be bypassed by selecting a target, and the
|
|
106
|
+
connector does not move through arbitrary statuses to discover possible paths.
|
|
107
|
+
If loading the status list fails, the connector preserves the saved mapping and
|
|
108
|
+
offers a retry instead of silently reducing the picker to Automatic.
|
|
109
|
+
|
|
85
110
|
## CLI
|
|
86
111
|
|
|
87
112
|
The `specrails-desktop` CLI is documented in full in [../cli.md](../cli.md). Quick reference:
|
|
@@ -134,6 +159,26 @@ specrails-desktop --port 5000 implement "#42"
|
|
|
134
159
|
|
|
135
160
|
## Environment variables
|
|
136
161
|
|
|
162
|
+
### Project execution environment
|
|
163
|
+
|
|
164
|
+
Project Settings stores the **names** of environment variables to pass through,
|
|
165
|
+
such as `NODE_AUTH_TOKEN`; it never stores their values. One parent project setting
|
|
166
|
+
applies to every repository and workspace admitted to its rails, including isolated
|
|
167
|
+
worktrees. The same policy is applied to retained runtime controls and recovery.
|
|
168
|
+
|
|
169
|
+
Non-empty values already inherited by Desktop take precedence. For missing configured names
|
|
170
|
+
on macOS/Linux, Desktop probes the login shell, using `SHELL` or the account's login
|
|
171
|
+
shell when a GUI launch does not supply it. A zsh login shell reads `~/.zprofile`
|
|
172
|
+
(and interactive configuration); configure the real credential there rather than
|
|
173
|
+
adding it to tracked repository files. Windows uses its inherited environment.
|
|
174
|
+
|
|
175
|
+
Recovered values stay in a project-owned in-memory overlay, not the server's global
|
|
176
|
+
environment, SQLite or runtime snapshots. Each project's lookup cache expires after
|
|
177
|
+
30 seconds, including failed/empty results, so a repaired profile or rotated shell
|
|
178
|
+
credential can be picked up without restarting Desktop. Removing a configured name
|
|
179
|
+
drops its recovered value immediately. A missing value remains absent: Desktop never
|
|
180
|
+
substitutes a dummy credential or treats a blocked package-manager command as passed.
|
|
181
|
+
|
|
137
182
|
A few env vars are read directly by the app. The two most useful operational ones:
|
|
138
183
|
|
|
139
184
|
| Variable | Description |
|
|
@@ -68,6 +68,25 @@ all workspace refreshes and the final recovery-state write succeed.
|
|
|
68
68
|
Core 6 registry updates and same-version runtime repairs are offered using the
|
|
69
69
|
same supported-major policy as runtime selection.
|
|
70
70
|
|
|
71
|
+
## Role definitions follow the engine
|
|
72
|
+
|
|
73
|
+
Core owns how the implement roles think: `rolePromptDefaults()` returns the
|
|
74
|
+
factory architect, developer, reviewer and fixer definitions (one shared senior
|
|
75
|
+
engineer identity, blast-radius discipline, quality bar and the mandatory
|
|
76
|
+
OpenSpec binding). Desktop owns the workflows that use those roles, and never
|
|
77
|
+
keeps a copy of those definitions.
|
|
78
|
+
|
|
79
|
+
- The Implement rail (engine 1) reads the defaults from the selected Core at
|
|
80
|
+
launch, merged with the user's global overrides from Settings.
|
|
81
|
+
- Loop recipes store `inherit` for the built-in definitions (and
|
|
82
|
+
`inherit:<role>` for declared recipe roles such as `plan`/`build`). The
|
|
83
|
+
launch bridge resolves them to the same Core defaults plus global overrides
|
|
84
|
+
and freezes the result with the run, so every loop follows the installed
|
|
85
|
+
Core automatically. Text the user edits in the loop editor is stored verbatim
|
|
86
|
+
and stays frozen with the loop; "Use the Core definition" returns a role to
|
|
87
|
+
`inherit`. `/api/loop-agent-defaults` returns `rolePromptDefaults` so the
|
|
88
|
+
editor can preview what `inherit` resolves to today.
|
|
89
|
+
|
|
71
90
|
## Version reporting
|
|
72
91
|
|
|
73
92
|
Global status distinguishes the selected runtime and its source, the active
|
|
@@ -147,6 +147,33 @@ One chip per graph node, states `pending → running → ok / failed / interrupt
|
|
|
147
147
|
- **Filtering** searches every section: while the filter is non-empty, all sections are treated as expanded; clearing it restores the previous mode.
|
|
148
148
|
- **Copy**: each step header carries its own copy button (per-step lines only; the control lives outside the toggle so the header stays a valid button), and the toolbar copy grabs the whole log with `── Step N · title ──` separators.
|
|
149
149
|
|
|
150
|
+
### Verbose verification and retained display
|
|
151
|
+
|
|
152
|
+
Desktop compacts verification output before publishing readable log lines. Passing
|
|
153
|
+
cases and repeated console stacks no longer flood the rail; bounded diagnostic
|
|
154
|
+
context, final test totals and recorded command outcomes remain visible. Omission
|
|
155
|
+
notices distinguish a concise display from the original runtime evidence. This
|
|
156
|
+
changes presentation only, including when tests print warnings or errors but exit
|
|
157
|
+
successfully; recorded exit codes and workflow completion remain authoritative.
|
|
158
|
+
|
|
159
|
+
Both job detail surfaces omit invisible raw `verification-output` events from
|
|
160
|
+
their in-memory display buffer and preserve lifecycle markers when bounding
|
|
161
|
+
ordinary output. A translated notice indicates when earlier readable output was
|
|
162
|
+
removed and that Copy contains only the retained view. Initial history and live
|
|
163
|
+
updates use the same retention rules. Persisted events are unchanged.
|
|
164
|
+
|
|
165
|
+
For detailed verification output, expand **Details → Verification evidence** and
|
|
166
|
+
open a check's stdout or stderr, using the next-page control as needed. These are
|
|
167
|
+
the retained Core artifacts: currently up to 1 MiB per stream, with truncation
|
|
168
|
+
reported explicitly. They follow the configured runtime retention policy and are
|
|
169
|
+
not an unlimited archive. Compaction does not rewrite historical logs in storage.
|
|
170
|
+
|
|
171
|
+
Readable runtime lines preserve an optional recorded `attemptId` in both stored
|
|
172
|
+
log payloads and live WebSocket frames. The existing attempt map uses it to attach
|
|
173
|
+
buffered verification summaries to their originating step, including parallel
|
|
174
|
+
attempts. Older lines without that identity retain their existing arrival-order
|
|
175
|
+
fallback; Desktop does not invent an attempt ID.
|
|
176
|
+
|
|
150
177
|
## Performance
|
|
151
178
|
|
|
152
179
|
- **Collapsed sections render nothing** — the body subtree is not mounted, so a 10k-line settled step costs one header row.
|
|
@@ -48,6 +48,13 @@ with allocation rollback and the original failed checkout retained. A synchronou
|
|
|
48
48
|
click guard prevents duplicate submissions; accepted relaunches stay disabled
|
|
49
49
|
until the new card appears, and rejected admission details remain inline.
|
|
50
50
|
|
|
51
|
+
Workspace narrowing is optional: when a delivery manifest has no explicit
|
|
52
|
+
workspace selections, Relaunch omits `workspaceSelection` and uses registered
|
|
53
|
+
defaults for its recorded repositories. Partial selections remain partial and
|
|
54
|
+
saved explicit selections take precedence; invalid selections still fail normal
|
|
55
|
+
admission. Removing the last repository with a workspace override from a launch
|
|
56
|
+
card also omits the property, instead of sending an invalid empty map.
|
|
57
|
+
|
|
51
58
|
```rail-launch
|
|
52
59
|
{
|
|
53
60
|
"version": 1,
|
|
@@ -21,20 +21,22 @@ Dashboard/Jobs/Analytics/Settings).
|
|
|
21
21
|
- **Profiles** tab — create and edit profiles.
|
|
22
22
|
- **Usage** tab — see which profiles are actually being used (backed by
|
|
23
23
|
the `/analytics` endpoint covered in section 8).
|
|
24
|
-
- **Catalog** tab — read the
|
|
25
|
-
`
|
|
24
|
+
- **Catalog** tab — read the runtime-defined baseline roles (`sr-architect`,
|
|
25
|
+
`sr-developer`, `sr-reviewer`, shown read-only with the definition the Core
|
|
26
|
+
runtime executes) or author custom `custom-*` ones via the Studio.
|
|
26
27
|
|
|
27
28
|
When the project has no profiles yet, the empty state offers two entry
|
|
28
29
|
points:
|
|
29
30
|
|
|
30
|
-
- **Migrate from current agents** —
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
- **Migrate from current agents** — seeds the baseline trio `sr-architect`,
|
|
32
|
+
`sr-developer` and `sr-reviewer` as role identifiers with the provider's
|
|
33
|
+
default model, reads the primary provider's native role catalog
|
|
34
|
+
(`.claude/agents/` or `.kimi-code/skills/`) for custom roles and creates a
|
|
35
|
+
`default` profile mirroring today's behavior. No `sr-*` file has to exist:
|
|
36
|
+
a `model:` is read only from role files an older install still carries.
|
|
37
|
+
Provider-specific model identifiers are retained only when they are in that
|
|
38
|
+
provider's catalog; otherwise migration uses its default and stamps the
|
|
39
|
+
provider into the saved profile.
|
|
38
40
|
- **Blank profile** — start from scratch.
|
|
39
41
|
|
|
40
42
|
## 2. Saved profiles vs selection
|
|
@@ -128,6 +130,11 @@ template/duplicate/edit and rail execution remain available.
|
|
|
128
130
|
`sr-developer`. Fix the listed issues and Save re-enables.
|
|
129
131
|
- **"agent 'xyz' already exists" (409)** — the name collides with an
|
|
130
132
|
existing provider role. Pick a different name.
|
|
133
|
+
- **No `.claude/agents/sr-*.md` in the project** — expected. The baseline
|
|
134
|
+
roles are defined by the Core runtime, not installed as files; read them in
|
|
135
|
+
the Catalog tab and edit their definitions, provider and model in
|
|
136
|
+
**Settings → Agent runtime**. Files left behind by an older Core are
|
|
137
|
+
tolerated until Core prunes them.
|
|
131
138
|
- **The whole Agents section is missing** — it can be disabled server-side
|
|
132
139
|
with `SPECRAILS_AGENTS_SECTION=false`, which 404s the entire
|
|
133
140
|
`/profiles` router. Unset it (or leave it at its default) to restore the
|
|
@@ -140,7 +140,7 @@ one-active-generation-per-rail all come free from the shipped machinery.
|
|
|
140
140
|
|
|
141
141
|
**The loop.** `factory:revision` is the fourth factory loop: `{{cmd:revise}}` (a
|
|
142
142
|
distilled step that applies the one requested change on top of the branch and
|
|
143
|
-
then runs `sr-reviewer` over the resulting diff, so packet v2 keeps its reviewer
|
|
143
|
+
then runs the reviewer role (`sr-reviewer`) over the resulting diff, so packet v2 keeps its reviewer
|
|
144
144
|
tier) followed by the standard `{{cmd:verify}}`. No Architect step — the plan and
|
|
145
145
|
the code already exist. It is resolvable by id but deliberately NOT listed in the
|
|
146
146
|
public catalog: its prompt consumes `{{const:REVISION_REQUEST}}`, which only a
|
|
@@ -124,6 +124,15 @@ Both temporary checkouts use non-force cleanup, including after a Git runner exc
|
|
|
124
124
|
recording the failed replacement id so every client can safely undo its tombstone for that pair.
|
|
125
125
|
- Isolation units remain `per-ticket` or one `all`-scope batch unit. Initial/final SHAs plus commit
|
|
126
126
|
verification distinguish changed, resumed, and no-change results.
|
|
127
|
+
- Warm dependency preparation preserves a registered directory's position inside its Git checkout.
|
|
128
|
+
For example, registering `apps/catalog` prepares its installed packages and Yarn state beneath
|
|
129
|
+
`worktree/apps/catalog/node_modules`. Discovery stays relative to the registered directory
|
|
130
|
+
(depth at most two); unrelated root and sibling installations are not selected. Package entries
|
|
131
|
+
link to the source installation, while tool caches remain local. Existing real installations and
|
|
132
|
+
foreign links are preserved. Live cleanup uses the same projection and exact target fingerprints,
|
|
133
|
+
including proofs for historical misplaced links; symlinked destination ancestors confer no
|
|
134
|
+
authority. Registration paths, frozen manifests and Core scope remain unchanged. A failed run
|
|
135
|
+
keeps its old checkout; after updating Desktop, a fresh Relaunch receives the corrected layout.
|
|
127
136
|
- Per-unit settlement returns structured execution + delivery results. `onLoopRunFinished` receives
|
|
128
137
|
the engine outcome only; commit/status/ref/provenance/push failures cannot rewrite it.
|
|
129
138
|
- Before staging, settlement blocks active OpenSpec change directories introduced relative to
|
|
@@ -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 1018 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
|
|
@@ -79,7 +79,7 @@ match; integration suites may live elsewhere. Use `rg` to find other consumers.
|
|
|
79
79
|
|
|
80
80
|
- [AgentRoleFields.tsx](../../client/src/features/agents/components/AgentRoleFields.tsx) · [test](../../client/src/features/agents/components/__tests__/AgentRoleFields.test.tsx)
|
|
81
81
|
- [AgentStudio.tsx](../../client/src/features/agents/components/AgentStudio.tsx)
|
|
82
|
-
- [AgentsCatalogTab.tsx](../../client/src/features/agents/components/AgentsCatalogTab.tsx)
|
|
82
|
+
- [AgentsCatalogTab.tsx](../../client/src/features/agents/components/AgentsCatalogTab.tsx) · [test](../../client/src/features/agents/components/__tests__/AgentsCatalogTab.test.tsx)
|
|
83
83
|
- [AiRefineOverlay.tsx](../../client/src/features/agents/components/AiRefineOverlay.tsx)
|
|
84
84
|
- [ProfileAnalyticsCard.tsx](../../client/src/features/agents/components/ProfileAnalyticsCard.tsx)
|
|
85
85
|
- [ProfileEditor.tsx](../../client/src/features/agents/components/ProfileEditor.tsx) · [test](../../client/src/features/agents/components/__tests__/ProfileEditor.test.tsx)
|
|
@@ -317,6 +317,7 @@ match; integration suites may live elsewhere. Use `rg` to find other consumers.
|
|
|
317
317
|
- [DiscardSpecDialog.tsx](../../client/src/features/integrations/components/jira/DiscardSpecDialog.tsx) · [test](../../client/src/features/integrations/components/jira/__tests__/DiscardSpecDialog.test.tsx)
|
|
318
318
|
- [JiraConnectWizard.tsx](../../client/src/features/integrations/components/jira/JiraConnectWizard.tsx) · [test](../../client/src/features/integrations/components/jira/__tests__/JiraConnectWizard.test.tsx)
|
|
319
319
|
- [JiraConnectedCard.tsx](../../client/src/features/integrations/components/jira/JiraConnectedCard.tsx)
|
|
320
|
+
- [JiraOutboxPanel.tsx](../../client/src/features/integrations/components/jira/JiraOutboxPanel.tsx) · [test](../../client/src/features/integrations/components/jira/__tests__/JiraOutboxPanel.test.tsx)
|
|
320
321
|
- [JiraSpecDetailsPanel.tsx](../../client/src/features/integrations/components/jira/JiraSpecDetailsPanel.tsx) · [test](../../client/src/features/integrations/components/jira/__tests__/JiraSpecDetailsPanel.test.tsx)
|
|
321
322
|
|
|
322
323
|
## client/src/features/integrations/context
|
|
@@ -365,6 +366,7 @@ match; integration suites may live elsewhere. Use `rg` to find other consumers.
|
|
|
365
366
|
## client/src/features/jobs/lib
|
|
366
367
|
|
|
367
368
|
- [cancel-job.ts](../../client/src/features/jobs/lib/cancel-job.ts) · [test](../../client/src/features/jobs/lib/__tests__/cancel-job.test.ts)
|
|
369
|
+
- [job-event-buffer.ts](../../client/src/features/jobs/lib/job-event-buffer.ts) · [test](../../client/src/features/jobs/lib/__tests__/job-event-buffer.test.ts)
|
|
368
370
|
- [job-log-mode.ts](../../client/src/features/jobs/lib/job-log-mode.ts)
|
|
369
371
|
- [job-time.ts](../../client/src/features/jobs/lib/job-time.ts)
|
|
370
372
|
|
|
@@ -1093,6 +1095,7 @@ match; integration suites may live elsewhere. Use `rg` to find other consumers.
|
|
|
1093
1095
|
- [agent-runtime-settings-router.ts](../../server/modules/agent-runtime/runtime/agent-runtime-settings-router.ts)
|
|
1094
1096
|
- [agent-runtime-settings.ts](../../server/modules/agent-runtime/runtime/agent-runtime-settings.ts) · [test](../../server/modules/agent-runtime/runtime/agent-runtime-settings.test.ts)
|
|
1095
1097
|
- [agent-runtime-settlement.ts](../../server/modules/agent-runtime/runtime/agent-runtime-settlement.ts) · [test](../../server/modules/agent-runtime/runtime/agent-runtime-settlement.test.ts)
|
|
1098
|
+
- [agent-runtime-verification-log.ts](../../server/modules/agent-runtime/runtime/agent-runtime-verification-log.ts)
|
|
1096
1099
|
- [agent-runtime-verification-suggestions.ts](../../server/modules/agent-runtime/runtime/agent-runtime-verification-suggestions.ts) · [test](../../server/modules/agent-runtime/runtime/agent-runtime-verification-suggestions.test.ts)
|
|
1097
1100
|
|
|
1098
1101
|
## server/modules/agents/runtime
|
|
@@ -1424,6 +1427,7 @@ match; integration suites may live elsewhere. Use `rg` to find other consumers.
|
|
|
1424
1427
|
|
|
1425
1428
|
## server/util
|
|
1426
1429
|
|
|
1430
|
+
- [checkout-path.ts](../../server/util/checkout-path.ts)
|
|
1427
1431
|
- [cli-prompt.ts](../../server/util/cli-prompt.ts) · [test](../../server/util/cli-prompt.test.ts)
|
|
1428
1432
|
- [distribute-int.ts](../../server/util/distribute-int.ts)
|
|
1429
1433
|
- [secure-fs.ts](../../server/util/secure-fs.ts) · [test](../../server/util/secure-fs.test.ts)
|
|
@@ -190,16 +190,18 @@ Por qué token‑paste y no OAuth en v1:
|
|
|
190
190
|
|
|
191
191
|
## 6. Ciclo de vida y mapeo de estados (la parte difícil)
|
|
192
192
|
|
|
193
|
-
Los issues de Jira **no tienen un campo `status` asignable** — hay que **transicionar**, y las transiciones están **gateadas por el workflow** del cliente (que es arbitrario). Las
|
|
193
|
+
Los issues de Jira **no tienen un campo `status` asignable** — hay que **transicionar**, y las transiciones están **gateadas por el workflow** del cliente (que es arbitrario). Las 5 lógicas de Specrails (`todo/in_progress/on_review/done/cancelled`) deben caer sobre N estados de cliente repartidos en solo **3 categorías estables**: `new` / `indeterminate` / `done` (`statusCategory.key`).
|
|
194
194
|
|
|
195
195
|
### Resolver de dos niveles
|
|
196
196
|
|
|
197
197
|
1. **Mapa explícito por proyecto gana siempre.** En Settings el usuario elige, de la **lista real de estados** de su proyecto (fetched en vivo), el target para cada estado lógico. Esto resuelve la ambigüedad (p.ej. dos estados sobre categoría `done`: `Released` vs `Won't Do`).
|
|
198
198
|
2. **Fallback por categoría** cuando no hay mapa: anclar en `statusCategory.key` (nunca en el **nombre** localizable del estado). Para `cancelled`, preferir un *cancel‑lexicon* (`won't do`, `cancelled`, `rejected`, `abandoned`, `invalid`, `duplicate`) y fijar `resolution`; para `done`/éxito, preferir un *ship‑lexicon* (`done`, `closed`, `released`, `resolved`, `complete`) y **alejarse** del cancel‑lexicon.
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
El mapa también se respeta al materializar Jira en la caché local para los cinco estados lógicos. Si varios estados comparten destino, `on_review` mantiene su prioridad histórica; para otras ambigüedades se conserva la interpretación por categoría.
|
|
201
201
|
|
|
202
|
-
|
|
202
|
+
### Camino de transición — recorrido acotado por categorías
|
|
203
|
+
|
|
204
|
+
Como solo ves las transiciones salientes del estado **actual**, un workflow `Backlog → Selected for Dev → In Progress → Done` no ofrece arista directa a `Done`. Algoritmo: `GET /transitions` del issue vivo → aplicar la arista que reduce la distancia a la categoría objetivo (orden `new < indeterminate < done`) → re‑`GET` → repetir. Cap ~5 saltos, dedup de estados visitados (evitar bucles), parar si ninguna transición reduce distancia. **Idempotency‑first**: si hay destino explícito, comparar primero su ID/nombre con el estado actual, incluso si pertenece a otra categoría. Sin destino explícito, la categoría objetivo permite el **no‑op**. Un estado intermedio de la categoría esperada no completa un destino explícito diferente. Este recorrido no explora estados laterales modificando Jira a ciegas. Si no hay camino en N saltos → **dead‑letter** no‑fatal ("mover estado manualmente en Jira") y **jamás** se hace error del rail.
|
|
203
205
|
|
|
204
206
|
### Pantallas de transición / campos requeridos
|
|
205
207
|
|
|
@@ -238,10 +240,11 @@ Diseño de poll:
|
|
|
238
240
|
### Outbound — outbox durable
|
|
239
241
|
|
|
240
242
|
- Drenaje en worker de fondo: **FIFO por‑issue** (una transición debe aterrizar antes que el comentario que la describe), **paralelo entre issues distintos**, con cap de concurrencia.
|
|
241
|
-
- **Idempotencia:** transiciones por
|
|
243
|
+
- **Idempotencia:** transiciones por identidad explícita ID/nombre y, sin mapa, por categoría; **comentarios** con un *self‑marker* invisible embebido en el body ADF (`[specrails:job-<id>]`) — Jira no tiene idempotencia nativa de comentarios, así que antes de re‑postear se hace `GET .../comment` y se salta si el marker ya existe. El marker dobla como filtro de auto‑eco en el poll.
|
|
242
244
|
- **Rate limits:** honrar `Retry-After` en 429 exacto; si ausente, backoff exponencial con jitter (base 2s, cap 30s, ~4 reintentos); respetar el techo ~20 writes/2s por issue. Token‑bucket por debajo de los burst caps.
|
|
243
245
|
- **Clasificación de errores:** `401` = credencial → **pausar** outbox del proyecto + banner re‑auth (no reintentar en bucle); `403` = permiso de operación concreta → dead‑letter nombrando la operación ("tu cuenta no puede transicionar en PROJ"), sin inferir fallo global; `404` sobre issue conocido = terminal (issue borrado/movido) → marcar link `orphaned`, parar la op; solo `429/5xx/timeout` son reintentables.
|
|
244
246
|
- **Dead‑letter visible** con reintento manual: `GET /jira/outbox`, `POST /jira/outbox/:id/retry`, indicador `JiraSyncIndicator` en UI. Un workflow‑gap o un 403 **nunca** es un drop silencioso.
|
|
247
|
+
- **Operaciones reemplazadas:** las transiciones fallidas con una intención posterior para el mismo issue se conservan como `superseded`, fuera del contador de atención y sin reintento. Un reintento antiguo ya encolado tampoco se aplica si una transición posterior ya terminó. Se conservan el FIFO normal y los comentarios/ediciones independientes. El panel identifica el issue y el destino, y muestra errores al cargar los estados con una acción de reintento.
|
|
245
248
|
|
|
246
249
|
---
|
|
247
250
|
|
|
@@ -173,9 +173,9 @@ Without profiles, every rail uses the project's frontmatter-baked models. With p
|
|
|
173
173
|
Open **Agents** in the project's right sidebar. Two sub-tabs:
|
|
174
174
|
|
|
175
175
|
- **Profiles** — full CRUD over `.specrails/profiles/*.json`. The live validator enforces the baseline trio and routing ordering — Save is disabled with an "N issues to resolve" hint while the profile is broken.
|
|
176
|
-
- **Agents Catalog** — read-only viewer of
|
|
176
|
+
- **Agents Catalog** — read-only viewer of the runtime-defined baseline roles (`sr-architect`, `sr-developer`, `sr-reviewer`, shown with the definition the Core runtime actually executes) and your `custom-*` agents.
|
|
177
177
|
|
|
178
|
-
The empty state offers **Migrate from current agents**: one click creates a `default` profile
|
|
178
|
+
The empty state offers **Migrate from current agents**: one click creates a `default` profile from the baseline roles and your existing custom agents — no role file has to exist for it to work.
|
|
179
179
|
|
|
180
180
|
Each profile gets a per-profile analytics card showing usage for the last 7 / 30 / 90 days: jobs, success rate, avg tokens, avg duration.
|
|
181
181
|
|
package/package.json
CHANGED
|
@@ -19,8 +19,8 @@ const bundled_core_1 = require("./bundled-core");
|
|
|
19
19
|
const core_runtime_1 = require("./core-runtime");
|
|
20
20
|
// Windows has no `which`; probe PATH via `where` instead.
|
|
21
21
|
const WHICH_CMD = process.platform === 'win32' ? 'where' : 'which';
|
|
22
|
-
/** Paired development target; older supported contracts remain compatible. */
|
|
23
|
-
exports.EXPECTED_CORE_CONTRACT_SCHEMA_VERSION = '5.
|
|
22
|
+
/** Paired development target; older supported contracts (5.1) remain compatible. */
|
|
23
|
+
exports.EXPECTED_CORE_CONTRACT_SCHEMA_VERSION = '5.2';
|
|
24
24
|
// These must mirror KNOWN_VERBS in cli/specrails-desktop.ts
|
|
25
25
|
const DESKTOP_KNOWN_COMMANDS = new Set([
|
|
26
26
|
'implement',
|
|
@@ -29,6 +29,10 @@ const DESKTOP_KNOWN_COMMANDS = new Set([
|
|
|
29
29
|
// Commands older Cores still ship that Desktop no longer invokes. They are
|
|
30
30
|
// tolerated (not drift) but never required: Batch was folded into implement.
|
|
31
31
|
const DESKTOP_TOLERATED_LEGACY_COMMANDS = new Set(['batch-implement']);
|
|
32
|
+
// Checkpoints that contract 5.1 still declares but Desktop retired: role agents
|
|
33
|
+
// are runtime-defined since contract 5.2, so `agent_generation` is tolerated
|
|
34
|
+
// (not drift) and never required.
|
|
35
|
+
const DESKTOP_TOLERATED_LEGACY_CHECKPOINTS = new Set(['agent_generation']);
|
|
32
36
|
function isRecord(value) {
|
|
33
37
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
34
38
|
}
|
|
@@ -233,7 +237,7 @@ async function checkCoreCompat() {
|
|
|
233
237
|
: (contract.checkpoints && typeof contract.checkpoints === 'object'
|
|
234
238
|
? Object.keys(contract.checkpoints)
|
|
235
239
|
: []);
|
|
236
|
-
const missingCheckpoints = contractCheckpoints.filter((c) => !desktopCheckpointKeys.includes(c));
|
|
240
|
+
const missingCheckpoints = contractCheckpoints.filter((c) => !desktopCheckpointKeys.includes(c) && !DESKTOP_TOLERATED_LEGACY_CHECKPOINTS.has(c));
|
|
237
241
|
const deterministic = contract.lifecycle?.mode === 'deterministic' && contract.lifecycle.requiresEnrich === false;
|
|
238
242
|
// Supporting legacy installation checkpoints is compatible with Core 5;
|
|
239
243
|
// demanding them from a deterministic installer would recreate enrich.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.checkoutSubdirectory =
|
|
3
|
+
exports.checkoutSubdirectory = void 0;
|
|
4
4
|
exports.prepareCoreExecution = prepareCoreExecution;
|
|
5
5
|
exports.coreVerificationContext = coreVerificationContext;
|
|
6
6
|
exports.checkCoreCompletion = checkCoreCompletion;
|
|
@@ -10,6 +10,9 @@ const node_path_1 = require("node:path");
|
|
|
10
10
|
const core_node_runtime_1 = require("./core-node-runtime");
|
|
11
11
|
const core_update_state_1 = require("./core-update-state");
|
|
12
12
|
const agent_runtime_loader_1 = require("./modules/agent-runtime/runtime/agent-runtime-loader");
|
|
13
|
+
const checkout_path_1 = require("./util/checkout-path");
|
|
14
|
+
var checkout_path_2 = require("./util/checkout-path");
|
|
15
|
+
Object.defineProperty(exports, "checkoutSubdirectory", { enumerable: true, get: function () { return checkout_path_2.checkoutSubdirectory; } });
|
|
13
16
|
const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
14
17
|
// Resolve existing ancestors too: the tickets file may not exist yet, and macOS
|
|
15
18
|
// exposes temporary directories through both /var and /private/var.
|
|
@@ -26,22 +29,6 @@ function canonicalPath(path) {
|
|
|
26
29
|
return (0, node_path_1.join)(canonicalPath(parent), (0, node_path_1.basename)(path));
|
|
27
30
|
}
|
|
28
31
|
}
|
|
29
|
-
/** Where a directory sits inside its git checkout: '' for the checkout root or a directory outside git. */
|
|
30
|
-
function checkoutSubdirectory(directory) {
|
|
31
|
-
let start;
|
|
32
|
-
try {
|
|
33
|
-
start = (0, node_fs_1.realpathSync)(directory);
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
return '';
|
|
37
|
-
}
|
|
38
|
-
for (let current = start;; current = (0, node_path_1.dirname)(current)) {
|
|
39
|
-
if ((0, node_fs_1.existsSync)((0, node_path_1.join)(current, '.git')))
|
|
40
|
-
return (0, node_path_1.relative)(current, start).split(node_path_1.sep).join('/');
|
|
41
|
-
if ((0, node_path_1.dirname)(current) === current)
|
|
42
|
-
return '';
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
32
|
/**
|
|
46
33
|
* A registered repository may be one package of a larger git checkout (a
|
|
47
34
|
* monorepo app). An isolated worktree always mounts the whole checkout, so the
|
|
@@ -54,7 +41,7 @@ function registeredScope(checkout, sourcePath) {
|
|
|
54
41
|
(0, node_fs_1.realpathSync)(sourcePath);
|
|
55
42
|
const local = (0, node_path_1.relative)(checkout, sourcePath).split(node_path_1.sep).join('/');
|
|
56
43
|
const contained = local && local !== '..' && !local.startsWith('../') && !(0, node_path_1.isAbsolute)(local);
|
|
57
|
-
const prefix = checkoutSubdirectory(checkout) ? (contained ? local : '') : checkoutSubdirectory(sourcePath) || (contained ? local : '');
|
|
44
|
+
const prefix = (0, checkout_path_1.checkoutSubdirectory)(checkout) ? (contained ? local : '') : (0, checkout_path_1.checkoutSubdirectory)(sourcePath) || (contained ? local : '');
|
|
58
45
|
if (!prefix)
|
|
59
46
|
return undefined;
|
|
60
47
|
if (!(0, node_fs_1.statSync)((0, node_path_1.join)(checkout, ...prefix.split('/'))).isDirectory())
|
|
@@ -33,6 +33,7 @@ exports.markOutboxDone = markOutboxDone;
|
|
|
33
33
|
exports.markOutboxRetry = markOutboxRetry;
|
|
34
34
|
exports.markOutboxDead = markOutboxDead;
|
|
35
35
|
exports.resetInflight = resetInflight;
|
|
36
|
+
exports.reconcileSupersededTransitions = reconcileSupersededTransitions;
|
|
36
37
|
exports.retryDeadOutbox = retryDeadOutbox;
|
|
37
38
|
exports.listOutbox = listOutbox;
|
|
38
39
|
exports.countOutboxByState = countOutboxByState;
|
|
@@ -328,12 +329,38 @@ function resetInflight(db) {
|
|
|
328
329
|
.run(new Date().toISOString());
|
|
329
330
|
return info.changes;
|
|
330
331
|
}
|
|
331
|
-
/**
|
|
332
|
+
/**
|
|
333
|
+
* Preserve obsolete failed status intentions as terminal history rather than
|
|
334
|
+
* offering a retry that could revert a newer intention. Ordinary pending and
|
|
335
|
+
* inflight operations retain FIFO ownership. A pending row with a newer DONE
|
|
336
|
+
* transition is an old retry (normal FIFO cannot produce that ordering).
|
|
337
|
+
* Comments and field edits are independent operations and never superseded.
|
|
338
|
+
*/
|
|
339
|
+
function reconcileSupersededTransitions(db) {
|
|
340
|
+
return db.prepare(`UPDATE jira_outbox AS candidate
|
|
341
|
+
SET state = 'superseded', next_attempt_at = NULL, updated_at = ?
|
|
342
|
+
WHERE candidate.op_type = 'transition'
|
|
343
|
+
AND candidate.state IN ('dead', 'pending')
|
|
344
|
+
AND EXISTS (
|
|
345
|
+
SELECT 1 FROM jira_outbox AS newer
|
|
346
|
+
WHERE newer.jira_issue_id = candidate.jira_issue_id
|
|
347
|
+
AND newer.op_type = 'transition'
|
|
348
|
+
AND newer.id > candidate.id
|
|
349
|
+
AND (candidate.state = 'dead' OR newer.state = 'done')
|
|
350
|
+
)`).run(new Date().toISOString()).changes;
|
|
351
|
+
}
|
|
352
|
+
/** Reconcile and re-queue atomically, so an obsolete intention is never revived. */
|
|
332
353
|
function retryDeadOutbox(db, id) {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
.
|
|
336
|
-
|
|
354
|
+
return db.transaction(() => {
|
|
355
|
+
reconcileSupersededTransitions(db);
|
|
356
|
+
const row = db.prepare('SELECT state FROM jira_outbox WHERE id = ?').get(id);
|
|
357
|
+
if (row?.state === 'superseded')
|
|
358
|
+
return 'superseded';
|
|
359
|
+
if (row?.state !== 'dead')
|
|
360
|
+
return null;
|
|
361
|
+
db.prepare("UPDATE jira_outbox SET state = 'pending', next_attempt_at = NULL, dead_reason = NULL, last_error = NULL, updated_at = ? WHERE id = ? AND state = 'dead'").run(new Date().toISOString(), id);
|
|
362
|
+
return 'pending';
|
|
363
|
+
})();
|
|
337
364
|
}
|
|
338
365
|
function listOutbox(db, opts = {}) {
|
|
339
366
|
const limit = Math.min(opts.limit ?? 200, 1000);
|
|
@@ -344,7 +371,7 @@ function listOutbox(db, opts = {}) {
|
|
|
344
371
|
}
|
|
345
372
|
function countOutboxByState(db) {
|
|
346
373
|
const rows = db.prepare('SELECT state, COUNT(*) AS n FROM jira_outbox GROUP BY state').all();
|
|
347
|
-
const out = { pending: 0, inflight: 0, done: 0, dead: 0 };
|
|
374
|
+
const out = { pending: 0, inflight: 0, done: 0, dead: 0, superseded: 0 };
|
|
348
375
|
for (const r of rows) {
|
|
349
376
|
if (r.state in out)
|
|
350
377
|
out[r.state] = r.n;
|
|
@@ -37,19 +37,21 @@ function issueStatusCategory(issue) {
|
|
|
37
37
|
return k === 'new' || k === 'done' ? k : 'indeterminate';
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* category fallback
|
|
44
|
-
* a ticket parked at on_review once its outbox transition drains (the frozen
|
|
45
|
-
* guard only protects the pending window). Unconfigured ⇒ byte-identical
|
|
46
|
-
* category mapping.
|
|
40
|
+
* Explicit status identities apply in both sync directions, even when a
|
|
41
|
+
* customer's category differs from the logical state's default. Preserve the
|
|
42
|
+
* historical on_review precedence for shared mappings; other ambiguous targets
|
|
43
|
+
* retain category fallback rather than choosing by configuration object order.
|
|
47
44
|
*/
|
|
48
45
|
function mapStatus(issue, statusMap) {
|
|
49
46
|
const name = (issue.fields.status?.name ?? '').toLowerCase();
|
|
50
|
-
const
|
|
51
|
-
|
|
47
|
+
const matches = (target) => Boolean(target
|
|
48
|
+
&& (issue.fields.status?.id === target || (name && name === target.toLowerCase())));
|
|
49
|
+
if (matches(statusMap?.on_review))
|
|
52
50
|
return 'on_review';
|
|
51
|
+
const states = ['todo', 'in_progress', 'done', 'cancelled'];
|
|
52
|
+
const mapped = states.filter((state) => matches(statusMap?.[state]));
|
|
53
|
+
if (mapped.length === 1)
|
|
54
|
+
return mapped[0];
|
|
53
55
|
const cat = issueStatusCategory(issue);
|
|
54
56
|
if (cat === 'new')
|
|
55
57
|
return 'todo';
|