@exaudeus/workrail 3.67.0 → 3.68.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/dist/application/services/compiler/template-registry.js +10 -1
  2. package/dist/cli/commands/worktrain-init.js +1 -1
  3. package/dist/console-ui/assets/{index-tOl8Vowf.js → index-DPdRJHMX.js} +1 -1
  4. package/dist/console-ui/index.html +1 -1
  5. package/dist/coordinators/modes/full-pipeline.js +4 -4
  6. package/dist/coordinators/modes/implement-shared.js +5 -5
  7. package/dist/coordinators/modes/implement.js +4 -4
  8. package/dist/coordinators/pr-review.js +4 -4
  9. package/dist/daemon/workflow-runner.d.ts +1 -0
  10. package/dist/daemon/workflow-runner.js +1 -0
  11. package/dist/manifest.json +31 -31
  12. package/dist/mcp/handlers/v2-context-budget.js +18 -0
  13. package/dist/mcp/handlers/v2-workflow.js +1 -1
  14. package/dist/mcp/workflow-protocol-contracts.js +2 -2
  15. package/dist/v2/durable-core/constants.d.ts +2 -0
  16. package/dist/v2/durable-core/constants.js +2 -1
  17. package/dist/v2/projections/session-metrics.js +1 -1
  18. package/docs/authoring-v2.md +4 -4
  19. package/docs/changelog-recent.md +3 -3
  20. package/docs/configuration.md +1 -1
  21. package/docs/design/adaptive-coordinator-context-candidates.md +1 -1
  22. package/docs/design/adaptive-coordinator-context.md +1 -1
  23. package/docs/design/adaptive-coordinator-routing-candidates.md +18 -18
  24. package/docs/design/adaptive-coordinator-routing-review.md +1 -1
  25. package/docs/design/adaptive-coordinator-routing.md +34 -34
  26. package/docs/design/agent-cascade-protocol.md +2 -2
  27. package/docs/design/console-daemon-separation-discovery.md +323 -0
  28. package/docs/design/context-assembly-design-candidates.md +1 -1
  29. package/docs/design/context-assembly-implementation-plan.md +1 -1
  30. package/docs/design/context-assembly-layer.md +2 -2
  31. package/docs/design/context-assembly-review-findings.md +1 -1
  32. package/docs/design/coordinator-access-audit.md +293 -0
  33. package/docs/design/coordinator-architecture-audit.md +62 -0
  34. package/docs/design/coordinator-error-handling-audit.md +240 -0
  35. package/docs/design/coordinator-testability-audit.md +426 -0
  36. package/docs/design/daemon-architecture-discovery.md +1 -1
  37. package/docs/design/daemon-console-separation-discovery.md +242 -0
  38. package/docs/design/daemon-memory-audit.md +203 -0
  39. package/docs/design/design-candidates-console-daemon-separation.md +256 -0
  40. package/docs/design/design-candidates-discovery-loop-fix.md +141 -0
  41. package/docs/design/design-review-findings-console-daemon-separation.md +106 -0
  42. package/docs/design/design-review-findings-discovery-loop-fix.md +81 -0
  43. package/docs/design/discovery-loop-fix-candidates.md +161 -0
  44. package/docs/design/discovery-loop-fix-design-review.md +106 -0
  45. package/docs/design/discovery-loop-fix-validation.md +258 -0
  46. package/docs/design/discovery-loop-investigation-A.md +188 -0
  47. package/docs/design/discovery-loop-investigation-B.md +287 -0
  48. package/docs/design/exploration-workflow-candidates.md +205 -0
  49. package/docs/design/exploration-workflow-design-review.md +166 -0
  50. package/docs/design/exploration-workflow-discovery.md +443 -0
  51. package/docs/design/ide-context-files-candidates.md +231 -0
  52. package/docs/design/ide-context-files-design-review.md +85 -0
  53. package/docs/design/ide-context-files.md +615 -0
  54. package/docs/design/implementation-plan-discovery-loop-fix.md +199 -0
  55. package/docs/design/implementation-plan-queue-poll-rotation.md +102 -0
  56. package/docs/design/in-process-http-audit.md +190 -0
  57. package/docs/design/layer3b-ghost-nodes-design-candidates.md +2 -2
  58. package/docs/design/loadSessionNotes-candidates.md +108 -0
  59. package/docs/design/loadSessionNotes-test-coverage-discovery.md +297 -0
  60. package/docs/design/loadSessionNotes-test-coverage-session4.md +209 -0
  61. package/docs/design/loadSessionNotes-test-coverage-v3.md +321 -0
  62. package/docs/design/probe-session-design-candidates.md +261 -0
  63. package/docs/design/probe-session-phase0.md +490 -0
  64. package/docs/design/routines-guide.md +7 -7
  65. package/docs/design/session-metrics-attribution-candidates.md +250 -0
  66. package/docs/design/session-metrics-attribution-design-review.md +115 -0
  67. package/docs/design/session-metrics-attribution-discovery.md +319 -0
  68. package/docs/design/session-metrics-candidates.md +227 -0
  69. package/docs/design/session-metrics-design-review.md +104 -0
  70. package/docs/design/session-metrics-discovery.md +454 -0
  71. package/docs/design/spawn-session-debug.md +202 -0
  72. package/docs/design/trigger-validator-candidates.md +214 -0
  73. package/docs/design/trigger-validator-review.md +109 -0
  74. package/docs/design/trigger-validator-shaping-phase0.md +239 -0
  75. package/docs/design/trigger-validator.md +454 -0
  76. package/docs/design/v2-core-design-locks.md +2 -2
  77. package/docs/design/workflow-extension-points.md +15 -15
  78. package/docs/design/workflow-id-validation-at-startup.md +1 -1
  79. package/docs/design/workflow-id-validation-implementation-plan.md +2 -2
  80. package/docs/design/workflow-trigger-lifecycle-audit.md +175 -0
  81. package/docs/design/worktrain-task-queue-candidates.md +5 -5
  82. package/docs/design/worktrain-task-queue.md +4 -4
  83. package/docs/discovery/coordinator-script-design.md +1 -1
  84. package/docs/discovery/coordinator-ux-discovery.md +3 -3
  85. package/docs/discovery/simulation-report.md +1 -1
  86. package/docs/discovery/workflow-modernization-discovery.md +326 -0
  87. package/docs/discovery/workflow-selection-for-discovery-tasks.md +33 -33
  88. package/docs/discovery/worktrain-status-briefing.md +1 -1
  89. package/docs/discovery/wr-discovery-goal-reframing.md +1 -1
  90. package/docs/docker.md +1 -1
  91. package/docs/ideas/backlog.md +227 -0
  92. package/docs/ideas/third-party-workflow-setup-design-thinking.md +1 -1
  93. package/docs/integrations/claude-code.md +5 -5
  94. package/docs/integrations/firebender.md +1 -1
  95. package/docs/plans/agentic-orchestration-roadmap.md +2 -2
  96. package/docs/plans/mr-review-workflow-redesign.md +9 -9
  97. package/docs/plans/ui-ux-workflow-design-candidates.md +4 -4
  98. package/docs/plans/ui-ux-workflow-discovery.md +2 -2
  99. package/docs/plans/workflow-categories-candidates.md +8 -8
  100. package/docs/plans/workflow-categories-discovery.md +4 -4
  101. package/docs/plans/workflow-modernization-design.md +430 -0
  102. package/docs/plans/workflow-staleness-detection-candidates.md +11 -11
  103. package/docs/plans/workflow-staleness-detection-review.md +4 -4
  104. package/docs/plans/workflow-staleness-detection.md +9 -9
  105. package/docs/plans/workrail-platform-vision.md +3 -3
  106. package/docs/reference/agent-context-cleaner-snippet.md +1 -1
  107. package/docs/reference/agent-context-guidance.md +4 -4
  108. package/docs/reference/context-optimization.md +2 -2
  109. package/docs/roadmap/now-next-later.md +2 -2
  110. package/docs/roadmap/open-work-inventory.md +16 -16
  111. package/docs/workflows.md +31 -31
  112. package/package.json +1 -1
  113. package/spec/workflow-tags.json +47 -47
  114. package/workflows/adaptive-ticket-creation.json +16 -16
  115. package/workflows/architecture-scalability-audit.json +22 -22
  116. package/workflows/bug-investigation.agentic.v2.json +3 -3
  117. package/workflows/classify-task-workflow.json +1 -1
  118. package/workflows/coding-task-workflow-agentic.json +6 -6
  119. package/workflows/cross-platform-code-conversion.v2.json +8 -8
  120. package/workflows/document-creation-workflow.json +8 -8
  121. package/workflows/documentation-update-workflow.json +8 -8
  122. package/workflows/intelligent-test-case-generation.json +2 -2
  123. package/workflows/learner-centered-course-workflow.json +2 -2
  124. package/workflows/mr-review-workflow.agentic.v2.json +4 -4
  125. package/workflows/personal-learning-materials-creation-branched.json +8 -8
  126. package/workflows/presentation-creation.json +5 -5
  127. package/workflows/production-readiness-audit.json +1 -1
  128. package/workflows/relocation-workflow-us.json +31 -31
  129. package/workflows/routines/context-gathering.json +1 -1
  130. package/workflows/routines/design-review.json +1 -1
  131. package/workflows/routines/execution-simulation.json +1 -1
  132. package/workflows/routines/feature-implementation.json +3 -3
  133. package/workflows/routines/final-verification.json +1 -1
  134. package/workflows/routines/hypothesis-challenge.json +1 -1
  135. package/workflows/routines/ideation.json +1 -1
  136. package/workflows/routines/parallel-work-partitioning.json +3 -3
  137. package/workflows/routines/philosophy-alignment.json +2 -2
  138. package/workflows/routines/plan-analysis.json +1 -1
  139. package/workflows/routines/plan-generation.json +1 -1
  140. package/workflows/routines/tension-driven-design.json +6 -6
  141. package/workflows/scoped-documentation-workflow.json +26 -26
  142. package/workflows/ui-ux-design-workflow.json +14 -14
  143. package/workflows/workflow-diagnose-environment.json +1 -1
  144. package/workflows/workflow-for-workflows.json +1 -1
