@exaudeus/workrail 3.39.0 → 3.41.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/dist/cli/commands/init.js +0 -3
- package/dist/cli-worktrain.js +58 -26
- package/dist/cli.js +0 -18
- package/dist/config/app-config.d.ts +0 -16
- package/dist/config/app-config.js +0 -14
- package/dist/config/config-file.js +0 -3
- package/dist/console-ui/assets/index-CQt4UhPB.js +28 -0
- package/dist/console-ui/assets/index-DGj8EsFR.css +1 -0
- package/dist/console-ui/index.html +2 -2
- package/dist/coordinators/pr-review.d.ts +23 -1
- package/dist/coordinators/pr-review.js +224 -5
- package/dist/daemon/daemon-events.d.ts +9 -1
- package/dist/daemon/soul-template.d.ts +2 -2
- package/dist/daemon/soul-template.js +11 -1
- package/dist/daemon/workflow-runner.d.ts +17 -3
- package/dist/daemon/workflow-runner.js +401 -28
- package/dist/di/container.js +1 -25
- package/dist/di/tokens.d.ts +0 -3
- package/dist/di/tokens.js +0 -3
- package/dist/engine/engine-factory.js +0 -1
- package/dist/infrastructure/console-defaults.d.ts +1 -0
- package/dist/infrastructure/console-defaults.js +4 -0
- package/dist/infrastructure/session/index.d.ts +0 -1
- package/dist/infrastructure/session/index.js +1 -3
- package/dist/manifest.json +124 -124
- package/dist/mcp/handlers/session.d.ts +1 -0
- package/dist/mcp/handlers/session.js +61 -13
- package/dist/mcp/output-schemas.d.ts +10 -10
- package/dist/mcp/server.js +1 -18
- package/dist/mcp/tools.d.ts +12 -12
- package/dist/mcp/transports/http-entry.js +0 -2
- package/dist/mcp/transports/stdio-entry.js +1 -2
- package/dist/mcp/types.d.ts +0 -2
- package/dist/trigger/daemon-console.d.ts +2 -0
- package/dist/trigger/daemon-console.js +1 -1
- package/dist/trigger/trigger-listener.d.ts +2 -0
- package/dist/trigger/trigger-listener.js +3 -1
- package/dist/trigger/trigger-router.d.ts +4 -3
- package/dist/trigger/trigger-router.js +13 -5
- package/dist/trigger/trigger-store.js +17 -4
- package/dist/types/workflow-source.d.ts +0 -1
- package/dist/types/workflow-source.js +3 -6
- package/dist/types/workflow.d.ts +1 -1
- package/dist/types/workflow.js +1 -2
- package/dist/v2/durable-core/domain/artifact-contract-validator.js +66 -0
- package/dist/v2/durable-core/schemas/artifacts/coordinator-signal.d.ts +25 -0
- package/dist/v2/durable-core/schemas/artifacts/coordinator-signal.js +31 -0
- package/dist/v2/durable-core/schemas/artifacts/index.d.ts +3 -1
- package/dist/v2/durable-core/schemas/artifacts/index.js +14 -1
- package/dist/v2/durable-core/schemas/artifacts/review-verdict.d.ts +41 -0
- package/dist/v2/durable-core/schemas/artifacts/review-verdict.js +30 -0
- package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +236 -236
- package/dist/v2/durable-core/schemas/session/events.d.ts +50 -50
- package/dist/v2/durable-core/schemas/session/gaps.d.ts +2 -2
- package/dist/v2/durable-core/schemas/session/manifest.d.ts +4 -4
- package/dist/v2/durable-core/schemas/session/outputs.d.ts +8 -8
- package/dist/v2/usecases/console-routes.d.ts +2 -1
- package/dist/v2/usecases/console-routes.js +207 -5
- package/dist/v2/usecases/console-service.js +14 -0
- package/dist/v2/usecases/console-types.d.ts +1 -0
- package/docs/authoring.md +16 -16
- package/docs/design/coordinator-artifact-protocol-design-candidates.md +155 -0
- package/docs/design/coordinator-artifact-protocol-design-review.md +103 -0
- package/docs/design/coordinator-artifact-protocol-implementation-plan.md +259 -0
- package/docs/design/coordinator-message-queue-drain-plan.md +241 -0
- package/docs/design/coordinator-message-queue-drain-review.md +120 -0
- package/docs/design/coordinator-message-queue-drain.md +289 -0
- package/docs/design/shaping-workflow-external-research.md +119 -0
- package/docs/discovery/late-bound-goals-impl-plan.md +147 -0
- package/docs/discovery/late-bound-goals-review.md +82 -0
- package/docs/discovery/late-bound-goals.md +118 -0
- package/docs/discovery/steer-endpoint-design-candidates.md +288 -0
- package/docs/discovery/steer-endpoint-design-review-findings.md +104 -0
- package/docs/discovery/steer-endpoint-implementation-plan.md +284 -0
- package/docs/ideas/backlog.md +447 -97
- package/docs/ideas/design-candidates-console-session-tree-impl.md +64 -0
- package/docs/ideas/design-candidates-session-tree-view.md +196 -0
- package/docs/ideas/design-review-findings-console-session-tree-impl.md +75 -0
- package/docs/ideas/design-review-findings-session-tree-view.md +88 -0
- package/docs/ideas/implementation_plan_session_tree_view.md +238 -0
- package/package.json +2 -1
- package/spec/authoring-spec.json +16 -16
- package/spec/shape.schema.json +178 -0
- package/spec/workflow-tags.json +232 -47
- package/workflows/coding-task-workflow-agentic.json +491 -480
- package/workflows/mr-review-workflow.agentic.v2.json +5 -1
- package/workflows/wr.shaping.json +182 -0
- package/dist/console-ui/assets/index-3oXZ_A9m.js +0 -28
- package/dist/console-ui/assets/index-8dh0Psu-.css +0 -1
- package/dist/infrastructure/session/DashboardHeartbeat.d.ts +0 -8
- package/dist/infrastructure/session/DashboardHeartbeat.js +0 -39
- package/dist/infrastructure/session/DashboardLockRelease.d.ts +0 -2
- package/dist/infrastructure/session/DashboardLockRelease.js +0 -29
- package/dist/infrastructure/session/HttpServer.d.ts +0 -60
- package/dist/infrastructure/session/HttpServer.js +0 -912
- package/workflows/coding-task-workflow-agentic.lean.v2.json +0 -648
- package/workflows/coding-task-workflow-agentic.v2.json +0 -324
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# Implementation Plan: POST /api/v2/sessions/:sessionId/steer
|
|
2
|
+
|
|
3
|
+
**Branch:** `feat/session-steer-endpoint`
|
|
4
|
+
**Confidence:** High
|
|
5
|
+
**PR count:** 1
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem Statement
|
|
10
|
+
|
|
11
|
+
A coordinator script needs to inject text into an agent's next turn during a running daemon session.
|
|
12
|
+
The `steer()` mechanism in `AgentLoop` already delivers injected text. The gap is a bridge between
|
|
13
|
+
an HTTP endpoint and the closure-scoped `pendingSteerText` variable in `runWorkflow()`.
|
|
14
|
+
|
|
15
|
+
Additionally, the existing `pendingSteerText: string | null` is a single-value field that silently
|
|
16
|
+
drops coordinator steers when overwritten by `onAdvance()`. This must be fixed first (R1 finding).
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Acceptance Criteria
|
|
21
|
+
|
|
22
|
+
1. `POST /api/v2/sessions/:sessionId/steer` with body `{ "text": "..." }` returns HTTP 200
|
|
23
|
+
`{ "success": true }` when the sessionId belongs to an active daemon session.
|
|
24
|
+
2. The injected text is delivered to the agent via `agent.steer()` on the next `turn_end` event,
|
|
25
|
+
concatenated after the step text from `onAdvance()`.
|
|
26
|
+
3. Returns HTTP 404 `{ "success": false, "error": "Session not found or not a daemon session" }`
|
|
27
|
+
when the sessionId is not in the registry.
|
|
28
|
+
4. Returns HTTP 503 `{ "success": false, "error": "Steer not available..." }` in standalone console
|
|
29
|
+
mode (no steerRegistry injected).
|
|
30
|
+
5. Returns HTTP 400 for missing or non-string `text` body.
|
|
31
|
+
6. Multiple calls to the endpoint between `turn_end` events: all injected texts are delivered in the
|
|
32
|
+
same steer message, joined with `\n\n`.
|
|
33
|
+
7. After the session completes, calling the endpoint returns 404.
|
|
34
|
+
8. The existing `pendingSteerText` variable is replaced by `pendingSteerParts: string[]` and
|
|
35
|
+
`onAdvance()` behavior is unchanged from the caller's perspective (step advance still works).
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Non-Goals
|
|
40
|
+
|
|
41
|
+
- Auth token on the endpoint (v1 is localhost-only, network binding is the security layer)
|
|
42
|
+
- `waitForCoordinator` blocking gate mechanism (Phase 2B, separate task)
|
|
43
|
+
- `wr.coordinator_signal` artifact schema (Phase A, separate task)
|
|
44
|
+
- MCP-mode injection (deferred to v2)
|
|
45
|
+
- Crash recovery for in-flight steers (in-memory only, v1 known limitation)
|
|
46
|
+
- Structured request body beyond `{ text: string }` (v2 concern)
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Philosophy-Driven Constraints
|
|
51
|
+
|
|
52
|
+
- **DI for boundaries**: `SteerRegistry` must be injected into `mountConsoleRoutes()` and
|
|
53
|
+
`runWorkflow()`. No module-level singletons.
|
|
54
|
+
- **Errors as data**: HTTP responses use `{ success: bool, error?: string }` shape. No thrown
|
|
55
|
+
exceptions at the route level.
|
|
56
|
+
- **Validate at boundaries**: 400 for invalid body, 503 for disabled, 404 for not-found -- all
|
|
57
|
+
checked before touching the registry.
|
|
58
|
+
- **YAGNI**: Only what's listed in acceptance criteria. No speculative extension points.
|
|
59
|
+
- **Explicit domain types**: Named type alias `SteerRegistry` (not raw `Map<string, fn>` literal).
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Invariants
|
|
64
|
+
|
|
65
|
+
1. `pendingSteerParts` is only mutated in two places: `onAdvance()` (push step text) and the steer
|
|
66
|
+
callback registered in the `SteerRegistry` (push coordinator text). No other writer.
|
|
67
|
+
2. `pendingSteerParts` is only read and drained in the `turn_end` subscriber. Single reader.
|
|
68
|
+
3. JavaScript single-threaded event loop: no race between push and drain.
|
|
69
|
+
4. The steer callback is registered after `workrailSessionId` is decoded from the continueToken,
|
|
70
|
+
and deregistered in `runWorkflow()`'s `finally` block. No stale entries possible.
|
|
71
|
+
5. The endpoint is only active when `steerRegistry` is provided to `mountConsoleRoutes()`.
|
|
72
|
+
The standalone console does not provide it.
|
|
73
|
+
6. The `steerRegistry` param is optional on all functions. No existing callers are broken.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Selected Approach
|
|
78
|
+
|
|
79
|
+
**Hybrid (type alias + parameter injection):**
|
|
80
|
+
- Named type alias `export type SteerRegistry = Map<string, (text: string) => void>` in
|
|
81
|
+
`src/daemon/workflow-runner.ts`.
|
|
82
|
+
- `pendingSteerText: string | null` replaced by `const pendingSteerParts: string[] = []`.
|
|
83
|
+
- `onAdvance()` uses `pendingSteerParts.push(stepText)`.
|
|
84
|
+
- turn_end subscriber drains with `const parts = pendingSteerParts.splice(0)` and calls
|
|
85
|
+
`agent.steer(buildUserMessage(parts.join('\n\n')))` if `parts.length > 0`.
|
|
86
|
+
- `runWorkflow()` gains optional `steerRegistry?: SteerRegistry` param. After workrailSessionId
|
|
87
|
+
is decoded, calls `steerRegistry?.set(workrailSessionId, (text) => pendingSteerParts.push(text))`.
|
|
88
|
+
In `finally`: `steerRegistry?.delete(workrailSessionId)`.
|
|
89
|
+
- `mountConsoleRoutes()` gains optional `steerRegistry?: SteerRegistry` param after `triggerRouter`.
|
|
90
|
+
- `POST /api/v2/sessions/:sessionId/steer` endpoint added in `console-routes.ts`.
|
|
91
|
+
- `TriggerRouter` constructor gains optional `steerRegistry?: SteerRegistry`; passes to
|
|
92
|
+
`runWorkflowFn()` calls in `route()` and `dispatch()`.
|
|
93
|
+
- `RunWorkflowFn` type in `trigger-router.ts` extended with optional 6th param.
|
|
94
|
+
|
|
95
|
+
**Runner-Up:** C2 (SteerRegistry class). Loses only for having a new file for 3 trivial operations.
|
|
96
|
+
Use if the registry gains additional methods or needs isolated unit tests.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Vertical Slices
|
|
101
|
+
|
|
102
|
+
### Slice 1: Fix `pendingSteerText` -> `pendingSteerParts` (R1 prerequisite)
|
|
103
|
+
|
|
104
|
+
**Files:** `src/daemon/workflow-runner.ts` only.
|
|
105
|
+
|
|
106
|
+
**Change:**
|
|
107
|
+
- Rename `pendingSteerText: string | null` to `const pendingSteerParts: string[] = []`.
|
|
108
|
+
- Update `onAdvance()`: `pendingSteerText = stepText` -> `pendingSteerParts.push(stepText)`.
|
|
109
|
+
- Update turn_end subscriber drain:
|
|
110
|
+
- Before: `if (pendingSteerText !== null && !isComplete) { ... }`
|
|
111
|
+
- After: `if (!isComplete) { const parts = pendingSteerParts.splice(0); if (parts.length > 0) { agent.steer(buildUserMessage(parts.join('\n\n'))); } }`
|
|
112
|
+
- Note: `isComplete` guard moves outside the `splice(0)` -- drain always happens, steer only if not complete and parts non-empty.
|
|
113
|
+
|
|
114
|
+
**Acceptance:** Existing behavior unchanged. Session still receives step text on each advance.
|
|
115
|
+
No coordinator injection yet. Build+type-check passes. Existing tests pass.
|
|
116
|
+
|
|
117
|
+
**Risk:** Low. Pure refactor, no behavior change from external perspective.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### Slice 2: SteerRegistry type alias + runWorkflow() registration
|
|
122
|
+
|
|
123
|
+
**Files:** `src/daemon/workflow-runner.ts`.
|
|
124
|
+
|
|
125
|
+
**Change:**
|
|
126
|
+
- Add: `export type SteerRegistry = Map<string, (text: string) => void>;`
|
|
127
|
+
- Add optional param to `runWorkflow()`: `steerRegistry?: SteerRegistry`
|
|
128
|
+
- After `workrailSessionId` is decoded (line ~2190): register callback:
|
|
129
|
+
```typescript
|
|
130
|
+
if (steerRegistry && workrailSessionId) {
|
|
131
|
+
steerRegistry.set(workrailSessionId, (text: string) => { pendingSteerParts.push(text); });
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
- In `finally` block: `if (steerRegistry && workrailSessionId) { steerRegistry.delete(workrailSessionId); }`
|
|
135
|
+
- Add code comment on the `set()` call documenting the registration gap.
|
|
136
|
+
|
|
137
|
+
**Acceptance:** `runWorkflow()` compiles with new optional param. Existing callers unchanged.
|
|
138
|
+
Manual test: if a steerRegistry Map is passed and a callback is registered/called during a session,
|
|
139
|
+
text is pushed to `pendingSteerParts`.
|
|
140
|
+
|
|
141
|
+
**Risk:** Low. Additive change, no behavior change when `steerRegistry` is undefined.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
### Slice 3: TriggerRouter wiring
|
|
146
|
+
|
|
147
|
+
**Files:** `src/trigger/trigger-router.ts`.
|
|
148
|
+
|
|
149
|
+
**Change:**
|
|
150
|
+
- Import `SteerRegistry` from `workflow-runner.js`.
|
|
151
|
+
- Extend `RunWorkflowFn` type with optional 6th param:
|
|
152
|
+
`steerRegistry?: SteerRegistry` after `emitter?`.
|
|
153
|
+
- Add `private readonly steerRegistry?: SteerRegistry` to `TriggerRouter`.
|
|
154
|
+
- Add `steerRegistry?: SteerRegistry` to TriggerRouter constructor params.
|
|
155
|
+
- Assign in constructor: `this.steerRegistry = steerRegistry`.
|
|
156
|
+
- Update `route()` call: `this.runWorkflowFn(workflowTrigger, this.ctx, this.apiKey, undefined, this.emitter, this.steerRegistry)`
|
|
157
|
+
- Update `dispatch()` call: same.
|
|
158
|
+
|
|
159
|
+
**Acceptance:** TriggerRouter compiles. `runWorkflow` in production TriggerRouter path passes
|
|
160
|
+
steerRegistry to the agent loop. Existing trigger tests unaffected (registry is optional).
|
|
161
|
+
|
|
162
|
+
**Risk:** Low. Additive param. All existing test calls to `TriggerRouter` pass `undefined` or
|
|
163
|
+
omit the param.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
### Slice 4: HTTP endpoint in console-routes.ts
|
|
168
|
+
|
|
169
|
+
**Files:** `src/v2/usecases/console-routes.ts`.
|
|
170
|
+
|
|
171
|
+
**Change:**
|
|
172
|
+
- Import `SteerRegistry` from `../../daemon/workflow-runner.js`.
|
|
173
|
+
- Add `steerRegistry?: SteerRegistry` to `mountConsoleRoutes()` param list (after `triggerRouter`).
|
|
174
|
+
- Add endpoint after the `POST /api/v2/auto/dispatch` block:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// POST /api/v2/sessions/:sessionId/steer
|
|
178
|
+
// Injects text into a running daemon session's next agent turn.
|
|
179
|
+
// Daemon-only: requires steerRegistry to be provided at server startup.
|
|
180
|
+
// Auth: localhost-only (127.0.0.1 binding). No token auth in v1.
|
|
181
|
+
// TODO(v2): Add token auth before any multi-user or remote deployment.
|
|
182
|
+
app.post('/api/v2/sessions/:sessionId/steer', express.json(), (req: Request, res: Response) => {
|
|
183
|
+
if (!steerRegistry) {
|
|
184
|
+
res.status(503).json({ success: false, error: 'Steer not available (not a daemon context).' });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const { sessionId } = req.params;
|
|
188
|
+
const body = req.body as { text?: unknown };
|
|
189
|
+
const text = typeof body.text === 'string' ? body.text.trim() : '';
|
|
190
|
+
if (!text) {
|
|
191
|
+
res.status(400).json({ success: false, error: 'text is required and must be a non-empty string.' });
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const callback = steerRegistry.get(sessionId);
|
|
195
|
+
if (!callback) {
|
|
196
|
+
res.status(404).json({ success: false, error: 'Session not found or not a daemon session.' });
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
callback(text);
|
|
200
|
+
res.json({ success: true });
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Acceptance:** All 5 HTTP response cases work correctly (200, 400, 404, 503). Session receives
|
|
205
|
+
injected text on next turn_end. Standalone console returns 503.
|
|
206
|
+
|
|
207
|
+
**Risk:** Low. New endpoint, no changes to existing routes.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
### Slice 5: Daemon wiring in daemon-console.ts
|
|
212
|
+
|
|
213
|
+
**Files:** `src/trigger/daemon-console.ts`.
|
|
214
|
+
|
|
215
|
+
**Change:**
|
|
216
|
+
- Import `SteerRegistry` from `../daemon/workflow-runner.js`.
|
|
217
|
+
- Before constructing `TriggerRouter`: `const steerRegistry: SteerRegistry = new Map();`
|
|
218
|
+
- Pass to `TriggerRouter` constructor: `new TriggerRouter(index, ctx, apiKey, runWorkflow, execFn, ..., steerRegistry)`
|
|
219
|
+
- Pass to `mountConsoleRoutes()`: add `steerRegistry` as the last argument (after `triggerRouter`).
|
|
220
|
+
- Also update the direct `runWorkflow()` call in `console-routes.ts` `POST /auto/dispatch` path
|
|
221
|
+
(when `triggerRouter` is absent): pass `steerRegistry` as 6th arg.
|
|
222
|
+
|
|
223
|
+
**Acceptance:** End-to-end: daemon starts, `POST /auto/dispatch` creates a session, coordinator
|
|
224
|
+
calls `POST /sessions/:id/steer`, agent receives injected text on next turn.
|
|
225
|
+
|
|
226
|
+
**Risk:** Medium. This is the wiring step that connects all slices. Most likely source of missed
|
|
227
|
+
call sites.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Test Design
|
|
232
|
+
|
|
233
|
+
### Unit tests (workflow-runner.ts)
|
|
234
|
+
- Test that `onAdvance()` pushes to `pendingSteerParts`.
|
|
235
|
+
- Test that turn_end subscriber joins and steers when `pendingSteerParts.length > 0`.
|
|
236
|
+
- Test that multiple pushes (simulate both `onAdvance` and steer callback) produce joined text.
|
|
237
|
+
- Test that `steerRegistry.set()` is called after workrailSessionId decoded.
|
|
238
|
+
- Test that `steerRegistry.delete()` is called in finally (mock registry, verify delete).
|
|
239
|
+
|
|
240
|
+
### Integration test (console-routes.ts)
|
|
241
|
+
- Mock `steerRegistry` with a Map. POST to endpoint with valid body -> 200, callback called.
|
|
242
|
+
- POST with empty body -> 400.
|
|
243
|
+
- POST with unknown sessionId -> 404.
|
|
244
|
+
- POST without steerRegistry injected -> 503.
|
|
245
|
+
|
|
246
|
+
### Regression: existing tests
|
|
247
|
+
- All existing `runWorkflow()` tests must pass unchanged (optional param, default undefined).
|
|
248
|
+
- All existing `mountConsoleRoutes()` tests must pass unchanged.
|
|
249
|
+
- All existing TriggerRouter tests must pass unchanged.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Risk Register
|
|
254
|
+
|
|
255
|
+
| Risk | Likelihood | Impact | Mitigation |
|
|
256
|
+
|---|---|---|---|
|
|
257
|
+
| Missed call site for steerRegistry in dispatch/route | Medium | High | Search for all `runWorkflowFn(` calls in trigger-router.ts before submitting |
|
|
258
|
+
| `pendingSteerParts.splice(0)` stale closure ref | Low | High | splice(0) mutates in-place; closure over array variable (not array contents) is safe |
|
|
259
|
+
| Registration gap causes 404 for early steers | Very low | Low | Document with code comment; coordinator retries on 404 |
|
|
260
|
+
| `mountConsoleRoutes` callers not updated | Low | Medium | Only 3 callers: daemon-console.ts, standalone-console.ts (no change), console-routes.ts |
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## PR Packaging Strategy
|
|
265
|
+
|
|
266
|
+
Single PR on branch `feat/session-steer-endpoint`. All 5 slices together. The R1 fix (Slice 1) is
|
|
267
|
+
small enough that it doesn't need its own PR. The endpoint is only usable when all slices are present.
|
|
268
|
+
|
|
269
|
+
PR title: `feat(console): add POST /api/v2/sessions/:sessionId/steer for coordinator injection`
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Philosophy Alignment Per Slice
|
|
274
|
+
|
|
275
|
+
| Slice | Principle | Status |
|
|
276
|
+
|---|---|---|
|
|
277
|
+
| S1 pendingSteerParts | Immutability by default | Tension (mutable array) -- acceptable, mutation bounded |
|
|
278
|
+
| S1 pendingSteerParts | Compose with small pure functions | Satisfied -- drain is one expression |
|
|
279
|
+
| S2 SteerRegistry type | Explicit domain types | Satisfied -- named alias |
|
|
280
|
+
| S2 runWorkflow registration | DI for boundaries | Satisfied -- injected, not global |
|
|
281
|
+
| S3 TriggerRouter | Make illegal states unrepresentable | Satisfied -- optional param can't be confused with required |
|
|
282
|
+
| S4 HTTP endpoint | Validate at boundaries | Satisfied -- 400/503/404 before touching registry |
|
|
283
|
+
| S4 HTTP endpoint | Errors as data | Satisfied -- { success: bool } shape |
|
|
284
|
+
| S5 daemon wiring | YAGNI | Satisfied -- single Map, no extra abstraction |
|