@exaudeus/workrail 3.66.0 → 3.68.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/application/services/compiler/template-registry.js +10 -1
- package/dist/application/validation.js +1 -1
- package/dist/cli/commands/worktrain-init.js +1 -1
- package/dist/console/standalone-console.js +4 -1
- package/dist/console-ui/assets/{index-BynU38Vu.js → index-CyzltI6D.js} +1 -1
- package/dist/console-ui/index.html +1 -1
- package/dist/coordinators/modes/full-pipeline.js +4 -4
- package/dist/coordinators/modes/implement-shared.js +5 -5
- package/dist/coordinators/modes/implement.js +4 -4
- package/dist/coordinators/pr-review.js +4 -4
- package/dist/daemon/workflow-runner.d.ts +1 -0
- package/dist/daemon/workflow-runner.js +1 -0
- package/dist/infrastructure/storage/schema-validating-workflow-storage.d.ts +21 -2
- package/dist/infrastructure/storage/schema-validating-workflow-storage.js +48 -0
- package/dist/manifest.json +41 -41
- package/dist/mcp/handlers/v2-workflow.js +24 -7
- package/dist/mcp/output-schemas.d.ts +36 -0
- package/dist/mcp/output-schemas.js +11 -1
- package/dist/mcp/workflow-protocol-contracts.js +2 -2
- package/dist/v2/projections/session-metrics.d.ts +1 -1
- package/dist/v2/projections/session-metrics.js +16 -35
- package/dist/v2/usecases/console-routes.d.ts +2 -2
- package/docs/authoring-v2.md +4 -4
- package/docs/changelog-recent.md +3 -3
- package/docs/configuration.md +1 -1
- package/docs/design/adaptive-coordinator-context-candidates.md +1 -1
- package/docs/design/adaptive-coordinator-context.md +1 -1
- package/docs/design/adaptive-coordinator-routing-candidates.md +18 -18
- package/docs/design/adaptive-coordinator-routing-review.md +1 -1
- package/docs/design/adaptive-coordinator-routing.md +34 -34
- package/docs/design/agent-cascade-protocol.md +2 -2
- package/docs/design/console-daemon-separation-discovery.md +323 -0
- package/docs/design/context-assembly-design-candidates.md +1 -1
- package/docs/design/context-assembly-implementation-plan.md +1 -1
- package/docs/design/context-assembly-layer.md +2 -2
- package/docs/design/context-assembly-review-findings.md +1 -1
- package/docs/design/coordinator-access-audit.md +293 -0
- package/docs/design/coordinator-architecture-audit.md +62 -0
- package/docs/design/coordinator-error-handling-audit.md +240 -0
- package/docs/design/coordinator-testability-audit.md +426 -0
- package/docs/design/daemon-architecture-discovery.md +1 -1
- package/docs/design/daemon-console-separation-discovery.md +242 -0
- package/docs/design/daemon-memory-audit.md +203 -0
- package/docs/design/design-candidates-console-daemon-separation.md +256 -0
- package/docs/design/design-candidates-discovery-loop-fix.md +141 -0
- package/docs/design/design-review-findings-console-daemon-separation.md +106 -0
- package/docs/design/design-review-findings-discovery-loop-fix.md +81 -0
- package/docs/design/discovery-loop-fix-candidates.md +161 -0
- package/docs/design/discovery-loop-fix-design-review.md +106 -0
- package/docs/design/discovery-loop-fix-validation.md +258 -0
- package/docs/design/discovery-loop-investigation-A.md +188 -0
- package/docs/design/discovery-loop-investigation-B.md +287 -0
- package/docs/design/exploration-workflow-candidates.md +205 -0
- package/docs/design/exploration-workflow-design-review.md +166 -0
- package/docs/design/exploration-workflow-discovery.md +443 -0
- package/docs/design/ide-context-files-candidates.md +231 -0
- package/docs/design/ide-context-files-design-review.md +85 -0
- package/docs/design/ide-context-files.md +615 -0
- package/docs/design/implementation-plan-discovery-loop-fix.md +199 -0
- package/docs/design/implementation-plan-queue-poll-rotation.md +102 -0
- package/docs/design/in-process-http-audit.md +190 -0
- package/docs/design/layer3b-ghost-nodes-design-candidates.md +2 -2
- package/docs/design/loadSessionNotes-candidates.md +108 -0
- package/docs/design/loadSessionNotes-test-coverage-discovery.md +297 -0
- package/docs/design/loadSessionNotes-test-coverage-session4.md +209 -0
- package/docs/design/loadSessionNotes-test-coverage-v3.md +321 -0
- package/docs/design/probe-session-design-candidates.md +261 -0
- package/docs/design/probe-session-phase0.md +490 -0
- package/docs/design/routines-guide.md +7 -7
- package/docs/design/session-metrics-attribution-candidates.md +250 -0
- package/docs/design/session-metrics-attribution-design-review.md +115 -0
- package/docs/design/session-metrics-attribution-discovery.md +319 -0
- package/docs/design/session-metrics-candidates.md +227 -0
- package/docs/design/session-metrics-design-review.md +104 -0
- package/docs/design/session-metrics-discovery.md +454 -0
- package/docs/design/spawn-session-debug.md +202 -0
- package/docs/design/trigger-validator-candidates.md +214 -0
- package/docs/design/trigger-validator-review.md +109 -0
- package/docs/design/trigger-validator-shaping-phase0.md +239 -0
- package/docs/design/trigger-validator.md +454 -0
- package/docs/design/v2-core-design-locks.md +2 -2
- package/docs/design/workflow-extension-points.md +15 -15
- package/docs/design/workflow-id-validation-at-startup.md +1 -1
- package/docs/design/workflow-id-validation-implementation-plan.md +2 -2
- package/docs/design/workflow-trigger-lifecycle-audit.md +175 -0
- package/docs/design/worktrain-task-queue-candidates.md +5 -5
- package/docs/design/worktrain-task-queue.md +4 -4
- package/docs/discovery/coordinator-script-design.md +1 -1
- package/docs/discovery/coordinator-ux-discovery.md +3 -3
- package/docs/discovery/simulation-report.md +1 -1
- package/docs/discovery/workflow-modernization-discovery.md +326 -0
- package/docs/discovery/workflow-selection-for-discovery-tasks.md +33 -33
- package/docs/discovery/worktrain-status-briefing.md +1 -1
- package/docs/discovery/wr-discovery-goal-reframing.md +1 -1
- package/docs/docker.md +1 -1
- package/docs/ideas/backlog.md +227 -0
- package/docs/ideas/third-party-workflow-setup-design-thinking.md +1 -1
- package/docs/integrations/claude-code.md +5 -5
- package/docs/integrations/firebender.md +1 -1
- package/docs/plans/agentic-orchestration-roadmap.md +2 -2
- package/docs/plans/mr-review-workflow-redesign.md +9 -9
- package/docs/plans/ui-ux-workflow-design-candidates.md +4 -4
- package/docs/plans/ui-ux-workflow-discovery.md +2 -2
- package/docs/plans/workflow-categories-candidates.md +8 -8
- package/docs/plans/workflow-categories-discovery.md +4 -4
- package/docs/plans/workflow-modernization-design.md +430 -0
- package/docs/plans/workflow-staleness-detection-candidates.md +11 -11
- package/docs/plans/workflow-staleness-detection-review.md +4 -4
- package/docs/plans/workflow-staleness-detection.md +9 -9
- package/docs/plans/workrail-platform-vision.md +3 -3
- package/docs/reference/agent-context-cleaner-snippet.md +1 -1
- package/docs/reference/agent-context-guidance.md +4 -4
- package/docs/reference/context-optimization.md +2 -2
- package/docs/roadmap/now-next-later.md +2 -2
- package/docs/roadmap/open-work-inventory.md +16 -16
- package/docs/workflows.md +31 -31
- package/package.json +1 -1
- package/spec/workflow-tags.json +47 -47
- package/workflows/adaptive-ticket-creation.json +16 -16
- package/workflows/architecture-scalability-audit.json +22 -22
- package/workflows/bug-investigation.agentic.v2.json +3 -3
- package/workflows/classify-task-workflow.json +1 -1
- package/workflows/coding-task-workflow-agentic.json +6 -6
- package/workflows/cross-platform-code-conversion.v2.json +8 -8
- package/workflows/document-creation-workflow.json +8 -8
- package/workflows/documentation-update-workflow.json +8 -8
- package/workflows/intelligent-test-case-generation.json +2 -2
- package/workflows/learner-centered-course-workflow.json +2 -2
- package/workflows/mr-review-workflow.agentic.v2.json +4 -4
- package/workflows/personal-learning-materials-creation-branched.json +8 -8
- package/workflows/presentation-creation.json +5 -5
- package/workflows/production-readiness-audit.json +1 -1
- package/workflows/relocation-workflow-us.json +31 -31
- package/workflows/routines/context-gathering.json +1 -1
- package/workflows/routines/design-review.json +1 -1
- package/workflows/routines/execution-simulation.json +1 -1
- package/workflows/routines/feature-implementation.json +3 -3
- package/workflows/routines/final-verification.json +1 -1
- package/workflows/routines/hypothesis-challenge.json +1 -1
- package/workflows/routines/ideation.json +1 -1
- package/workflows/routines/parallel-work-partitioning.json +3 -3
- package/workflows/routines/philosophy-alignment.json +2 -2
- package/workflows/routines/plan-analysis.json +1 -1
- package/workflows/routines/plan-generation.json +1 -1
- package/workflows/routines/tension-driven-design.json +6 -6
- package/workflows/scoped-documentation-workflow.json +26 -26
- package/workflows/ui-ux-design-workflow.json +14 -14
- package/workflows/workflow-diagnose-environment.json +1 -1
- package/workflows/workflow-for-workflows.json +32 -77
- package/workflows/workflow-for-workflows.v2.json +0 -788
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# Console-Daemon Separation Discovery
|
|
2
|
+
|
|
3
|
+
## Context / Ask
|
|
4
|
+
|
|
5
|
+
**Stated Goal (solution statement):** Enforce strict separation between WorkRail Console, WorkTrain Daemon, and WorkRail MCP Server so none imports from or calls another at runtime. Each reads shared state (filesystem) independently and is fully independently restartable. A specific candidate was floated: split console reads (port 3456) from daemon control (port 3200) at the browser level.
|
|
6
|
+
|
|
7
|
+
**Reframed Problem:** The daemon's embedded console server holds live references to daemon internals, creating tight coupling that prevents independent restart. The core need is to keep control actions (dispatch, steer, force-poll) available in the browser UI without the console server holding live daemon object references.
|
|
8
|
+
|
|
9
|
+
## Path Recommendation
|
|
10
|
+
|
|
11
|
+
**Path: `design_first`** -- The goal was stated as a solution (strict zero-import separation + split-by-port). Before researching the landscape, the right move is to verify the problem framing and understand what constraints make certain solutions harder or easier. The stated solution may be correct but could also be over-engineering -- or it may be missing a simpler invariant (e.g. "no shared object lifecycle" rather than "no shared imports").
|
|
12
|
+
|
|
13
|
+
Rationale over alternatives:
|
|
14
|
+
- `landscape_first` would map the current codebase without challenging whether the solution direction is right -- premature if the framing is off.
|
|
15
|
+
- `full_spectrum` is warranted when both landscape grounding AND reframing are needed; here the reframing already happened in Step 1 and the landscape research is bounded to a few specific files.
|
|
16
|
+
|
|
17
|
+
## Constraints / Anti-goals
|
|
18
|
+
|
|
19
|
+
**Core constraints:**
|
|
20
|
+
- WorkRail MCP Server must not be destabilized (used in production by others)
|
|
21
|
+
- Control actions (dispatch, steer, force-poll) must remain accessible from the browser UI
|
|
22
|
+
- `npx vitest run` must pass after any changes
|
|
23
|
+
- No major architectural surgery -- this is a WorkTrain (daemon) concern, not a WorkRail MCP concern
|
|
24
|
+
|
|
25
|
+
**Anti-goals:**
|
|
26
|
+
- Do not add features to the MCP server to support this separation
|
|
27
|
+
- Do not remove control functionality from the UI -- degrading gracefully when daemon is down is acceptable, but removing buttons permanently is not
|
|
28
|
+
- Do not create a new inter-process communication protocol (e.g. message bus, event sourcing) if simpler approaches work
|
|
29
|
+
|
|
30
|
+
## Primary Uncertainty
|
|
31
|
+
|
|
32
|
+
Does the console frontend (`console/src/`) assume a single origin for ALL API calls, or does it already have the plumbing to call different backends? This is the single biggest unknown that determines whether split-by-port is cheap or expensive.
|
|
33
|
+
|
|
34
|
+
## Known Approaches
|
|
35
|
+
|
|
36
|
+
1. **Split by port at browser level** (stated candidate): Console server (3456) is filesystem-only. Daemon HTTP server (3200) gets control endpoints. Browser frontend calls both ports. CORS on localhost allows this.
|
|
37
|
+
2. **Thin HTTP proxy on the console server**: Console server proxies control actions to the daemon's HTTP server (3200) when it's available. No frontend changes needed. But the console server now has a runtime dependency on the daemon's port -- soft coupling via HTTP.
|
|
38
|
+
3. **Lock-file / sidecar protocol**: Console server reads a daemon lock file to discover if the daemon is running and on which port, then proxies selectively. Already partially done (`daemon-console.lock`). Extension of existing pattern.
|
|
39
|
+
4. **Remove control actions from the console entirely**: Control actions (dispatch, steer, force-poll) are moved to a CLI (`worktrain dispatch`, `worktrain steer`). Browser UI becomes purely read-only. Simplest architecturally; may be too limiting for the owner's workflow.
|
|
40
|
+
|
|
41
|
+
## Stakeholders
|
|
42
|
+
|
|
43
|
+
- Project owner (Etienne): primary user of the daemon; cares about architectural cleanliness and independent restartability
|
|
44
|
+
- External WorkRail MCP users: must not be affected
|
|
45
|
+
|
|
46
|
+
## Artifact Strategy
|
|
47
|
+
|
|
48
|
+
This document is a **human-readable artifact** for tracking discovery findings and decisions. It is NOT the execution truth for the workflow. Execution truth lives in WorkRail's durable step notes and context variables. If a chat rewind occurs, this file may be stale -- consult the WorkRail session notes as authoritative.
|
|
49
|
+
|
|
50
|
+
## Capability Notes
|
|
51
|
+
|
|
52
|
+
- **Delegation (WorkRail Executor):** Available -- routines including `wr.routine-context-gathering`, `wr.routine-hypothesis-challenge`, `wr.routine-tension-driven-design` are accessible.
|
|
53
|
+
- **Web browsing:** Not applicable for this discovery (all sources are local codebase files).
|
|
54
|
+
- **Fallback:** All context gathering will be done directly by reading source files. No external research needed.
|
|
55
|
+
|
|
56
|
+
## Landscape Packet
|
|
57
|
+
|
|
58
|
+
### Key Source Files Read
|
|
59
|
+
|
|
60
|
+
**`src/trigger/daemon-console.ts`** (daemon's embedded console server, port 3456)
|
|
61
|
+
- Imports `V2ToolContext` from `../mcp/types.js`, `TriggerRouter`, `SteerRegistry`, `PollingScheduler` from daemon internals
|
|
62
|
+
- Calls `mountConsoleRoutes()` passing all four live daemon handles as optional params
|
|
63
|
+
- Writes `~/.workrail/daemon-console.lock` with `{ pid, port }` on startup
|
|
64
|
+
- The `StartDaemonConsoleOptions` interface explicitly declares `triggerRouter?`, `steerRegistry?`, `pollingScheduler?` as optional params
|
|
65
|
+
|
|
66
|
+
**`src/v2/usecases/console-routes.ts`** (shared route layer, 1079 lines)
|
|
67
|
+
- `mountConsoleRoutes()` accepts 10 parameters; the last 3 are daemon-specific: `triggerRouter?`, `steerRegistry?`, `pollingScheduler?`
|
|
68
|
+
- Three control endpoints depend on these optional params:
|
|
69
|
+
- `POST /api/v2/auto/dispatch` -- requires `v2ToolContext` and optionally `triggerRouter`; falls back to direct `runWorkflow()` if no router
|
|
70
|
+
- `GET /api/v2/triggers` -- returns empty list if no `triggerRouter`
|
|
71
|
+
- `POST /api/v2/triggers/:id/poll` -- returns 503 if no `pollingScheduler`
|
|
72
|
+
- `POST /api/v2/sessions/:id/steer` -- returns 503 if no `steerRegistry`
|
|
73
|
+
- All control endpoints already have 503 / empty-list fallbacks when daemon handles are absent
|
|
74
|
+
- The standalone console (`src/console/standalone-console.ts`) calls `mountConsoleRoutes()` with `undefined` for all three daemon params -- this is working today
|
|
75
|
+
|
|
76
|
+
**`src/console/standalone-console.ts`** (the already-correct standalone implementation)
|
|
77
|
+
- Zero imports from `src/daemon/` or `src/trigger/`
|
|
78
|
+
- Constructs its own infrastructure adapters (LocalDataDirV2, LocalSessionEventLogStoreV2, etc.)
|
|
79
|
+
- Calls `mountConsoleRoutes()` with only `consoleService` -- all daemon params are `undefined`
|
|
80
|
+
- Writes the same `daemon-console.lock` file as the daemon console
|
|
81
|
+
- **This file is already the correct architecture.** The daemon console is the problem, not the standalone console.
|
|
82
|
+
|
|
83
|
+
**`src/trigger/trigger-listener.ts`** (daemon HTTP server, port 3200)
|
|
84
|
+
- `TriggerListenerHandle` already exposes `router`, `steerRegistry`, `scheduler` as public fields
|
|
85
|
+
- These were designed to be passed to `startDaemonConsole()` by the caller
|
|
86
|
+
- The trigger listener's Express app (`createTriggerApp()`) only handles `POST /webhook/:triggerId` and `GET /health` -- no console routes
|
|
87
|
+
- Port 3200 has no console routes today
|
|
88
|
+
|
|
89
|
+
**`src/mcp/handlers/session.ts`** (`handleOpenDashboard`)
|
|
90
|
+
- Reads `~/.workrail/daemon-console.lock` to discover the console port -- soft coupling via filesystem, not imports
|
|
91
|
+
- Falls back to `DEFAULT_CONSOLE_PORT` (3456) if lock file is absent
|
|
92
|
+
- This is an acceptable soft coupling (filesystem read, not a module import)
|
|
93
|
+
|
|
94
|
+
**Console frontend (`console/src/api/hooks.ts`)**
|
|
95
|
+
- **ALL API calls use relative paths** (`/api/v2/sessions`, `/api/v2/auto/dispatch`, etc.)
|
|
96
|
+
- The frontend assumes a single origin -- it is a pure SPA served from whatever host serves the static files
|
|
97
|
+
- `dispatchWorkflow()` calls `POST /api/v2/auto/dispatch` -- this is the one control action the frontend currently makes
|
|
98
|
+
- `useTriggerList()` calls `GET /api/v2/triggers` with a 503 fallback to empty list
|
|
99
|
+
- Steer and force-poll are NOT called from the frontend yet (no UI for these)
|
|
100
|
+
|
|
101
|
+
### Critical Finding: The Architecture Already Exists
|
|
102
|
+
|
|
103
|
+
`src/console/standalone-console.ts` is already the correct standalone implementation. It has NO daemon imports. The `mountConsoleRoutes()` layer already handles the absence of daemon handles gracefully (503s and empty lists). The standalone console today returns 503 for dispatch, steer, and poll when daemon handles are absent.
|
|
104
|
+
|
|
105
|
+
The `daemon-console.ts` embedded console is redundant -- it does the same thing as `standalone-console.ts` but passes live daemon handles. When the daemon runs, both the standalone console AND the daemon-embedded console would serve on port 3456, causing a port conflict. The lock file mechanism means only one can run at a time.
|
|
106
|
+
|
|
107
|
+
**The actual question is: should the daemon start a console server at all, or should users always run `worktrain console` separately?**
|
|
108
|
+
|
|
109
|
+
### Vite Base URL Configuration
|
|
110
|
+
|
|
111
|
+
Console frontend is built with a Vite config. All API calls use relative URLs -- there is no `VITE_API_BASE_URL` or similar abstraction. The frontend is tightly bound to its origin server.
|
|
112
|
+
|
|
113
|
+
## Problem Frame Packet
|
|
114
|
+
|
|
115
|
+
### The Real Problem (Narrowed)
|
|
116
|
+
|
|
117
|
+
The problem is NOT that separation is impossible -- `standalone-console.ts` already achieves it. The problem is that `daemon-console.ts` was added as a convenience so users don't have to run `worktrain console` separately when the daemon is running. This convenience introduced the coupling.
|
|
118
|
+
|
|
119
|
+
The "split by port at browser level" option is only relevant IF the daemon keeps its embedded console AND the browser needs to reach daemon-specific endpoints. But the embedded console's only purpose was to add the control endpoints (dispatch, steer, poll) to the browser UI.
|
|
120
|
+
|
|
121
|
+
**The tensions to resolve:**
|
|
122
|
+
1. **Convenience vs. separation**: should users start the console separately, or should the daemon start it for them?
|
|
123
|
+
2. **Control endpoints**: if the standalone console is filesystem-only (per strict separation), dispatch/steer/poll return 503. Is this acceptable?
|
|
124
|
+
3. **Single origin assumption**: the frontend uses relative URLs -- any "split by port" approach requires frontend changes to reach daemon:3200 directly.
|
|
125
|
+
|
|
126
|
+
## Candidate Generation Setup
|
|
127
|
+
|
|
128
|
+
**Path: design_first -- Requirements for the candidate set:**
|
|
129
|
+
|
|
130
|
+
1. At least one candidate must meaningfully REFRAME the problem instead of just packaging the obvious options. The obvious options are "keep daemon-console.ts with fewer imports" and "add control routes to trigger-listener.ts". A reframing would question whether the daemon needs a console server at all.
|
|
131
|
+
|
|
132
|
+
2. Candidates must span a genuine range of tradeoff positions:
|
|
133
|
+
- One that maximizes separation purity (even at cost of functionality)
|
|
134
|
+
- One that minimizes user friction (even at cost of architectural purity)
|
|
135
|
+
- One that addresses the real root cause (redundancy of daemon-console.ts given standalone-console.ts already exists)
|
|
136
|
+
|
|
137
|
+
3. Each candidate must state what happens to the control actions (dispatch, steer, poll) and whether the frontend needs changes.
|
|
138
|
+
|
|
139
|
+
4. Given the single-origin frontend constraint, any candidate that proposes split-by-port MUST account for the frontend work required.
|
|
140
|
+
|
|
141
|
+
**Candidates MUST NOT be generated solely from the problem statement. They must reflect the actual code constraints discovered:**
|
|
142
|
+
- `standalone-console.ts` already works correctly with no daemon imports
|
|
143
|
+
- `mountConsoleRoutes()` already has 503 fallbacks for all control endpoints
|
|
144
|
+
- The frontend uses relative URLs exclusively
|
|
145
|
+
- `TriggerListenerHandle` already exposes `router`, `steerRegistry`, `scheduler` publicly
|
|
146
|
+
|
|
147
|
+
## Candidate Directions
|
|
148
|
+
|
|
149
|
+
### Candidate A: Delete daemon-console.ts, make standalone-console.ts the only console server (Simplest / Reframing)
|
|
150
|
+
|
|
151
|
+
**One-sentence summary:** Delete `daemon-console.ts` and its call site in daemon startup; the standalone console (`worktrain console`) is the only console server, and dispatch returns 503 in the browser UI.
|
|
152
|
+
|
|
153
|
+
**Tensions resolved:**
|
|
154
|
+
- Separation: complete. Standalone console has zero daemon imports.
|
|
155
|
+
- No redundancy: one console server, no lock-file collision risk.
|
|
156
|
+
|
|
157
|
+
**Tensions accepted:**
|
|
158
|
+
- UX regression: users must run `worktrain console` separately when using the daemon.
|
|
159
|
+
- The dispatch button in the browser returns 503 when running via the standalone console (daemon handles absent).
|
|
160
|
+
|
|
161
|
+
**Boundary:** The daemon startup path (`src/cli/commands/worktrain-daemon.ts` or equivalent). Remove the `startDaemonConsole()` call and delete `daemon-console.ts`.
|
|
162
|
+
|
|
163
|
+
**Failure mode to watch for:** Users don't know they need to run `worktrain console` separately; the dispatch button 503s confusingly. Mitigation: improve the 503 message to say "Dispatch requires the daemon; run `worktrain console` while the daemon is running to enable this."
|
|
164
|
+
|
|
165
|
+
**Relation to existing patterns:** The standalone console already exists and works. This candidate follows the existing architecture -- it just removes the redundant coupled variant.
|
|
166
|
+
|
|
167
|
+
**Gains:** Zero code to write. daemon-console.ts (~220 lines) deleted. No lock-file ambiguity. Architecturally clean.
|
|
168
|
+
**Gives up:** Daemon no longer auto-starts the console. Dispatch from browser only works when running standalone console alongside daemon.
|
|
169
|
+
|
|
170
|
+
**Philosophy alignment:** "Architectural fixes over patches" (A). "YAGNI" (A). "Make illegal states unrepresentable" (A -- eliminates the ambiguous dual-console state).
|
|
171
|
+
**Philosophy conflict:** None.
|
|
172
|
+
|
|
173
|
+
**Scope:** Best-fit. Deletes ~220 lines, removes one call site in daemon startup.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
### Candidate B: Move control endpoints to trigger-listener.ts (port 3200), update frontend to use absolute URLs for control actions
|
|
178
|
+
|
|
179
|
+
**One-sentence summary:** Add `POST /dispatch`, `POST /sessions/:id/steer`, `POST /triggers/:id/poll` and `GET /triggers` to the daemon's existing HTTP server (port 3200), update the frontend to call `http://localhost:3200/...` for these control actions while reading sessions from the standalone console's relative URLs.
|
|
180
|
+
|
|
181
|
+
**Tensions resolved:**
|
|
182
|
+
- Separation: complete for read paths. Console server (3456) stays filesystem-only.
|
|
183
|
+
- Control actions work from browser UI when daemon is running.
|
|
184
|
+
- Daemon HTTP server (3200) already binds; no new port needed.
|
|
185
|
+
|
|
186
|
+
**Tensions accepted:**
|
|
187
|
+
- Frontend complexity: all four control endpoints (dispatch, steer, poll, triggers list) need absolute URLs pointing at 3200.
|
|
188
|
+
- `fetch('http://localhost:3200/dispatch')` from a page served at `http://localhost:3456` requires CORS headers on 3200 (which trigger-listener.ts does not currently set up).
|
|
189
|
+
- Frontend must detect daemon unavailability (port 3200 unreachable) and disable control buttons.
|
|
190
|
+
|
|
191
|
+
**Boundary:** `src/trigger/trigger-listener.ts` (add routes) + `console/src/api/hooks.ts` (change URLs for control calls) + CORS middleware on trigger-listener.ts.
|
|
192
|
+
|
|
193
|
+
**Failure mode to watch for:** The frontend must gracefully handle ECONNREFUSED when fetching from port 3200 (daemon not running). React Query's retry behavior may cause confusing UX. Also: trigger-listener.ts currently only handles webhook traffic; adding console-API-style routes to it changes its purpose from "webhook receiver" to "daemon HTTP API". That scope change may introduce coupling in the other direction.
|
|
194
|
+
|
|
195
|
+
**Relation to existing patterns:** Departs from existing pattern. `createTriggerApp()` is a pure webhook receiver today. Adding console routes to it means `console-routes.ts` imports from `trigger-listener.ts` or vice versa -- new cross-boundary coupling.
|
|
196
|
+
|
|
197
|
+
**Gains:** Complete separation of console server from daemon. Dispatch works from browser.
|
|
198
|
+
**Gives up:** Frontend changes required for all control endpoints. CORS added to trigger-listener.ts. trigger-listener.ts's purpose changes.
|
|
199
|
+
|
|
200
|
+
**Philosophy alignment:** "Dependency injection for boundaries" (A -- control deps injected at port 3200). "Architectural fixes over patches" (partially A).
|
|
201
|
+
**Philosophy conflict:** "YAGNI" -- adding frontend URL-management infrastructure and CORS to a previously simple webhook receiver.
|
|
202
|
+
|
|
203
|
+
**Scope:** Too broad. Touches frontend, trigger-listener.ts, CORS config, and browser availability detection -- for a feature (live browser dispatch) that only the project owner uses.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
### Candidate C: Thin HTTP proxy on standalone console -- forward control actions to daemon:3200
|
|
208
|
+
|
|
209
|
+
**One-sentence summary:** The standalone console server (3456) proxies `POST /api/v2/auto/dispatch`, steer, and poll to `http://127.0.0.1:3200` when the daemon is detected as running (via lock-file or health check), returning 503 when it is not.
|
|
210
|
+
|
|
211
|
+
**Tensions resolved:**
|
|
212
|
+
- Frontend: zero changes needed. All relative URLs continue to work.
|
|
213
|
+
- Separation: standalone-console.ts has no daemon object imports. The proxy is an HTTP call, not an import.
|
|
214
|
+
- Dispatch works from browser UI when daemon is running; returns 503 when not.
|
|
215
|
+
|
|
216
|
+
**Tensions accepted:**
|
|
217
|
+
- Soft coupling: the console server now has a runtime dependency on the daemon's port. It must know the daemon's HTTP address (port 3200 or `WORKRAIL_TRIGGER_PORT`).
|
|
218
|
+
- Proxy adds request latency and a new failure mode (daemon listening on 3200 but unhealthy).
|
|
219
|
+
- The lock file or a new `daemon.lock` file must record the daemon's control port, not just the console port.
|
|
220
|
+
|
|
221
|
+
**Boundary:** `src/console/standalone-console.ts` (add proxy routes using `http-proxy-middleware` or a simple `fetch` forward) + a new `daemon-control.lock` file that records the daemon's port 3200.
|
|
222
|
+
|
|
223
|
+
**Failure mode to watch for:** Proxy silently failing (fetch timeout) when daemon is slow, leaving the browser spinner indefinitely. Also: circular routing if someone configures the console and daemon on the same port.
|
|
224
|
+
|
|
225
|
+
**Relation to existing patterns:** Adapts the existing lock-file discovery pattern (`daemon-console.lock`). The session.ts `readConsoleLockPort()` already reads a lock file to find the console -- the same pattern works for finding the daemon.
|
|
226
|
+
|
|
227
|
+
**Gains:** Zero frontend changes. Clean server-side separation. Works with the existing relative-URL frontend.
|
|
228
|
+
**Gives up:** New runtime HTTP dependency from console to daemon. Proxy failure modes. New lock file convention.
|
|
229
|
+
|
|
230
|
+
**Philosophy alignment:** "Errors are data" (proxy failures should be explicit 503/504, not silent). "Validate at boundaries" (proxy validates daemon availability before forwarding).
|
|
231
|
+
**Philosophy conflict:** "Architectural fixes over patches" -- a proxy is a patch that hides the coupling rather than eliminating it.
|
|
232
|
+
|
|
233
|
+
**Scope:** Best-fit. Changes only `standalone-console.ts` (add 3 proxy routes) and daemon startup (write a daemon-control.lock). No frontend changes.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
### Candidate D: Daemon spawns standalone-console as a subprocess (Reframing 2)
|
|
238
|
+
|
|
239
|
+
**One-sentence summary:** Instead of the daemon having an embedded console server, it spawns `worktrain console` as a child process, which starts the fully-decoupled standalone console on port 3456.
|
|
240
|
+
|
|
241
|
+
**Tensions resolved:**
|
|
242
|
+
- Separation: complete. The console server runs as a separate process with its own module scope. No daemon objects leak into the console process.
|
|
243
|
+
- Daemon convenience: users don't need to manually start the console -- the daemon starts it.
|
|
244
|
+
- Lock file works naturally: the child process writes the lock file as it does today.
|
|
245
|
+
|
|
246
|
+
**Tensions accepted:**
|
|
247
|
+
- Process management complexity: the daemon must wait for the console to start (port bound), handle crashes (restart or accept failure), and stop it on daemon shutdown.
|
|
248
|
+
- Dispatch from browser returns 503 (child process has no daemon handles). Unless Candidate C's proxy is also added.
|
|
249
|
+
- The `worktrain console` binary must be resolvable from the daemon process (path resolution, version alignment).
|
|
250
|
+
|
|
251
|
+
**Boundary:** Daemon startup code -- replace `startDaemonConsole()` call with `child_process.execFile('worktrain', ['console'])` and a port-wait loop.
|
|
252
|
+
|
|
253
|
+
**Failure mode to watch for:** Path resolution for `worktrain console` binary fails in some environments (e.g. npm global vs. local installs). Also: zombie child processes if daemon crashes without cleaning up.
|
|
254
|
+
|
|
255
|
+
**Relation to existing patterns:** Departs significantly. No subprocess management exists in the current daemon startup path. Would require substantial new error handling.
|
|
256
|
+
|
|
257
|
+
**Gains:** True process isolation. Console module scope is separate from daemon module scope.
|
|
258
|
+
**Gives up:** Process management complexity. Dispatch still 503s without Candidate C's proxy.
|
|
259
|
+
|
|
260
|
+
**Philosophy alignment:** "Make illegal states unrepresentable" (process boundaries enforce separation).
|
|
261
|
+
**Philosophy conflict:** "YAGNI" -- subprocess management is significant complexity for a feature (true process isolation) that achieves less than Candidate A at higher cost.
|
|
262
|
+
|
|
263
|
+
## Challenge Notes
|
|
264
|
+
|
|
265
|
+
_To be filled in Step 5 (hypothesis challenge)_
|
|
266
|
+
|
|
267
|
+
## Resolution Notes
|
|
268
|
+
|
|
269
|
+
### Final Direction: Candidate A (Delete daemon-console.ts) with amended 503 message
|
|
270
|
+
|
|
271
|
+
**Direction changed from review?** No. The review confirmed the selected direction and surfaced two concrete implementation details:
|
|
272
|
+
|
|
273
|
+
1. RC1 (no `worktrain dispatch` CLI): The original 503 message improvement referenced a CLI command that does not exist. The message must be updated to say something like "Browser dispatch requires the WorkTrain daemon context. Run `worktrain console` while the daemon is running to enable dispatch." No CLI reference.
|
|
274
|
+
|
|
275
|
+
2. RC2 (daemon log suggestion): The daemon startup log should add a hint line suggesting the user run `worktrain console` separately. Small addition to cli-worktrain.ts.
|
|
276
|
+
|
|
277
|
+
**What the review did NOT change:** The recommendation to delete daemon-console.ts remains correct. No RED findings. The ORANGE finding (503 message) and YELLOW findings (stale JSDoc, signal handler cleanup) are all implementation details, not direction changers.
|
|
278
|
+
|
|
279
|
+
**Confidence:** HIGH. The codebase evidence is unambiguous. standalone-console.ts is already the correct architecture.
|
|
280
|
+
|
|
281
|
+
## Decision Log
|
|
282
|
+
|
|
283
|
+
- 2026-04-21: Goal classified as solution_statement. Reframed to focus on live-reference coupling, not just import coupling. Path set to design_first.
|
|
284
|
+
- 2026-04-21: Final selection: Candidate A -- delete daemon-console.ts. Runner-up: Candidate C (HTTP proxy) as optional follow-on if owner needs live browser dispatch. No direction change from review. Review findings: ORANGE-O1 (503 message must be improved + no worktrain dispatch CLI exists), YELLOW-Y1 (stale JSDoc), YELLOW-Y2 (signal handler cleanup), YELLOW-Y3 (launchd docs).
|
|
285
|
+
|
|
286
|
+
## Final Summary
|
|
287
|
+
|
|
288
|
+
### Selected Direction: Candidate A -- Delete daemon-console.ts
|
|
289
|
+
|
|
290
|
+
**Problem:** `src/trigger/daemon-console.ts` is a redundant coupled server that imports live daemon handles (`TriggerRouter`, `SteerRegistry`, `PollingScheduler`). It prevents the systems from being independently restartable and creates a tight coupling that the project owner wants to eliminate.
|
|
291
|
+
|
|
292
|
+
**Key landscape finding:** `src/console/standalone-console.ts` ALREADY implements the target architecture -- zero daemon imports, filesystem-only, independently startable. `mountConsoleRoutes()` already has 503 fallbacks for all control endpoints when daemon handles are absent.
|
|
293
|
+
|
|
294
|
+
**Why Candidate A wins:** It deletes the root cause (the redundant coupled server) rather than patching it. The standalone console is already correct. Net result: ~220 lines deleted, zero lines written.
|
|
295
|
+
|
|
296
|
+
**Strongest alternative (Candidate C -- HTTP proxy):** Would become the recommendation if the owner needs live browser dispatch without running a separate console process. The proxy approach adds 3 routes to standalone-console.ts that forward control actions to daemon:3200 via HTTP fetch. Zero frontend changes needed.
|
|
297
|
+
|
|
298
|
+
**Why the runner-up lost:** It adds complexity (proxy routes, a new lock file convention, proxy failure modes) to solve a problem (browser dispatch returning 503) that may not be a real pain point for a single-owner workflow.
|
|
299
|
+
|
|
300
|
+
**Confidence: HIGH.** The codebase evidence is unambiguous.
|
|
301
|
+
|
|
302
|
+
**Residual risk:** The owner may use browser dispatch regularly. If so, Candidate C is the right follow-on after A.
|
|
303
|
+
|
|
304
|
+
### Implementation Plan (Candidate A)
|
|
305
|
+
|
|
306
|
+
Files to change:
|
|
307
|
+
|
|
308
|
+
1. **DELETE `src/trigger/daemon-console.ts`** (~220 lines)
|
|
309
|
+
2. **DELETE `tests/unit/daemon-console.test.ts`** (tests for deleted file)
|
|
310
|
+
3. **`src/cli-worktrain.ts` lines ~370-449:** Remove `startDaemonConsole` import, call, and `consoleHandle` variable; update signal handler to not call `consoleHandle.stop()`; add a log line like `[Daemon] Start the console with: worktrain console`
|
|
311
|
+
4. **`src/v2/usecases/console-routes.ts` line ~758:** Update the POST /api/v2/auto/dispatch 503 message to: "Browser dispatch requires the WorkTrain daemon context. Run 'worktrain console' while the daemon is running to enable dispatch."
|
|
312
|
+
5. **`src/trigger/trigger-listener.ts` JSDoc lines ~81-88:** Remove the stale reference to `startDaemonConsole` in the `steerRegistry` field JSDoc
|
|
313
|
+
6. **(Optional) `docs/configuration.md`:** Document that users must run `worktrain console` separately when running the daemon
|
|
314
|
+
|
|
315
|
+
### Pivot Condition
|
|
316
|
+
|
|
317
|
+
If the owner says "I use browser dispatch regularly and want it to work automatically" -- implement Candidate C (thin HTTP proxy in standalone-console.ts forwarding control actions to daemon:3200) as a follow-on after A.
|
|
318
|
+
|
|
319
|
+
### Design Documents
|
|
320
|
+
|
|
321
|
+
- `docs/design/console-daemon-separation-discovery.md` -- this file (discovery notes)
|
|
322
|
+
- `docs/design/design-candidates-console-daemon-separation.md` -- full candidate analysis with tradeoffs
|
|
323
|
+
- `docs/design/design-review-findings-console-daemon-separation.md` -- review findings (ORANGE/YELLOW)
|
|
@@ -98,7 +98,7 @@ These are all loaded in parallel (`Promise.all`) before the `Agent` is construct
|
|
|
98
98
|
### Current coordinator: what `pr-review.ts` spawns
|
|
99
99
|
|
|
100
100
|
`runPrReviewCoordinator` -> `spawnSession(workflowId, goal, workspace)` where:
|
|
101
|
-
- `workflowId`: `'mr-review
|
|
101
|
+
- `workflowId`: `'wr.mr-review'`
|
|
102
102
|
- `goal`: `'Review PR #N "title" before merge'`
|
|
103
103
|
- `workspace`: absolute path
|
|
104
104
|
|
|
@@ -522,7 +522,7 @@ A new `src/context-assembly/` module containing:
|
|
|
522
522
|
const bundle = deps.contextAssembler
|
|
523
523
|
? await deps.contextAssembler.assemble({ kind: 'pr_review', prNumber: pr.number, workspacePath: opts.workspace, payloadBody: pr.description })
|
|
524
524
|
: undefined;
|
|
525
|
-
const spawnResult = await deps.spawnSession('mr-review
|
|
525
|
+
const spawnResult = await deps.spawnSession('wr.mr-review', goal, opts.workspace, bundle);
|
|
526
526
|
```
|
|
527
527
|
|
|
528
528
|
#### Why this design was selected
|