@@ -22,7 +22,7 @@
22
22
 
23
23
  **Chosen path:** `design_first`
24
24
 
25
- **Rationale:** The goal was stated as a solution (a coordinator with a routing/classification layer). The risk is designing the wrong routing mechanism. The landscape is well-understood from existing code (`pr-review.ts`, `classify-task-workflow.json`). The dominant risk is not lack of knowledge -- it is solving the wrong subproblem (e.g., treating all routing as LLM classification when static heuristics cover most cases, or treating one monolithic script as the right shape when decomposition into per-mode coordinators may be cleaner).
25
+ **Rationale:** The goal was stated as a solution (a coordinator with a routing/classification layer). The risk is designing the wrong routing mechanism. The landscape is well-understood from existing code (`pr-review.ts`, `wr.classify-task.json`). The dominant risk is not lack of knowledge -- it is solving the wrong subproblem (e.g., treating all routing as LLM classification when static heuristics cover most cases, or treating one monolithic script as the right shape when decomposition into per-mode coordinators may be cleaner).
26
26
 
27
27
  ---
28
28
 
@@ -58,12 +58,12 @@ If a chat rewind occurs: the notes and context variables survive; this file may
58
58
 
59
59
  **What exists:**
60
60
  - `src/coordinators/pr-review.ts` -- 1462-line hardcoded coordinator for PR review. Establishes the `CoordinatorDeps` injectable interface (16 methods), `spawnSession`/`awaitSessions`/`getAgentResult` pattern, fix-agent loop with escalation-first failure policy.
61
- - `workflows/classify-task-workflow.json` -- EXISTS as of v3.40.0 (contrary to Apr 15 backlog entry that listed it as missing). Single LLM step, no tools, outputs 7 variables including `recommendedPipeline` (ordered workflow ID array with decision rules already encoded).
61
+ - `workflows/wr.classify-task.json` -- EXISTS as of v3.40.0 (contrary to Apr 15 backlog entry that listed it as missing). Single LLM step, no tools, outputs 7 variables including `recommendedPipeline` (ordered workflow ID array with decision rules already encoded).
62
62
  - `src/cli-worktrain.ts` -- wires `worktrain run pr-review` subcommand. No `worktrain run pipeline` or adaptive coordinator command exists yet.
63
63
  - `src/trigger/types.ts` -- `TriggerDefinition` has `workflowId`, `goal`, `goalTemplate`, `contextMapping`, `agentConfig`. No `pipelineMode` field.
64
- - Three-Workflow Pipeline decision (Apr 18): `wr.discovery -> wr.shaping -> coding-task-workflow-agentic`. Phase 0.5 in coding-task detects pitch.md and sets `solutionFixed=true` to skip design phases.
64
+ - Three-Workflow Pipeline decision (Apr 18): `wr.discovery -> wr.shaping -> wr.coding-task`. Phase 0.5 in coding-task detects pitch.md and sets `solutionFixed=true` to skip design phases.
65
65
  - `wr.shaping` and `wr.discovery` workflows both exist as of v3.40.0.
66
- - `coding-task-workflow-agentic` Phase 0.5 detects upstream context (pitch.md, BRD, PRD, etc.).
66
+ - `wr.coding-task` Phase 0.5 detects upstream context (pitch.md, BRD, PRD, etc.).
67
67
 
68
68
  **The Apr 15 backlog full pipeline DAG** (still relevant design intent):
69
69
  ```
@@ -91,13 +91,13 @@ trigger
91
91
 
92
92
  ### Contradictions and tensions
93
93
 
94
- - **classify-task-workflow is listed as NOT YET BUILT in the Apr 15 backlog** but the file `workflows/classify-task-workflow.json` exists today (v3.40.0, Apr 19). This is resolved: it was built between Apr 15 and Apr 19.
94
+ - **wr.classify-task is listed as NOT YET BUILT in the Apr 15 backlog** but the file `workflows/wr.classify-task.json` exists today (v3.40.0, Apr 19). This is resolved: it was built between Apr 15 and Apr 19.
95
95
  - **"Always run classify-task first"** (Apr 15 backlog) vs. **"Static heuristics for well-known cases"** (primary uncertainty). The Apr 15 backlog says "always" but this was written before Phase 0.5 upstream context detection was built. With Phase 0.5, many routing decisions can be made statically.
96
96
  - **`recommendedPipeline` from classify-task** includes `wr.discovery` for Medium/Large tasks, but the Three-Workflow Pipeline decision treats `wr.discovery` as optional. The coordinator must decide: use classify-task's `recommendedPipeline` verbatim, or treat it as a hint that can be overridden by static signals (e.g., pitch.md already present = skip discovery even if classify says Medium)?
97
97
 
98
98
  ### Evidence gaps
99
99
 
100
- 1. Does `spawn_agent` (the in-workflow tool) return the `recommendedPipeline` output variable from `classify-task-workflow`? The backlog note says `spawn_agent` currently does NOT return `artifacts` (limitation #5 in v3.40.0 current state). This means the coordinator script cannot use `spawn_agent` to run classify-task and read output -- it must use `spawnSession` + `getAgentResult` + parse the notes, just as `pr-review.ts` does for verdict artifacts.
100
+ 1. Does `spawn_agent` (the in-workflow tool) return the `recommendedPipeline` output variable from `wr.classify-task`? The backlog note says `spawn_agent` currently does NOT return `artifacts` (limitation #5 in v3.40.0 current state). This means the coordinator script cannot use `spawn_agent` to run classify-task and read output -- it must use `spawnSession` + `getAgentResult` + parse the notes, just as `pr-review.ts` does for verdict artifacts.
101
101
  2. No existing test harness for a multi-mode coordinator. `pr-review.ts` tests exist but only cover the review pipeline.
102
102
  3. The `worktrain-spawn.ts` CLI wiring for `spawnSession` is the only proven path to dispatch sessions from a coordinator script. No other dispatch mechanism has been tested.
103
103
 
@@ -122,7 +122,7 @@ trigger
122
122
 
123
123
  3. **Single coordinator file vs per-mode decomposition**: `pr-review.ts` is 1462 lines for one mode. A monolithic adaptive coordinator handling all modes risks becoming unmaintainable. Per-mode coordinator functions (each independently testable) with a thin routing dispatcher is a cleaner architecture -- but introduces coordination between files.
124
124
 
125
- 4. **`recommendedPipeline` verbatim vs as a hint**: classify-task-workflow encodes pipeline selection rules. If the coordinator uses these verbatim, it cannot apply static overrides (e.g., pitch.md present -> skip discovery). If it treats them as hints, it re-implements routing logic and classify-task's rules become advisory only.
125
+ 4. **`recommendedPipeline` verbatim vs as a hint**: wr.classify-task encodes pipeline selection rules. If the coordinator uses these verbatim, it cannot apply static overrides (e.g., pitch.md present -> skip discovery). If it treats them as hints, it re-implements routing logic and classify-task's rules become advisory only.
126
126
 
127
127
  5. **Phase 0.5 vs coordinator routing for upstream context**: coding-task already auto-detects pitch.md. So the coordinator's routing decision for "skip wr.shaping?" partially duplicates Phase 0.5's detection. The coordinator should route based on what phases to _spawn_, not what the coding workflow will internally skip -- but these can diverge (coordinator spawns shaping but coding-task's Phase 0.5 would have skipped it anyway).
128
128
 
@@ -130,8 +130,8 @@ trigger
130
130
 
131
131
  - [ ] A `worktrain run pipeline --task "fix the race condition in auth.ts"` command routes to the correct pipeline mode and logs the routing decision before spawning any sessions
132
132
  - [ ] A task with `#123` or `PR #123` in the goal routes to REVIEW_ONLY without spawning discovery or shaping sessions
133
- - [ ] A task with `pitch.md` present in the workspace routes to IMPLEMENT (coding-task-workflow-agentic only)
134
- - [ ] An ambiguous task (no static signal) routes to classify-task-workflow session, parses `recommendedPipeline`, and executes that pipeline
133
+ - [ ] A task with `pitch.md` present in the workspace routes to IMPLEMENT (wr.coding-task only)
134
+ - [ ] An ambiguous task (no static signal) routes to wr.classify-task session, parses `recommendedPipeline`, and executes that pipeline
135
135
  - [ ] A `dep bump` or `chore:` task routes to QUICK_REVIEW (mr-review only, no arch audit) based on goal text heuristics
136
136
  - [ ] Any phase failure produces a `PipelineOutcome` with `escalated: true` and a structured `escalationReason` -- no silent substitution
137
137
  - [ ] The `CoordinatorDeps` interface for the adaptive coordinator extends or reuses the existing `CoordinatorDeps` pattern from `pr-review.ts`
@@ -139,8 +139,8 @@ trigger
139
139
 
140
140
  ### Assumptions not yet verified
141
141
 
142
- 1. `classify-task-workflow` can be invoked via `spawnSession` + `awaitSessions` + `getAgentResult` with note parsing (same as pr-review reads verdict artifacts) -- this is assumed based on the spawn_agent artifact limitation
143
- 2. The `recommendedPipeline` text can be reliably parsed from classify-task-workflow's note output using a regex or structured block parser
142
+ 1. `wr.classify-task` can be invoked via `spawnSession` + `awaitSessions` + `getAgentResult` with note parsing (same as pr-review reads verdict artifacts) -- this is assumed based on the spawn_agent artifact limitation
143
+ 2. The `recommendedPipeline` text can be reliably parsed from wr.classify-task's note output using a regex or structured block parser
144
144
  3. A new CLI subcommand `worktrain run pipeline` can be added following the same pattern as `worktrain run pr-review` in `src/cli-worktrain.ts`
145
145
  4. Pipeline modes can be named and bounded at design time (not open-ended)
146
146
 
@@ -151,17 +151,17 @@ trigger
151
151
  ### HMW (How Might We) reframes
152
152
 
153
153
  - HMW make the pipeline mode explicit in the trigger config so routing is never ambiguous, while still supporting dynamic routing for ad-hoc CLI invocations?
154
- - HMW use classify-task-workflow's `recommendedPipeline` as the default while allowing static overrides to be applied on top, treating classification as advisory rather than authoritative?
154
+ - HMW use wr.classify-task's `recommendedPipeline` as the default while allowing static overrides to be applied on top, treating classification as advisory rather than authoritative?
155
155
 
156
156
  ### Primary uncertainty (updated)
157
157
 
158
- Can classify-task-workflow's `recommendedPipeline` output be used as the canonical routing source, with static overrides applied on top for well-known signal patterns (PR number, pitch.md, dep-bump keywords) -- rather than choosing between LLM and heuristics as mutually exclusive?
158
+ Can wr.classify-task's `recommendedPipeline` output be used as the canonical routing source, with static overrides applied on top for well-known signal patterns (PR number, pitch.md, dep-bump keywords) -- rather than choosing between LLM and heuristics as mutually exclusive?
159
159
 
160
160
  ### Known approaches
161
161
 
162
- 1. **classify-task-workflow first** -- always spawn a classification session, parse `recommendedPipeline`, then execute the pipeline. LLM-accurate, adds latency and cost per dispatch.
162
+ 1. **wr.classify-task first** -- always spawn a classification session, parse `recommendedPipeline`, then execute the pipeline. LLM-accurate, adds latency and cost per dispatch.
163
163
  2. **Static heuristics** -- parse goal text and trigger metadata (PR number present, labels, pitch.md present, explicit pipelineMode flag on trigger). Zero LLM cost, covers well-defined cases.
164
- 3. **Hybrid** -- static heuristics handle high-confidence cases; LLM classification handles ambiguous tasks. `classify-task-workflow` is an optional fast path, not always required.
164
+ 3. **Hybrid** -- static heuristics handle high-confidence cases; LLM classification handles ambiguous tasks. `wr.classify-task` is an optional fast path, not always required.
165
165
  4. **Explicit `pipelineMode` on trigger** -- add a `pipelineMode` field to `TriggerDefinition` (or as a context variable). Users/triggers declare mode explicitly. Removes ambiguity but requires configuration overhead.
166
166
  5. **classify-task advisory + static overrides** -- run classify-task first (small cost, accurate), then apply static override rules on top of `recommendedPipeline` to handle well-known signals. Classify sets the baseline; static rules correct known exceptions.
167
167
 
@@ -221,8 +221,8 @@ function routeTask(goal: string, workspace: string): PipelineMode
221
221
  **Per-mode pipeline sequences:**
222
222
  - `REVIEW_ONLY`: `mr-review-workflow.agentic.v2` -> route by verdict (clean: merge, minor: fix-agent-loop, blocking: escalate)
223
223
  - `QUICK_REVIEW`: same as REVIEW_ONLY but `agentConfig: { model: 'haiku-light' }`, no arch audit even if touched
224
- - `IMPLEMENT`: `coding-task-workflow-agentic` (Phase 0.5 finds pitch.md) -> `mr-review-workflow.agentic.v2` -> merge
225
- - `FULL`: `wr.discovery` -> `wr.shaping` -> `coding-task-workflow-agentic` -> PR -> `mr-review-workflow.agentic.v2` -> merge
224
+ - `IMPLEMENT`: `wr.coding-task` (Phase 0.5 finds pitch.md) -> `mr-review-workflow.agentic.v2` -> merge
225
+ - `FULL`: `wr.discovery` -> `wr.shaping` -> `wr.coding-task` -> PR -> `mr-review-workflow.agentic.v2` -> merge
226
226
 
227
227
  **Failure handling:** each phase failure returns a `PipelineOutcome` with `escalated: true` and `escalationReason`. No fallback to simpler pipeline. Same pattern as `PrOutcome` in pr-review.ts.
228
228
 
@@ -238,14 +238,14 @@ function routeTask(goal: string, workspace: string): PipelineMode
238
238
 
239
239
  ---
240
240
 
241
- ### Candidate B: classify-task-workflow as authoritative source (pure LLM routing)
241
+ ### Candidate B: wr.classify-task as authoritative source (pure LLM routing)
242
242
 
243
- **One-sentence summary:** The coordinator always spawns a `classify-task-workflow` session first, parses the `recommendedPipeline` output from step notes, and executes the pipeline that workflow specifies -- the coordinator script is a runner for whatever classify-task returns.
243
+ **One-sentence summary:** The coordinator always spawns a `wr.classify-task` session first, parses the `recommendedPipeline` output from step notes, and executes the pipeline that workflow specifies -- the coordinator script is a runner for whatever classify-task returns.
244
244
 
245
245
  **Architecture:**
246
246
  ```typescript
247
247
  async function routeTask(goal, workspace, deps): Promise<Result<readonly string[], string>> {
248
- const handle = await deps.spawnSession('classify-task-workflow', goal, workspace);
248
+ const handle = await deps.spawnSession('wr.classify-task', goal, workspace);
249
249
  const result = await deps.awaitSessions([handle], CLASSIFY_TIMEOUT_MS);
250
250
  const notes = await deps.getAgentResult(handle);
251
251
  return parseRecommendedPipeline(notes.recapMarkdown); // pure function, text block parser
@@ -257,15 +257,15 @@ async function routeTask(goal, workspace, deps): Promise<Result<readonly string[
257
257
 
258
258
  **Pipeline modes:** not named at the coordinator level -- the pipeline IS whatever classify-task returns. The coordinator just runs the sequence.
259
259
 
260
- **Failure handling:** if `parseRecommendedPipeline` fails (LLM deviated from format), default to `['wr.discovery', 'coding-task-workflow-agentic', 'mr-review-workflow.agentic.v2']`. Any spawned phase failure escalates with structured reason.
260
+ **Failure handling:** if `parseRecommendedPipeline` fails (LLM deviated from format), default to `['wr.discovery', 'wr.coding-task', 'mr-review-workflow.agentic.v2']`. Any spawned phase failure escalates with structured reason.
261
261
 
262
262
  **Tensions resolved:** intelligent routing for ambiguous tasks; single source of truth for pipeline selection rules (the workflow, not the coordinator).
263
263
  **Tensions accepted:** non-deterministic (same task may classify differently); adds 5-15 second LLM latency per dispatch; `recommendedPipeline` is a string array of workflow IDs, not a typed discriminated union.
264
264
  **Failure mode to watch:** coordinator runs `wr.discovery` unnecessarily for PR-only tasks if classify-task misclassifies them. Recovery: add static pre-check before spawning classify-task.
265
- **Follows:** classify-task-workflow's existing decision rules are already correct; this candidate delegates trust to them.
265
+ **Follows:** wr.classify-task's existing decision rules are already correct; this candidate delegates trust to them.
266
266
  **Gain:** routing rules live in the workflow, not the coordinator -- can be updated without code changes.
267
267
  **Give up:** determinism, routing transparency (routing reason requires parsing LLM output), typed pipeline modes.
268
- **Impact surface:** classify-task-workflow becomes a critical dependency -- format changes break coordinator.
268
+ **Impact surface:** wr.classify-task becomes a critical dependency -- format changes break coordinator.
269
269
  **Scope judgment:** Best-fit for teams that want routing rules to evolve without code deployment.
270
270
  **Philosophy:** Honors dependency injection (classify-task as a boundary). Conflicts with determinism-over-cleverness (LLM routing is clever but non-deterministic).
271
271
 
@@ -273,7 +273,7 @@ async function routeTask(goal, workspace, deps): Promise<Result<readonly string[
273
273
 
274
274
  ### Candidate C: static-first with LLM fallback (hybrid, recommended)
275
275
 
276
- **One-sentence summary:** A two-tier `routeTask()` applies static rules first (fast, deterministic, covers 80% of cases), then falls back to classify-task-workflow only for ambiguous tasks where no static signal fires.
276
+ **One-sentence summary:** A two-tier `routeTask()` applies static rules first (fast, deterministic, covers 80% of cases), then falls back to wr.classify-task only for ambiguous tasks where no static signal fires.
277
277
 
278
278
  **Architecture:**
279
279
  ```typescript
@@ -303,7 +303,7 @@ async function routeTask(goal, workspace, deps): Promise<Result<PipelineMode, st
303
303
  - `REVIEW_ONLY`: same as Candidate A
304
304
  - `QUICK_REVIEW`: same as Candidate A
305
305
  - `IMPLEMENT`: same as Candidate A
306
- - `FULL`: `wr.discovery` -> `wr.shaping` -> `coding-task-workflow-agentic` -> PR -> review -> merge
306
+ - `FULL`: `wr.discovery` -> `wr.shaping` -> `wr.coding-task` -> PR -> review -> merge
307
307
  - `CLASSIFY_AND_RUN`: execute phases from classify-task output in order; unknown workflow IDs escalate
308
308
 
309
309
  **Failure handling:** escalation-first, same as pr-review.ts. The routing failure (classify-task parse failure) produces ESCALATE mode with reason.
@@ -314,7 +314,7 @@ async function routeTask(goal, workspace, deps): Promise<Result<PipelineMode, st
314
314
  **Follows:** parseFindingsFromNotes two-tier strategy pattern. CoordinatorDeps injection for the LLM fallback path.
315
315
  **Gain:** fast for common cases, intelligent for ambiguous cases, deterministic for all named modes.
316
316
  **Give up:** complexity of two tiers; CLASSIFY_AND_RUN mode is not a named type with typed data.
317
- **Impact surface:** same as Candidate A plus classify-task-workflow dependency.
317
+ **Impact surface:** same as Candidate A plus wr.classify-task dependency.
318
318
  **Scope judgment:** Best-fit -- covers all named use cases efficiently. YAGNI risk is low because the LLM fallback adds ~30 lines of code, not a new architecture.
319
319
  **Philosophy:** Honors immutability, exhaustiveness (switch on PipelineMode is exhaustive), determinism-over-cleverness (static tier is deterministic, LLM is bounded fallback), errors-as-data.
320
320
 
@@ -421,7 +421,7 @@ Each mode coordinator is ~300-600 lines, fully independently testable. No mode-s
421
421
 
422
422
  ### Recommendation: C + E (Candidate C routing mechanism, Candidate E file architecture)
423
423
 
424
- **The routing mechanism decision (C):** Two-tier routing is the best-fit. Static rules cover the 4 well-defined cases (PR number, dep-bump, pitch.md, vague idea) without LLM cost. `CLASSIFY_AND_RUN` as the 5th mode handles genuinely ambiguous tasks via classify-task-workflow. This follows the `parseFindingsFromNotes` precedent in pr-review.ts (two-tier: structured first, fallback second).
424
+ **The routing mechanism decision (C):** Two-tier routing is the best-fit. Static rules cover the 4 well-defined cases (PR number, dep-bump, pitch.md, vague idea) without LLM cost. `CLASSIFY_AND_RUN` as the 5th mode handles genuinely ambiguous tasks via wr.classify-task. This follows the `parseFindingsFromNotes` precedent in pr-review.ts (two-tier: structured first, fallback second).
425
425
 
426
426
  **The architecture decision (E):** Per-mode coordinator files with a thin dispatcher is the correct architecture for 5 modes. Each mode file follows pr-review.ts independently. The dispatcher is the only code that changes when a new mode is added. This is how the codebase is already structured (pr-review.ts is one mode file) -- Candidate E just makes the pattern explicit.
427
427
 
@@ -447,7 +447,7 @@ Candidate D (pipelineMode in TriggerDefinition) would be justified if trigger op
447
447
 
448
448
  ### Pivot conditions
449
449
 
450
- - If `classify-task-workflow` note parsing proves unreliable (format drift), pivot to pure static (Candidate A) and accept that ambiguous tasks run FULL
450
+ - If `wr.classify-task` note parsing proves unreliable (format drift), pivot to pure static (Candidate A) and accept that ambiguous tasks run FULL
451
451
  - If `TriggerDefinition` change is needed for automated workflows, add Candidate D's pipelineMode field
452
452
  - If context-passing agent's design shows that the coordinator must inject structured context at spawn time, the mode coordinator files must include context injection logic -- this is implementation detail, not a routing design change
453
453
 
@@ -466,7 +466,7 @@ Candidate D (pipelineMode in TriggerDefinition) would be justified if trigger op
466
466
  1. **CLASSIFY_AND_RUN seam crack (genuine weakness, not blocking):** C's CLASSIFY_AND_RUN mode creates a typed/untyped seam in the dispatcher. Mitigation: CLASSIFY_AND_RUN fires only for tasks with no static signal; the dispatcher handles it with a dedicated `runClassifyAndRunPipeline` function that is documented as the "catch-all" path. Alternatively: fold CLASSIFY_AND_RUN into FULL (just run the three-workflow pipeline for all ambiguous tasks) and remove the LLM fallback entirely. This would make C = A for ambiguous tasks, simplifying the design.
467
467
  - **Final decision: simplify C by removing CLASSIFY_AND_RUN. Ambiguous tasks (no static signal) default to FULL. This gives Candidate A's simplicity with Candidate C's structure.**
468
468
 
469
- 2. **A is sufficient for MVP:** Challenge confirmed that Candidate A covers all 5 stated use cases. C adds value for future Medium tasks. For an MVP, A is correct. The recommended design IS essentially Candidate A + Candidate E architecture. No classify-task-workflow dependency at all for the initial implementation.
469
+ 2. **A is sufficient for MVP:** Challenge confirmed that Candidate A covers all 5 stated use cases. C adds value for future Medium tasks. For an MVP, A is correct. The recommended design IS essentially Candidate A + Candidate E architecture. No wr.classify-task dependency at all for the initial implementation.
470
470
 
471
471
  ### Final simplified design (A + E, not C + E)
472
472
 
@@ -489,7 +489,7 @@ Static rules (prioritized):
489
489
  3. `.workrail/current-pitch.md` exists -> `IMPLEMENT`
490
490
  4. else -> `FULL`
491
491
 
492
- **Why remove CLASSIFY_AND_RUN:** classify-task-workflow adds latency, non-determinism, and format-parsing fragility for no concrete benefit over FULL for the stated use cases. The "YAGNI with discipline" principle wins. If Medium tasks turn out to be wasteful with FULL, add classify-task as a future enhancement with a typed artifact (not text parsing).
492
+ **Why remove CLASSIFY_AND_RUN:** wr.classify-task adds latency, non-determinism, and format-parsing fragility for no concrete benefit over FULL for the stated use cases. The "YAGNI with discipline" principle wins. If Medium tasks turn out to be wasteful with FULL, add classify-task as a future enhancement with a typed artifact (not text parsing).
493
493
 
494
494
  **Architecture (E as designed):**
495
495
  ```
@@ -549,7 +549,7 @@ src/coordinators/
549
549
 
550
550
  1. **Routing determines spawn order, not context shape.** The routing layer (`routeTask()`) produces a `PipelineMode` variant. It does NOT know what context to pass to each spawned session. Context injection is entirely the responsibility of each mode coordinator (full-pipeline.ts, implement.ts, etc.), not the routing layer.
551
551
 
552
- 2. **FULL pipeline phase order is: `wr.discovery` -> `wr.shaping` -> `coding-task-workflow-agentic` -> review -> merge.** If the context-passing agent's design changes this order (e.g., by making shaping optional based on discovery findings), the `runFullPipeline()` function must be updated accordingly. The routing layer itself does not need to change.
552
+ 2. **FULL pipeline phase order is: `wr.discovery` -> `wr.shaping` -> `wr.coding-task` -> review -> merge.** If the context-passing agent's design changes this order (e.g., by making shaping optional based on discovery findings), the `runFullPipeline()` function must be updated accordingly. The routing layer itself does not need to change.
553
553
 
554
554
  3. **pitch.md is the canonical Shaping->Coding handoff.** The `IMPLEMENT` mode routes directly to coding because `current-pitch.md` already exists. The coding-task Phase 0.5 detects it and uses it. If the context-passing agent introduces a different handoff mechanism (e.g., coordinator-injected context instead of a file), the `IMPLEMENT` mode coordinator needs to inject that context at spawn time rather than relying on Phase 0.5 file detection.
555
555
 
@@ -582,8 +582,8 @@ The adaptive coordinator uses **pure static routing with per-mode file decomposi
582
582
  |------|---------------|
583
583
  | `REVIEW_ONLY` | `mr-review-workflow.agentic.v2` → verdict routing (clean: merge, minor: fix-loop, blocking: escalate) |
584
584
  | `QUICK_REVIEW` | same as REVIEW_ONLY with lighter model config |
585
- | `IMPLEMENT` | `coding-task-workflow-agentic` (Phase 0.5 reads pitch.md) → PR → `mr-review-workflow.agentic.v2` → merge |
586
- | `FULL` | `wr.discovery` → `wr.shaping` → `coding-task-workflow-agentic` → PR → `mr-review-workflow.agentic.v2` → merge |
585
+ | `IMPLEMENT` | `wr.coding-task` (Phase 0.5 reads pitch.md) → PR → `mr-review-workflow.agentic.v2` → merge |
586
+ | `FULL` | `wr.discovery` → `wr.shaping` → `wr.coding-task` → PR → `mr-review-workflow.agentic.v2` → merge |
587
587
 
588
588
  **File architecture (Candidate E):**
589
589
  ```
@@ -633,7 +633,7 @@ const COORDINATOR_MAX_MS = 120 * 60 * 1000; // 120 min total coordinator wa
633
633
  - Routing decision is logged as traceability JSON before any session spawn
634
634
  - FULL pipeline: each phase is an independent escalation point (discovery-fail, shaping-fail, coding-fail each escalate independently)
635
635
 
636
- **Why LLM classification (classify-task-workflow) was excluded:**
636
+ **Why LLM classification (wr.classify-task) was excluded:**
637
637
 
638
638
  After adversarial challenge, CLASSIFY_AND_RUN mode was removed. The LLM classification path adds non-determinism and format-parsing fragility (notes parsing vs typed artifact) for no concrete MVP benefit. All 5 stated use cases are covered by static rules. The upgrade path to add classify-task as a Tier 2 fallback exists when evidence shows >5% misrouting in production.
639
639
 
@@ -46,7 +46,7 @@ WorkRail defines three distinct tiers of execution. The system automatically sel
46
46
  How does WorkRail know which tier to use? It uses a **"Verify then Delegate"** pattern (The Probe Protocol).
47
47
 
48
48
  ### 1. The Boot Check (Diagnostic Phase)
49
- When a session starts (or via the `workflow-diagnose-environment` workflow), WorkRail guides the Main Agent to probe the environment:
49
+ When a session starts (or via the `wr.diagnose-environment` workflow), WorkRail guides the Main Agent to probe the environment:
50
50
 
51
51
  1. **Check for Subagents:** "Do you have a 'Researcher' subagent?"
52
52
  * *No:* **Fallback to Tier 1 (Solo).**
@@ -74,7 +74,7 @@ When executing a workflow step that calls for a specialized routine:
74
74
 
75
75
  To support this protocol, WorkRail provides:
76
76
 
77
- 1. **The Diagnostic Workflow:** A guided utility (`workflow-diagnose-environment.json`) to help users verify and configure their agents.
77
+ 1. **The Diagnostic Workflow:** A guided utility (`wr.diagnose-environment.json`) to help users verify and configure their agents.
78
78
  2. **The Asset Pack:** Standardized definitions for common roles (Researcher, Architect, Builder, Reviewer) that users can copy-paste into their IDE configs.
79
79
  * Includes System Prompts (for Tiers 1-3).
80
80
  * Includes Tool Whitelists (for enabling Tier 3).
@@ -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)
@@ -1,6 +1,6 @@
1
1
  # Design Candidates: Context Assembly Layer v1
2
2
 
3
- *Generated during coding-task-workflow-agentic session | 2026-04-19*
3
+ *Generated during wr.coding-task session | 2026-04-19*
4
4
 
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Implementation Plan: Context Assembly Layer v1
2
2
 
3
- *Authored during coding-task-workflow-agentic session | 2026-04-19*
3
+ *Authored during wr.coding-task session | 2026-04-19*
4
4
 
5
5
  ---
6
6
 
@@ -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-workflow-agentic'`
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-workflow-agentic', goal, opts.workspace, bundle);
525
+ const spawnResult = await deps.spawnSession('wr.mr-review', goal, opts.workspace, bundle);
526
526
  ```
527
527
 
528
528
  #### Why this design was selected