@exaudeus/workrail 3.40.0 → 3.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/init.js +0 -3
- package/dist/cli-worktrain.js +48 -11
- package/dist/cli.js +0 -18
- package/dist/config/app-config.d.ts +0 -16
- package/dist/config/app-config.js +0 -14
- package/dist/config/config-file.js +0 -3
- package/dist/console-ui/assets/index-DGj8EsFR.css +1 -0
- package/dist/console-ui/assets/index-DwfWMKvv.js +28 -0
- package/dist/console-ui/index.html +2 -2
- package/dist/context-assembly/deps.d.ts +8 -0
- package/dist/context-assembly/deps.js +2 -0
- package/dist/context-assembly/index.d.ts +6 -0
- package/dist/context-assembly/index.js +50 -0
- package/dist/context-assembly/infra.d.ts +3 -0
- package/dist/context-assembly/infra.js +154 -0
- package/dist/context-assembly/types.d.ts +30 -0
- package/dist/context-assembly/types.js +2 -0
- package/dist/coordinators/pr-review.d.ts +20 -1
- package/dist/coordinators/pr-review.js +189 -4
- package/dist/daemon/daemon-events.d.ts +9 -1
- package/dist/daemon/soul-template.d.ts +2 -2
- package/dist/daemon/soul-template.js +11 -1
- package/dist/daemon/workflow-runner.d.ts +14 -1
- package/dist/daemon/workflow-runner.js +406 -25
- package/dist/di/container.js +1 -25
- package/dist/di/tokens.d.ts +0 -3
- package/dist/di/tokens.js +0 -3
- package/dist/domain/execution/state.d.ts +6 -6
- package/dist/engine/engine-factory.js +0 -1
- package/dist/infrastructure/console-defaults.d.ts +1 -0
- package/dist/infrastructure/console-defaults.js +4 -0
- package/dist/infrastructure/session/index.d.ts +0 -1
- package/dist/infrastructure/session/index.js +1 -3
- package/dist/manifest.json +138 -122
- package/dist/mcp/handlers/session.d.ts +1 -0
- package/dist/mcp/handlers/session.js +61 -13
- package/dist/mcp/handlers/v2-workflow.d.ts +2 -2
- package/dist/mcp/output-schemas.d.ts +234 -234
- package/dist/mcp/server.js +1 -18
- package/dist/mcp/tools.d.ts +2 -2
- package/dist/mcp/transports/http-entry.js +0 -2
- package/dist/mcp/transports/stdio-entry.js +1 -2
- package/dist/mcp/types.d.ts +0 -2
- package/dist/mcp/v2/tools.d.ts +24 -24
- package/dist/trigger/daemon-console.d.ts +2 -0
- package/dist/trigger/daemon-console.js +1 -1
- package/dist/trigger/trigger-listener.d.ts +2 -0
- package/dist/trigger/trigger-listener.js +3 -1
- package/dist/trigger/trigger-router.d.ts +4 -3
- package/dist/trigger/trigger-router.js +4 -3
- package/dist/trigger/trigger-store.js +17 -4
- package/dist/v2/durable-core/schemas/artifacts/assessment.d.ts +2 -2
- package/dist/v2/durable-core/schemas/artifacts/coordinator-signal.d.ts +2 -2
- package/dist/v2/durable-core/schemas/artifacts/loop-control.d.ts +6 -6
- package/dist/v2/durable-core/schemas/artifacts/review-verdict.d.ts +6 -6
- package/dist/v2/durable-core/schemas/compiled-workflow/index.d.ts +56 -56
- package/dist/v2/durable-core/schemas/execution-snapshot/blocked-snapshot.d.ts +83 -83
- package/dist/v2/durable-core/schemas/execution-snapshot/execution-snapshot.v1.d.ts +1024 -1024
- package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +2336 -2336
- package/dist/v2/durable-core/schemas/session/dag-topology.d.ts +6 -6
- package/dist/v2/durable-core/schemas/session/events.d.ts +339 -339
- package/dist/v2/durable-core/schemas/session/gaps.d.ts +30 -30
- package/dist/v2/durable-core/schemas/session/manifest.d.ts +6 -6
- package/dist/v2/durable-core/schemas/session/outputs.d.ts +8 -8
- package/dist/v2/durable-core/schemas/session/validation-event.d.ts +3 -3
- package/dist/v2/usecases/console-routes.d.ts +2 -1
- package/dist/v2/usecases/console-routes.js +29 -5
- package/dist/v2/usecases/console-service.js +14 -0
- package/dist/v2/usecases/console-types.d.ts +1 -0
- package/docs/authoring.md +16 -16
- package/docs/design/context-assembly-design-candidates.md +199 -0
- package/docs/design/context-assembly-implementation-plan.md +211 -0
- package/docs/design/context-assembly-review-findings.md +112 -0
- package/docs/design/coordinator-message-queue-drain-plan.md +241 -0
- package/docs/design/coordinator-message-queue-drain-review.md +120 -0
- package/docs/design/coordinator-message-queue-drain.md +289 -0
- package/docs/design/shaping-workflow-external-research.md +119 -0
- package/docs/discovery/late-bound-goals-impl-plan.md +147 -0
- package/docs/discovery/late-bound-goals-review.md +82 -0
- package/docs/discovery/late-bound-goals.md +118 -0
- package/docs/discovery/steer-endpoint-design-candidates.md +288 -0
- package/docs/discovery/steer-endpoint-design-review-findings.md +104 -0
- package/docs/discovery/steer-endpoint-implementation-plan.md +284 -0
- package/docs/ideas/backlog.md +356 -0
- package/docs/ideas/design-candidates-console-session-tree-impl.md +64 -0
- package/docs/ideas/design-candidates-session-tree-view.md +196 -0
- package/docs/ideas/design-review-findings-console-session-tree-impl.md +75 -0
- package/docs/ideas/design-review-findings-session-tree-view.md +88 -0
- package/docs/ideas/implementation_plan_session_tree_view.md +238 -0
- package/package.json +2 -1
- package/spec/authoring-spec.json +16 -16
- package/spec/shape.schema.json +178 -0
- package/spec/workflow-tags.json +232 -47
- package/workflows/coding-task-workflow-agentic.json +491 -480
- package/workflows/wr.shaping.json +182 -0
- package/dist/console-ui/assets/index-8dh0Psu-.css +0 -1
- package/dist/console-ui/assets/index-CXWCAonr.js +0 -28
- package/dist/infrastructure/session/DashboardHeartbeat.d.ts +0 -8
- package/dist/infrastructure/session/DashboardHeartbeat.js +0 -39
- package/dist/infrastructure/session/DashboardLockRelease.d.ts +0 -2
- package/dist/infrastructure/session/DashboardLockRelease.js +0 -29
- package/dist/infrastructure/session/HttpServer.d.ts +0 -60
- package/dist/infrastructure/session/HttpServer.js +0 -912
- package/workflows/coding-task-workflow-agentic.lean.v2.json +0 -648
- package/workflows/coding-task-workflow-agentic.v2.json +0 -324
package/docs/ideas/backlog.md
CHANGED
|
@@ -5891,3 +5891,359 @@ Coordinator logic:
|
|
|
5891
5891
|
- Phase 1: coordinator scripts withhold `complete_step` advancement until the condition is met. This already works today -- the coordinator just doesn't advance the session until the fix agent is done.
|
|
5892
5892
|
- Phase 2: the coordinator passes structured context when advancing: `complete_step(session, { injectedContext: fixSummary })`. The session receives it as part of the next step's prompt.
|
|
5893
5893
|
- Phase 3: declarative pipelines -- workflow JSON declares that step N waits for an external condition before proceeding. The coordinator reads this and manages the timing automatically. No hand-coded coordinator script needed for common patterns.
|
|
5894
|
+
|
|
5895
|
+
---
|
|
5896
|
+
|
|
5897
|
+
### Coordinatable workflow steps: confirmation points the coordinator can satisfy (needs discovery, Apr 18, 2026)
|
|
5898
|
+
|
|
5899
|
+
⚠️ **Needs discovery before implementation. The questions below are open, not answered.**
|
|
5900
|
+
|
|
5901
|
+
**The insight:** workflows already have `requireConfirmation: true` on certain steps -- these are natural coordination points. Right now they pause for a human. The idea is to make them also pausable-for-a-coordinator, so a coordinator (or another agent) can be the one that responds instead of a human.
|
|
5902
|
+
|
|
5903
|
+
**The vision:**
|
|
5904
|
+
A workflow reaches a `requireConfirmation` step. In MCP mode (human-driven), it behaves exactly as today -- pauses and waits. In daemon/coordinator mode, instead of blocking forever, the coordinator can:
|
|
5905
|
+
- Inject a synthesized answer based on external work it just did ("architecture review found X, proceed with approach A")
|
|
5906
|
+
- Spawn another agent to generate the answer and inject its output
|
|
5907
|
+
- Ask a discovery agent to weigh in and forward the result
|
|
5908
|
+
- Simply forward a human's message from the message queue
|
|
5909
|
+
|
|
5910
|
+
The original session never knows whether a human or a coordinator satisfied the confirmation. It just receives the next turn with context.
|
|
5911
|
+
|
|
5912
|
+
**Why this is powerful:**
|
|
5913
|
+
Today the coordinator is external to the workflow -- it orchestrates sessions from outside. This makes the workflow itself coordinatable from within, so multi-agent collaboration can be declared in the workflow spec rather than bolted on in coordinator scripts.
|
|
5914
|
+
|
|
5915
|
+
**What's unknown and needs discovery:**
|
|
5916
|
+
1. **Mechanism:** is this an enriched `requireConfirmation` (add a `coordinatable: true` flag?), a new step type (`requireCoordinatorInput`?), or something at the engine level? Tradeoffs between each.
|
|
5917
|
+
2. **What gets injected:** always a structured decision ("proceed/revise/abort + findings"), or also data injection ("here are the file contents", "here's what the API returned")? How does the step receive it -- as a new tool call result, as a steer, as part of the step prompt?
|
|
5918
|
+
3. **Coordinator discovery:** how does the coordinator know a step is waiting for it vs waiting for a human? Does it poll the session state? Does the session emit a `coordinator_gate_pending` event? (This connects to the `waitForCoordinator` spec in this backlog.)
|
|
5919
|
+
4. **Timeout/fallback:** if the coordinator never responds, what happens? Fall back to human? Error? Configurable?
|
|
5920
|
+
5. **MCP invariant:** must behave identically to today in MCP/human-driven mode. The coordinator path is additive, not a behavior change for existing users.
|
|
5921
|
+
|
|
5922
|
+
**Relationship to other specs:**
|
|
5923
|
+
- "Long-running sessions: stay open across agent handoffs" -- the session pauses at the confirmation point, coordinator acts, session resumes
|
|
5924
|
+
- "POST /api/v2/sessions/:id/steer" -- this might be the injection mechanism
|
|
5925
|
+
- `signal_coordinator` tool -- the session might signal the coordinator instead of blocking
|
|
5926
|
+
- `waitForCoordinator` step flag (already in this backlog) -- same underlying need, different framing
|
|
5927
|
+
- "Coordinator review mode: self-healing vs comment-and-wait" -- confirmation points are where that routing decision gets expressed
|
|
5928
|
+
|
|
5929
|
+
---
|
|
5930
|
+
|
|
5931
|
+
## Architecture Decision: Three-Workflow Pipeline (Apr 18, 2026)
|
|
5932
|
+
|
|
5933
|
+
### Decision
|
|
5934
|
+
|
|
5935
|
+
The canonical WorkRail workflow pipeline for new features is:
|
|
5936
|
+
|
|
5937
|
+
```
|
|
5938
|
+
wr.discovery (optional) → wr.shaping (optional) → coding-task-workflow-agentic
|
|
5939
|
+
```
|
|
5940
|
+
|
|
5941
|
+
Each workflow is independently useful. The pipeline is an optional chain, not a required sequence.
|
|
5942
|
+
|
|
5943
|
+
### Rationale
|
|
5944
|
+
|
|
5945
|
+
**wr.discovery** produces a direction -- what problem is worth solving. Output: structured discovery notes at `.workrail/discovery/`.
|
|
5946
|
+
|
|
5947
|
+
**wr.shaping** produces a bounded pitch -- what specifically to build and explicitly NOT build, at a product level. Output: `.workrail/current-pitch.md`. Faithful Shape Up methodology. Tech-agnostic. No code-level content.
|
|
5948
|
+
|
|
5949
|
+
**coding-task-workflow-agentic** produces running code -- engineering approach, sliced implementation, verification. When pitch.md exists (Phase 0.5), it skips design ideation and translates the pitch directly into an engineering approach. The pitch's no-gos and appetite are binding constraints.
|
|
5950
|
+
|
|
5951
|
+
### No TechSpec workflow needed
|
|
5952
|
+
|
|
5953
|
+
The coding workflow already does everything a TechSpec workflow would do: Phase 1b generates design candidates, Phase 1c selects and challenges the approach, Phase 3 writes the spec and implementation plan. Adding a separate TechSpec workflow would duplicate this and create a question of which is canonical. The coding workflow is the engineering planning layer.
|
|
5954
|
+
|
|
5955
|
+
**The split that matters is product vs engineering:**
|
|
5956
|
+
- Product decisions (what to build, for whom, within what time) → wr.shaping
|
|
5957
|
+
- Engineering decisions (how to build it, which interfaces, which tests) → coding workflow
|
|
5958
|
+
|
|
5959
|
+
### When to skip shaping
|
|
5960
|
+
|
|
5961
|
+
- Task is small, concrete, and clearly scoped → go straight to coding workflow
|
|
5962
|
+
- Discovery already produced a bounded, implementable direction
|
|
5963
|
+
- You have a pre-written ticket or spec that already defines what to build
|
|
5964
|
+
|
|
5965
|
+
### Faithful Shape Up constraint
|
|
5966
|
+
|
|
5967
|
+
wr.shaping is tech-agnostic. A pitch for a Kotlin Android app and a pitch for a Python API service look structurally identical. No file paths, no function signatures, no implementation details. This makes pitches usable by human engineering teams at companies using Shape Up, not just WorkRail's coding workflow.
|
|
5968
|
+
|
|
5969
|
+
### Phase 0.5 mechanics
|
|
5970
|
+
|
|
5971
|
+
When `coding-task-workflow-agentic` finds `.workrail/current-pitch.md`:
|
|
5972
|
+
1. Reads all five pitch sections (Problem, Appetite, Solution/Elements, Rabbit Holes, No-Gos)
|
|
5973
|
+
2. Sets `shapedInputDetected=true`
|
|
5974
|
+
3. Skips phases 1a-1c (hypothesis, design generation, challenge-and-select)
|
|
5975
|
+
4. Phase 1d translates pitch elements/invariants/no-gos into an engineering approach
|
|
5976
|
+
5. Plan audit (Phase 4) checks for drift against the pitch
|
|
5977
|
+
6. Appetite is a hard ceiling -- oversized engineering work becomes follow-up tickets
|
|
5978
|
+
|
|
5979
|
+
|
|
5980
|
+
---
|
|
5981
|
+
|
|
5982
|
+
## Idea: `context-gather` Step Type (Apr 19, 2026)
|
|
5983
|
+
|
|
5984
|
+
### Problem
|
|
5985
|
+
|
|
5986
|
+
Phase 0.5 in the coding workflow currently looks for a shaped pitch by checking a local path. This doesn't handle: coordinator-injected context, manually written docs (GDoc, Confluence, Notion), Glean-indexed artifacts, or URLs embedded in the task description. The search logic is duplicated if other workflows need the same document.
|
|
5987
|
+
|
|
5988
|
+
### Proposed primitive
|
|
5989
|
+
|
|
5990
|
+
A new engine-level step type `context-gather` that resolves a named context artifact from ordered sources:
|
|
5991
|
+
|
|
5992
|
+
```json
|
|
5993
|
+
{
|
|
5994
|
+
"type": "context-gather",
|
|
5995
|
+
"id": "gather-pitch",
|
|
5996
|
+
"contextType": "shaped-pitch",
|
|
5997
|
+
"outputVar": "shapedInput",
|
|
5998
|
+
"optional": true,
|
|
5999
|
+
"sources": ["coordinator-injected", "local-paths", "task-url", "glean"]
|
|
6000
|
+
}
|
|
6001
|
+
```
|
|
6002
|
+
|
|
6003
|
+
**Source resolution order (stops at first hit):**
|
|
6004
|
+
1. `coordinator-injected` -- coordinator already attached context of this type to the session (most common in autonomous mode)
|
|
6005
|
+
2. `local-paths` -- check `.workrail/current-pitch.md`, `pitch.md`, `PRD.md`, `.workrail/pitches/` (most recent)
|
|
6006
|
+
3. `task-url` -- extract any URL from the task description and fetch via WebFetch or matching MCP (GDoc, Confluence, Notion)
|
|
6007
|
+
4. `glean` -- search Glean for recent docs matching the task keywords and `contextType`; opt-in only (risk of false positives silently constraining wrong scope)
|
|
6008
|
+
|
|
6009
|
+
If `optional: true` and no source resolves: `outputVar = null`, workflow continues normally.
|
|
6010
|
+
|
|
6011
|
+
### Why engine-level, not a routine
|
|
6012
|
+
|
|
6013
|
+
- Coordinator intercept requires the engine to check "has this type already been provided?" before running any search -- a routine can't express that
|
|
6014
|
+
- `contextType` is a declared intent multiple workflows can share (`wr.shaping`, `coding-task-workflow`, `wr.discovery`) without duplicating resolver logic
|
|
6015
|
+
- New sources (Linear, Jira, Notion) get added to the engine once, immediately available to all workflows
|
|
6016
|
+
|
|
6017
|
+
### Relationship to existing work
|
|
6018
|
+
|
|
6019
|
+
- Replaces/supersedes Phase 0.5's current local-path check in `coding-task-workflow-agentic`
|
|
6020
|
+
- Coordinator PR-review flow would inject `shaped-pitch` context before spawning the coding session
|
|
6021
|
+
- Any workflow that needs "find the spec/pitch/PRD for this task" uses the same step type
|
|
6022
|
+
|
|
6023
|
+
### Open questions
|
|
6024
|
+
|
|
6025
|
+
- How does the coordinator inject context into a session? Via a session variable set before `start_workflow`, or a new `inject_context` call?
|
|
6026
|
+
- How does `task-url` distinguish a GDoc URL from a Confluence URL from a Notion URL? MCP routing by domain?
|
|
6027
|
+
- What is the `contextType` vocabulary? Start with `shaped-pitch` -- what else? (`discovery-notes`, `design-spec`, `api-contract`?)
|
|
6028
|
+
- Glean false-positive risk: wrong document fed as shaped input silently constrains wrong scope. Needs confidence threshold or explicit user confirmation when Glean is the only hit.
|
|
6029
|
+
|
|
6030
|
+
|
|
6031
|
+
---
|
|
6032
|
+
|
|
6033
|
+
## Completed (Apr 19, 2026)
|
|
6034
|
+
|
|
6035
|
+
### wr.shaping -- Faithful Shape Up shaping workflow
|
|
6036
|
+
|
|
6037
|
+
Created `workflows/wr.shaping.json`. Faithful Shape Up methodology, tech-agnostic, produces `.workrail/current-pitch.md` only. Nine steps: ingest → frame gate → diverge (6 shapes, Verbalized Sampling) → converge → breadboard + elements → rabbit holes + no-gos → draft/critique loop → approval gate → write pitch.md. Two human gates with autonomous fallback. Appetite is calendar-time only (xs/s/m/l/xl). No code-level content -- a pitch for a Kotlin app and a pitch for a Python service look structurally identical.
|
|
6038
|
+
|
|
6039
|
+
### coding-task-workflow-agentic -- Upstream context Phase 0.5
|
|
6040
|
+
|
|
6041
|
+
Added Phase 0.5 "Locate Upstream Context" to `coding-task-workflow-agentic.json`. Format-agnostic: the agent uses whatever tools are available (repo search, WebFetch, Confluence/Notion/Glean MCPs, etc.) to find any upstream document -- pitch, PRD, BRD, RFC, design doc, user story, Jira epic, etc. Sets `upstreamSpecDetected` + `solutionFixed` flags. When `solutionFixed=true`, design ideation phases (1a-1c) are skipped and Phase 1d translates upstream constraints directly into an engineering approach. Plan audit (Phase 4) checks for drift against `upstreamBoundaries` whenever an upstream document was found.
|
|
6042
|
+
|
|
6043
|
+
Also consolidated from three workflow variants to one canonical file.
|
|
6044
|
+
|
|
6045
|
+
|
|
6046
|
+
---
|
|
6047
|
+
|
|
6048
|
+
## Current state update (Apr 19, 2026)
|
|
6049
|
+
|
|
6050
|
+
**npm version: v3.40.0**
|
|
6051
|
+
|
|
6052
|
+
### What shipped since v3.36.0 (Apr 18 -- Apr 19)
|
|
6053
|
+
|
|
6054
|
+
- ✅ **`wr.shaping`** -- faithful Shape Up shaping workflow (9 steps, two human gates with autonomous fallback)
|
|
6055
|
+
- ✅ **`coding-task-workflow-agentic` Phase 0.5** -- upstream context detection; skips design phases when solution is pre-specified. Three-workflow pipeline: shaping → discovery → coding.
|
|
6056
|
+
- ✅ **Coding workflow consolidated** -- from three variants (lean, full, lean.v2) to one canonical file.
|
|
6057
|
+
- ✅ **HttpServer removed from MCP server** (#601) -- pure stdio. MCP server can no longer accidentally start an HTTP server.
|
|
6058
|
+
- ✅ **Late-bound goals** (#604) -- `goalTemplate: "{{$.goal}}"` defaults for webhook-driven sessions. Goals can come from the payload, not just the static trigger definition.
|
|
6059
|
+
- ✅ **Coordinator message queue drain** (#606) -- `pr-review` coordinator reads `~/.workrail/message-queue.jsonl` before each spawn cycle. `worktrain tell stop`, `skip-pr <n>`, `add-pr <n>` work.
|
|
6060
|
+
- ✅ **Notifications shipped** -- `NotificationService` implemented, wired into `TriggerRouter` via `trigger-listener.ts`. `WORKTRAIN_NOTIFY_MACOS=true` and `WORKTRAIN_NOTIFY_WEBHOOK=<url>` in `~/.workrail/config.json`.
|
|
6061
|
+
- ✅ **`worktrain run pr-review`** -- fully wired coordinator command. `spawnSession` → `awaitSessions` → `getAgentResult` (session-wide artifact aggregation) → `parseFindingsFromNotes` → route by severity.
|
|
6062
|
+
- ✅ **`wr.review_verdict` artifact path** -- end-to-end wired: `mr-review-workflow.agentic.v2.json` phase-6 emits it, `artifact-contract-validator.ts` validates it at `continue_workflow` time, coordinator reads it with keyword-scan fallback.
|
|
6063
|
+
- ✅ **`worktrain logs` / `worktrain health`** -- structured daemon log tailing and per-session health summary. `worktrain status <id>` deprecated in favor of `worktrain health <id>`.
|
|
6064
|
+
- ✅ **`signal_coordinator` tool** -- agent can emit structured mid-session signals (`progress`, `finding`, `data_needed`, `approval_needed`, `blocked`) without advancing the step.
|
|
6065
|
+
- ✅ **`ChildWorkflowRunResult` + `assertNever`** -- spawn_agent delivery_failed bug fixed. `delivery_failed` impossible state is compile-time excluded.
|
|
6066
|
+
- ✅ **`lastStepArtifacts` on `WorkflowRunSuccess`** -- `onComplete` callback forwards artifacts alongside notes. Coordinator can read typed artifacts from result without a separate HTTP call.
|
|
6067
|
+
- ✅ **`steerRegistry` + POST `/sessions/:id/steer`** -- coordinator injection endpoint wired in daemon console. Running sessions register a steer callback; coordinators can inject mid-session messages via HTTP.
|
|
6068
|
+
- ✅ **GitHub polling adapters** -- `github_issues_poll` and `github_prs_poll` providers fully implemented alongside existing `gitlab_poll`.
|
|
6069
|
+
- ✅ **Knowledge graph spike** -- `src/knowledge-graph/` module: DuckDB in-memory + ts-morph indexer + two validation queries. NOT yet wired to an MCP tool (ts-morph in devDependencies).
|
|
6070
|
+
- ✅ **`worktrain daemon --install`** -- launchd plist creation, load, verify. Daemon survives MCP server reconnects.
|
|
6071
|
+
- ✅ **Performance sweep** -- April 2026 sweep identified 10 highest-leverage fixes, filed as issues #248-257. Not yet merged.
|
|
6072
|
+
|
|
6073
|
+
### Accurate limitations (as of v3.40.0)
|
|
6074
|
+
|
|
6075
|
+
1. **Console session tree UI not built** -- `parentSessionId` is stored in the `session_created` event and in `WorkflowRunSuccess`. Console `RunLineageDag` shows the per-session step DAG only. Cross-session parent-child tree is data-only. PRs #607 (tree view) and #608 (steer endpoint) are OPEN.
|
|
6076
|
+
2. **Daemon tool set is minimal** -- agent has: `complete_step`, `continue_workflow` (deprecated), `Bash`, `Read`, `Write`, `report_issue`, `spawn_agent`, `signal_coordinator`. No `Glob`, `Grep`, or `Edit`. Read/Write are thin wrappers.
|
|
6077
|
+
3. **`worktrain tell` messages only drained by coordinator** -- `drainMessageQueue` is called by `runPrReviewCoordinator`, not by the daemon loop. A running autonomous session cannot receive mid-run injections from `worktrain tell`. The `steerRegistry` HTTP endpoint is the mid-session channel.
|
|
6078
|
+
4. **Knowledge graph not wired** -- module exists, ts-morph must move to dependencies before an MCP tool can be built.
|
|
6079
|
+
5. **`spawn_agent` return missing `artifacts`** -- returns `{ childSessionId, outcome, notes }` only. Typed artifacts from child session are not surfaced to the parent agent. `lastStepArtifacts` on `WorkflowRunSuccess` exists but spawn_agent doesn't return it.
|
|
6080
|
+
6. **`worktrain inbox --watch` stub** -- `--watch` flag prints "not yet implemented" and exits.
|
|
6081
|
+
7. **Artifact store not built** -- agents still dump markdown/files directly into the repo. `~/.workrail/artifacts/` directory structure not created.
|
|
6082
|
+
8. **Performance issues not fixed** -- issues #248-257 filed from April sweep. `continue_workflow` triggers 6+ event log scans, full session rebuild per `/api/v2/sessions` request, N+1 workflow fetches, no caching.
|
|
6083
|
+
9. **No auto-commit** -- agents can write code but do not commit, push, or open PRs autonomously.
|
|
6084
|
+
10. **Assessment gates not battle-tested** -- end-to-end flow with `outputContract: required: true` not validated in production use.
|
|
6085
|
+
|
|
6086
|
+
### Open PRs to merge
|
|
6087
|
+
|
|
6088
|
+
- **#607** `feat(console): add session tree view for coordinator sessions` -- cross-session parent-child hierarchy in console. Blocked on: `parentSessionId` data is in store but console routes need to surface it.
|
|
6089
|
+
- **#608** `feat(console): add POST /api/v2/sessions/:sessionId/steer for coordinator injection` -- NOTE: this endpoint is already implemented in `daemon-console.ts` via `steerRegistry`. PR #608 may be adding this to the MCP server console separately. Check before merging.
|
|
6090
|
+
- **#610** `feat(workflows): add wr.shaping` -- the shaping workflow. Ready to merge.
|
|
6091
|
+
- **#587** `fix(mcp): add assertNever exhaustiveness guard to TriggerRouter` -- likely already applied in codebase (ChildWorkflowRunResult assertNever is live). May be a duplicate or different scope. Check.
|
|
6092
|
+
|
|
6093
|
+
### Next priorities (groomed Apr 19)
|
|
6094
|
+
|
|
6095
|
+
1. **Merge #610 (wr.shaping)** -- ready. Workflow is implemented and in the branch.
|
|
6096
|
+
2. **Merge #587 (TriggerRouter assertNever)** -- quick fix, check if still relevant.
|
|
6097
|
+
3. **Review and merge #607 + #608** -- console tree view and steer endpoint. Verify #608 doesn't duplicate what's already live in daemon-console.ts.
|
|
6098
|
+
4. **Performance fixes** -- issues #248-257. Pick highest-leverage first: SessionIndex (#248) and console projection cache (#249) eliminate most of the repeated scans.
|
|
6099
|
+
5. **Daemon tool set: add Glob + Grep** -- agents routinely need to search files. `Read` + `Bash` grep is slow and lossy. Native `Glob` and `Grep` tools would make coding sessions more reliable.
|
|
6100
|
+
6. **`spawn_agent` artifacts gap** -- add `artifacts?: readonly unknown[]` to the return value. `lastStepArtifacts` is already on `WorkflowRunSuccess`; wiring it through is ~30 LOC.
|
|
6101
|
+
7. **Knowledge graph wiring** -- move `ts-morph` and `@duckdb/node-api` to dependencies, add `query_knowledge_graph` MCP tool.
|
|
6102
|
+
8. **Artifact store foundation** -- `~/.workrail/artifacts/` directory, write path in `complete_step`.
|
|
6103
|
+
|
|
6104
|
+
---
|
|
6105
|
+
|
|
6106
|
+
### wr.shaping workflow: shape messy problems into implementation-ready specs (needs authoring, Apr 18, 2026)
|
|
6107
|
+
|
|
6108
|
+
**Status:** Design complete. Ready to author as a WorkRail workflow JSON.
|
|
6109
|
+
|
|
6110
|
+
**Design docs:**
|
|
6111
|
+
- `docs/design/shaping-workflow-discovery.md` -- WorkRail-internal discovery findings
|
|
6112
|
+
- `docs/design/shaping-workflow-external-research.md` -- External research synthesis (Shape Up, LLM failure modes, artifact schema)
|
|
6113
|
+
|
|
6114
|
+
**The gap this fills:** WorkRail has `wr.discovery` (divergent) and `coding-task-workflow-agentic` (convergent). Shaping is the missing middle -- converting messy discovery output into a bounded, implementation-ready spec without mid-implementation rabbit holes.
|
|
6115
|
+
|
|
6116
|
+
**The 11-step skeleton (see design doc for full detail):**
|
|
6117
|
+
1. ingest_and_extract -- extract problem frames, forces, open questions
|
|
6118
|
+
2. **frame_gate** -- MANDATORY HUMAN GATE: confirm problem + appetite
|
|
6119
|
+
3. diverge_solution_shapes -- 4 parallel rough shapes with varied framings
|
|
6120
|
+
4. converge_pick -- SEPARATE JUDGE (different model/prompt): pick best shape
|
|
6121
|
+
5. breadboard_and_elements -- fat-marker breadboard + Interface/Invariant/Exclusion classification
|
|
6122
|
+
6. rabbit_holes_nogos -- adversarial: risks, mitigations, no-gos, assumptions
|
|
6123
|
+
7. context_pack_build -- file globs, reuse_utilities, conventions, do-not-touch boundaries
|
|
6124
|
+
8. example_map_and_gherkin -- Given/When/Then acceptance criteria + verification commands
|
|
6125
|
+
9. draft_pitch -- self-refine ×2, SEPARATE CRITIC (obfuscated authorship)
|
|
6126
|
+
10. **approval_gate** -- MANDATORY HUMAN GATE: approve, edit, or restart
|
|
6127
|
+
11. finalize_and_handoff -- schema validation, emit shape.json + pitch.md
|
|
6128
|
+
|
|
6129
|
+
**The single most important design decision:** generator and critic run on structurally different prompts (ideally different model families). CoT and self-reflection alone do NOT mitigate anchoring or self-preference bias (Lou & Sun 2025; Panickssery et al. 2024).
|
|
6130
|
+
|
|
6131
|
+
**Output artifact:** `shape.json` -- contains problem story, appetite (multi-dimensional: calendar + tokens + turns + files), breadboard, elements, context_pack (file boundaries + reuse_utilities), Gherkin acceptance criteria, rabbit holes, no-gos, decomposition with walking skeleton, assumptions_log, build_readiness_score.
|
|
6132
|
+
|
|
6133
|
+
**Key insight for AI implementers:** LLMs need MORE explicit specs than humans on interfaces/invariants/file boundaries (no tacit knowledge, no scope-shame), but LESS explicit than junior humans on standard patterns. The dominant failure mode is confident architectural divergence -- working code that reinvents an existing utility. Context Pack (Step 7) directly prevents this.
|
|
6134
|
+
|
|
6135
|
+
**Next action:** author `wr.shaping` as a WorkRail workflow JSON using workflow-for-workflows, then update `coding-task-workflow-agentic` Phase 0 to detect and consume `shape.json` when present.
|
|
6136
|
+
|
|
6137
|
+
---
|
|
6138
|
+
|
|
6139
|
+
## Coordinator architecture: separation of concerns (Apr 19, 2026)
|
|
6140
|
+
|
|
6141
|
+
**Decision: defer knowledge graph implementation until the context assembly layer is designed.**
|
|
6142
|
+
|
|
6143
|
+
### The god class problem
|
|
6144
|
+
|
|
6145
|
+
`src/coordinators/pr-review.ts` is already ~500 LOC doing: session dispatch, result aggregation, finding classification, merge routing, message queue drain, and outbox writes. Adding knowledge graph queries, context bundle assembly, upstream doc fetching, and prior session lookups would make it a god class.
|
|
6146
|
+
|
|
6147
|
+
"Coordinator" is not a class or a script -- it is a **layer** that orchestrates across multiple concerns. Those concerns need to be separated before we add more to them.
|
|
6148
|
+
|
|
6149
|
+
### The right layering
|
|
6150
|
+
|
|
6151
|
+
```
|
|
6152
|
+
Trigger layer src/trigger/ receives events, validates, enqueues
|
|
6153
|
+
Dispatch layer (TBD) decides which workflow + what goal
|
|
6154
|
+
Context assembly (TBD) gathers and packages context before spawning
|
|
6155
|
+
Orchestration layer src/coordinators/ spawns, awaits, routes, retries, escalates
|
|
6156
|
+
Delivery layer src/trigger/delivery posts results back to origin systems
|
|
6157
|
+
```
|
|
6158
|
+
|
|
6159
|
+
**Context assembly** is the missing layer. Before dispatching a coding session, something needs to:
|
|
6160
|
+
- Run `buildIndex()` and query "what imports the file being changed"
|
|
6161
|
+
- Find the upstream pitch/PRD/BRD for the task
|
|
6162
|
+
- Pull relevant prior session notes
|
|
6163
|
+
- Package everything as a structured context bundle
|
|
6164
|
+
|
|
6165
|
+
This is NOT the orchestration script's job. The orchestration script should call `assembleContext(task, workspace)` and receive a bundle -- it should not know how that bundle was gathered.
|
|
6166
|
+
|
|
6167
|
+
### Why the knowledge graph belongs in context assembly, not in the daemon
|
|
6168
|
+
|
|
6169
|
+
Two options were considered:
|
|
6170
|
+
- **Daemon tool** (`makeQueryKnowledgeGraphTool` in `workflow-runner.ts`) -- agent queries mid-session on demand
|
|
6171
|
+
- **Coordinator pre-fetch** -- coordinator runs queries before spawning, injects answers as context
|
|
6172
|
+
|
|
6173
|
+
The coordinator pre-fetch is better for known patterns (e.g. "what imports the file being changed" before a coding task). The agent doesn't need to know the graph exists -- it just gets the relevant facts as context. This also avoids adding `ts-morph` + DuckDB to the production build.
|
|
6174
|
+
|
|
6175
|
+
The daemon tool approach is only better for ad-hoc mid-session queries the agent discovers dynamically. That's a secondary use case for v1.
|
|
6176
|
+
|
|
6177
|
+
### What to build before the knowledge graph
|
|
6178
|
+
|
|
6179
|
+
1. **Design the `ContextAssembler` abstraction** -- takes task description + workspace + trigger metadata, returns a structured context bundle. The knowledge graph is one of several sources (alongside upstream docs, prior session notes, repo state).
|
|
6180
|
+
2. **Refactor `pr-review.ts`** to use a `ContextAssembler` for the bits that fit there.
|
|
6181
|
+
3. **Then** implement knowledge graph as a `ContextAssembler` plugin -- not as a coordinator script addition and not as a daemon tool.
|
|
6182
|
+
|
|
6183
|
+
### Anti-pattern to avoid
|
|
6184
|
+
|
|
6185
|
+
Adding knowledge graph calls directly into `pr-review.ts` or any other coordinator script. That immediately creates the god class we're trying to avoid and couples the orchestration layer to a specific context source.
|
|
6186
|
+
|
|
6187
|
+
---
|
|
6188
|
+
|
|
6189
|
+
## Scheduled tasks (Apr 19, 2026)
|
|
6190
|
+
|
|
6191
|
+
**The idea:** WorkTrain runs tasks on a schedule -- not triggered by an external event, but by time. "Every Monday morning, run the code health scan." "Every night at 2am, check for new GitHub issues and triage them." "First of the month, run the production readiness audit."
|
|
6192
|
+
|
|
6193
|
+
### Why this matters for the autonomous pipeline vision
|
|
6194
|
+
|
|
6195
|
+
The full autonomous pipeline (prioritize → discover → shape → implement → test → PR → review → fix → merge) needs a way to start without a human pushing a button. Scheduled tasks are the trigger layer for proactive, time-driven work. Without them, WorkTrain is purely reactive -- it only acts when a webhook fires or a human dispatches it.
|
|
6196
|
+
|
|
6197
|
+
### What exists today
|
|
6198
|
+
|
|
6199
|
+
The trigger system (`src/trigger/`) supports `generic` (webhook) and polling providers (`gitlab_poll`, `github_issues_poll`, `github_prs_poll`). There is no native cron/schedule provider. The workaround today is OS crontab calling `curl` to fire a webhook.
|
|
6200
|
+
|
|
6201
|
+
### What to build
|
|
6202
|
+
|
|
6203
|
+
A `schedule` provider in triggers.yml:
|
|
6204
|
+
|
|
6205
|
+
```yaml
|
|
6206
|
+
triggers:
|
|
6207
|
+
- id: weekly-code-health
|
|
6208
|
+
provider: schedule
|
|
6209
|
+
cron: "0 9 * * 1" # every Monday at 9am
|
|
6210
|
+
workflowId: architecture-scalability-audit
|
|
6211
|
+
workspacePath: /path/to/repo
|
|
6212
|
+
goal: "Run weekly code health scan -- identify coupling violations, complexity hotspots, and performance anti-patterns introduced this week"
|
|
6213
|
+
|
|
6214
|
+
- id: nightly-issue-triage
|
|
6215
|
+
provider: schedule
|
|
6216
|
+
cron: "0 2 * * *" # every night at 2am
|
|
6217
|
+
workflowId: wr.discovery
|
|
6218
|
+
workspacePath: /path/to/repo
|
|
6219
|
+
goal: "Review open GitHub issues created in the last 24 hours and triage them: classify severity, identify duplicates, suggest which to prioritize"
|
|
6220
|
+
|
|
6221
|
+
- id: backlog-next-task
|
|
6222
|
+
provider: schedule
|
|
6223
|
+
cron: "0 8 * * 1-5" # weekday mornings at 8am
|
|
6224
|
+
workflowId: coding-task-workflow-agentic
|
|
6225
|
+
workspacePath: /path/to/repo
|
|
6226
|
+
goal: "Pick the highest-priority unstarted task from docs/ideas/backlog.md and implement it"
|
|
6227
|
+
```
|
|
6228
|
+
|
|
6229
|
+
### Key design decisions
|
|
6230
|
+
|
|
6231
|
+
- **Cron syntax**: standard 5-field cron (`min hour dom month dow`). Parsed by `node-cron` or equivalent -- already a pattern in the codebase (backlog mentions cron).
|
|
6232
|
+
- **Timezone**: configurable per trigger, defaults to system timezone. Important for "weekday morning" schedules that need to fire in the user's timezone.
|
|
6233
|
+
- **Missed runs**: if the daemon was down when a scheduled run should have fired, it does NOT catch up on missed runs by default. "Run at 9am Monday" means "run the next time 9am Monday arrives." Optional `catchUp: true` flag for cases where missing a run should be recovered.
|
|
6234
|
+
- **Overlap prevention**: if a scheduled run fires while the previous run is still active, it should be skipped (not queued). A `coding-task` that takes 2 hours should not spawn a second instance at the next cron tick.
|
|
6235
|
+
- **Manual trigger**: `worktrain run schedule <trigger-id>` to fire a scheduled trigger immediately without waiting for the cron time. Useful for testing.
|
|
6236
|
+
|
|
6237
|
+
### Integration with the autonomous pipeline
|
|
6238
|
+
|
|
6239
|
+
Scheduled tasks are the entry point for fully autonomous work:
|
|
6240
|
+
- "Every weekday morning, pick the next backlog item and run the full pipeline" -- this is how WorkTrain improves WorkTrain without any human input.
|
|
6241
|
+
- "Every time a PR is opened, run the MR review pipeline" -- this is github_prs_poll, already exists.
|
|
6242
|
+
- "Every Monday, run the architecture audit and file GitHub issues for findings" -- new scheduled capability.
|
|
6243
|
+
|
|
6244
|
+
### Implementation notes
|
|
6245
|
+
|
|
6246
|
+
- The `PollingScheduler` in `src/trigger/polling-scheduler.ts` already runs time-based loops for GitLab/GitHub polling. The schedule provider would be a similar loop, using cron expression matching instead of API polling.
|
|
6247
|
+
- `node-cron` or `croner` npm package for cron expression parsing and next-fire-time calculation. Lightweight, no daemon dependencies.
|
|
6248
|
+
- Scheduled triggers have no webhook payload -- `contextMapping` is empty, `goalTemplate` uses only static text or env vars.
|
|
6249
|
+
- The schedule state (last-fired-at per trigger) persists to `~/.workrail/schedule-state.json` so the daemon can detect missed runs on restart.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Design Candidates: Console Session Tree Implementation (Phase 3)
|
|
2
|
+
|
|
3
|
+
*2026-04-18 -- This document covers only the remaining Slice 5 (SessionTreeView UI component)*
|
|
4
|
+
*Phase 1 and Phase 2 artifacts: see design-candidates-session-tree-view.md and design-review-findings-session-tree-view.md*
|
|
5
|
+
|
|
6
|
+
## Problem Understanding
|
|
7
|
+
|
|
8
|
+
Slices 1-4 are implemented. The remaining work is Slice 5: add a SessionTreeView rendering path to SessionList.tsx.
|
|
9
|
+
|
|
10
|
+
**Tensions:**
|
|
11
|
+
- Expand toggle vs card navigation: two click targets on the same logical row. Resolved by a flex row with separate button elements.
|
|
12
|
+
- Per-coordinator expand state vs pure component: expand state lives in useState (UI state, not business logic -- correct placement).
|
|
13
|
+
- Auto-expand for in_progress: requires checking status in state initialization.
|
|
14
|
+
|
|
15
|
+
**Likely seam:** SessionList.tsx (presenter) + session-list-use-cases.ts (pure function buildSessionTree, already built).
|
|
16
|
+
|
|
17
|
+
**What makes it hard:** The expand toggle must be keyboard-navigable separately from the card AND must not trigger card navigation on click.
|
|
18
|
+
|
|
19
|
+
## Philosophy Constraints
|
|
20
|
+
|
|
21
|
+
- Pure presenter: no business logic in the component
|
|
22
|
+
- Immutability: expand state is a ReadonlyMap or regular Map in useState
|
|
23
|
+
- Functional/declarative: map SessionTreeNode[] to JSX
|
|
24
|
+
- Compose with small functions: SessionTreeView as a named function, separate from SessionList
|
|
25
|
+
|
|
26
|
+
## Impact Surface
|
|
27
|
+
|
|
28
|
+
- SessionList.tsx: adding viewMode branch
|
|
29
|
+
- session-list-use-cases.ts: already has buildSessionTree exported
|
|
30
|
+
- session-list-reducer.ts: already has viewMode + view_mode_changed
|
|
31
|
+
|
|
32
|
+
## Candidates
|
|
33
|
+
|
|
34
|
+
### Candidate A: SessionTreeView inline in SessionList.tsx (only candidate)
|
|
35
|
+
|
|
36
|
+
**Summary:** A `SessionTreeView` function component in SessionList.tsx takes `SessionTreeNode[]`, initializes expand state as `Map<string, boolean>` (auto-expand in_progress), and renders a flex row with [expand-toggle, coordinator-card] and children in a TreeLine wrapper below when expanded.
|
|
37
|
+
|
|
38
|
+
**Tensions resolved:** Expand/navigate separation (separate button elements). Accepts: expand state resets on navigation (transient UI state is acceptable).
|
|
39
|
+
|
|
40
|
+
**Boundary:** SessionList.tsx presenter layer.
|
|
41
|
+
|
|
42
|
+
**Failure mode:** Expand toggle accidentally triggers card navigation. Fixed by: expand toggle button is outside the coordinator ConsoleCard, not nested inside it.
|
|
43
|
+
|
|
44
|
+
**Repo pattern:** Follows SessionGroup component pattern in SessionList.tsx exactly.
|
|
45
|
+
|
|
46
|
+
**Gains:** Simple, pure, testable in isolation. **Loses:** Expand state resets when navigating away (transient).
|
|
47
|
+
|
|
48
|
+
**Scope:** Best-fit.
|
|
49
|
+
|
|
50
|
+
**Philosophy:** All principles honored.
|
|
51
|
+
|
|
52
|
+
## Comparison and Recommendation
|
|
53
|
+
|
|
54
|
+
Single candidate; no comparison needed. Candidate A is the correct approach.
|
|
55
|
+
|
|
56
|
+
## Self-Critique
|
|
57
|
+
|
|
58
|
+
Strongest counter-argument: expand state should be in the reducer (durable within page session). Counter-counter: expand state is UI state, not domain state. Reducer is for interaction state that needs to persist across renders (search, filter, sort, pagination). Expand state for individual coordinator rows is more like accordion state -- local useState is correct.
|
|
59
|
+
|
|
60
|
+
Pivot condition: if user feedback shows expand state loss is disruptive, move to reducer with `expanded_coordinators: ReadonlySet<string>` field.
|
|
61
|
+
|
|
62
|
+
## Open Questions for the Main Agent
|
|
63
|
+
|
|
64
|
+
None. Implementation is fully specified in docs/ideas/design-candidates-session-tree-view.md and the Phase 2 design spec.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Design Candidates: Session Tree View in Console
|
|
2
|
+
|
|
3
|
+
*Discovery session: 2026-04-18*
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Problem Understanding
|
|
8
|
+
|
|
9
|
+
### Core Tensions
|
|
10
|
+
|
|
11
|
+
1. **Flat API vs tree UI**: `/api/v2/sessions` returns a flat array. The UI wants a tree grouped by parent-child relationships. Options: build tree client-side from parentSessionId index, or change the API. The existing repo pattern (flat projection DTOs, pure use-case functions) favors building the tree client-side.
|
|
12
|
+
|
|
13
|
+
2. **Orphaned children vs tree integrity**: If a parent session is older than MAX_SESSIONS_TO_LOAD=500, its children have a dangling parentSessionId. Showing orphaned children as roots is the only safe fallback, but the tree is incomplete. Acceptable for MVP.
|
|
14
|
+
|
|
15
|
+
3. **Filtering with tree structure**: When filtering by status or search, should the parent appear if only a child matches? Naive filtering breaks the tree. Better approach: include parent when any child matches. Adds complexity to filterSessions().
|
|
16
|
+
|
|
17
|
+
4. **Type mirror sync**: ConsoleSessionSummary is duplicated between `src/v2/usecases/console-types.ts` (server) and `console/src/api/types.ts` (client mirror). Both must be updated in sync. This is an existing technical debt, not new -- adding parentSessionId just adds one more field to keep in sync.
|
|
18
|
+
|
|
19
|
+
### Likely Seam
|
|
20
|
+
|
|
21
|
+
The seam is between the flat sessions array and the tree render. The right place is a new pure function `buildSessionTree(sessions)` in `session-list-use-cases.ts`, not a new HTTP endpoint and not a modification to the GROUP_AXES grouping infrastructure.
|
|
22
|
+
|
|
23
|
+
### What Makes This Hard
|
|
24
|
+
|
|
25
|
+
The existing GROUP_AXES abstraction in `session-list-use-cases.ts` is flat -- each group has a string label. Tree grouping requires a fundamentally different rendering shape: a clickable coordinator SessionCard as the group header, with indented children below. Shoehorning tree rendering into GROUP_AXES produces an illegal state (coordinator appears as both a plain-text header label AND a card inside the group).
|
|
26
|
+
|
|
27
|
+
The filter-with-tree interaction is the hardest sub-problem: when tree mode is active, a filter that excludes the parent but matches a child must still show the parent as context.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Philosophy Constraints
|
|
32
|
+
|
|
33
|
+
From CLAUDE.md and repo patterns:
|
|
34
|
+
|
|
35
|
+
- **Immutability by default**: all new types use readonly fields
|
|
36
|
+
- **Pure functions in use-cases**: business logic in `session-list-use-cases.ts`, not in React components
|
|
37
|
+
- **Make illegal states unrepresentable**: coordinator session must not appear twice (as header AND as card)
|
|
38
|
+
- **Errors are data**: orphaned children (parent not in loaded set) should degrade gracefully to root-level display, never throw
|
|
39
|
+
- **YAGNI**: 2-level tree only for MVP; no recursive structure needed
|
|
40
|
+
- **Compose with small pure functions**: `buildSessionTree()` should be pure and independently testable
|
|
41
|
+
- **Validate at boundaries**: `extractParentSessionId()` happens in console-service.ts (the projection boundary); frontend trusts the value
|
|
42
|
+
|
|
43
|
+
**No philosophy conflicts found.** CLAUDE.md principles and repo patterns are consistent for this problem.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Impact Surface
|
|
48
|
+
|
|
49
|
+
If `ConsoleSessionSummary` gains a `parentSessionId` field:
|
|
50
|
+
- `src/v2/usecases/console-types.ts` -- server type definition
|
|
51
|
+
- `console/src/api/types.ts` -- client type mirror (must stay in sync manually)
|
|
52
|
+
- `projectSessionSummary()` in `console-service.ts` -- new field returned in the projection
|
|
53
|
+
- `filterSessions()` in `session-list-use-cases.ts` -- needs parentIndex for tree-mode filtering
|
|
54
|
+
- `SessionList.tsx` -- new tree rendering path
|
|
55
|
+
- Any future codegen for the type mirror would pick this up automatically
|
|
56
|
+
|
|
57
|
+
Existing consumers of the flat `/api/v2/sessions` endpoint are unaffected -- the new field is additive and optional for root sessions.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Candidates
|
|
62
|
+
|
|
63
|
+
### Candidate A -- Minimal: add parentSessionId, reuse GROUP_AXES with a 'tree' option
|
|
64
|
+
|
|
65
|
+
**Summary:** Add `parentSessionId` to both type files. Add a 'tree' option to GROUP_AXES that groups children under their parent's sessionId as the group key. The existing SessionGroup component shows the parent sessionId as the group label; children are SessionCards inside.
|
|
66
|
+
|
|
67
|
+
**Tensions resolved:** API stability (flat array unchanged). Type sync (one field added to both).
|
|
68
|
+
|
|
69
|
+
**Tensions accepted:** GROUP_AXES abstraction is abused. SessionGroup renders a plain-text label (the parent sessionId string), not a clickable coordinator SessionCard.
|
|
70
|
+
|
|
71
|
+
**Boundary solved at:** Frontend GROUP_AXES layer only.
|
|
72
|
+
|
|
73
|
+
**Why that boundary:** Minimum viable change -- no new components, no new functions.
|
|
74
|
+
|
|
75
|
+
**Failure mode:** Coordinator session appears BOTH as the group label text AND as a SessionCard inside the group (it's in the flat list). The group header is a non-navigable string, not a card. This is an illegal state: the coordinator is visible twice in different forms. Fixing this requires adding a custom node-renderer to SessionGroup -- at which point you've rebuilt Candidate B anyway.
|
|
76
|
+
|
|
77
|
+
**Repo pattern:** Abuses GROUP_AXES -- designed for grouping by string key, not parent-child hierarchies.
|
|
78
|
+
|
|
79
|
+
**Gains:** Zero new components. Minimal diff.
|
|
80
|
+
|
|
81
|
+
**Losses:** Visual quality. Coordinator not navigable from group header. No tree connector lines. Duplication bug.
|
|
82
|
+
|
|
83
|
+
**Scope:** Too narrow -- doesn't deliver the tree view quality described in the backlog.
|
|
84
|
+
|
|
85
|
+
**Philosophy:** YAGNI honored. Make illegal states unrepresentable violated (coordinator appears twice).
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
### Candidate B -- Best-fit: new buildSessionTree() + dedicated SessionTreeView component
|
|
90
|
+
|
|
91
|
+
**Summary:** Add `parentSessionId: string | null` to `ConsoleSessionSummary` on server and client. Implement `extractParentSessionId(events)` in `console-service.ts` (O(1), session_created is always eventIndex=0). Add a new pure function to `session-list-use-cases.ts`:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
interface SessionTreeNode {
|
|
95
|
+
readonly session: ConsoleSessionSummary;
|
|
96
|
+
readonly children: readonly ConsoleSessionSummary[];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
interface SessionTree {
|
|
100
|
+
readonly roots: readonly SessionTreeNode[];
|
|
101
|
+
readonly orphanChildIds: ReadonlySet<string>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function buildSessionTree(sessions: readonly ConsoleSessionSummary[]): SessionTree
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Add a view mode toggle (tree/flat) to `SessionListState`. When tree mode is active, render a new `SessionTreeView` component: coordinator cards at root level, children indented 20px with a CSS `border-left` connector line on the wrapper div. Orphans (parent not loaded) appear as roots.
|
|
108
|
+
|
|
109
|
+
Modify `filterSessions()` to accept an optional `parentIndex: ReadonlyMap<string, string>` parameter. When tree mode is active: if a child matches the filter, include its parent too (parent appears even if it doesn't match the filter text/status).
|
|
110
|
+
|
|
111
|
+
**Tensions resolved:** API stability. Tree rendering quality (no duplication). Orphan handling (explicit orphanChildIds set). Filter+tree interaction (parent included when child matches).
|
|
112
|
+
|
|
113
|
+
**Tensions accepted:** Type mirror sync remains manual.
|
|
114
|
+
|
|
115
|
+
**Boundary solved at:** Frontend use-cases layer (`buildSessionTree()` is pure) + new presenter component.
|
|
116
|
+
|
|
117
|
+
**Why that boundary:** `buildSessionTree()` is pure and testable without React. The tree is computed once per render cycle, not per-card. Follows the exact same pattern as the existing pure functions in `session-list-use-cases.ts`.
|
|
118
|
+
|
|
119
|
+
**Failure mode:** Filter-with-tree is the hardest case. The `filterSessions()` modification adds complexity. If the logic is wrong, the parent could be shown when it shouldn't (e.g., filter=complete, parent is in_progress, child is complete -- should the in_progress parent appear?). Decision: include parent when any child matches, regardless of parent's own status. This is the most useful behavior for the tree view use case.
|
|
120
|
+
|
|
121
|
+
**Repo pattern:** Follows the pure use-cases + presenter pattern exactly. New SessionTreeView component follows the same presenter shape as existing components.
|
|
122
|
+
|
|
123
|
+
**Gains:** Clean tree rendering with visual connectors. Navigable coordinator cards. No duplication. Pure testable logic. Degrades gracefully for orphaned children.
|
|
124
|
+
|
|
125
|
+
**Losses:** Slightly more code than Candidate A. Two components for the same view (flat list + tree view).
|
|
126
|
+
|
|
127
|
+
**Scope:** Best-fit -- delivers the design described in the backlog without overbuilding.
|
|
128
|
+
|
|
129
|
+
**Philosophy:** All principles honored. Immutability, explicit domain types, pure functions, YAGNI (2-level tree only).
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### Candidate C -- Server-side tree: new `GET /api/v2/sessions/tree` endpoint
|
|
134
|
+
|
|
135
|
+
**Summary:** Add a new server endpoint that returns `{ roots: ConsoleSessionSummaryWithChildren[], orphans: ConsoleSessionSummary[] }`. The flat `/api/v2/sessions` endpoint is unchanged. Server builds the tree from the loaded session set, embedding children under their parent in the response.
|
|
136
|
+
|
|
137
|
+
**Tensions resolved:** Tree structure computed at the source of truth (server, with access to the full 500-session window). Client receives a ready-to-render tree.
|
|
138
|
+
|
|
139
|
+
**Tensions accepted:** New endpoint means new React Query hook, new cache key, new loading state. Two overlapping endpoints that must be kept consistent.
|
|
140
|
+
|
|
141
|
+
**Boundary solved at:** Server projection layer (`console-service.ts`).
|
|
142
|
+
|
|
143
|
+
**Why that boundary:** Server has the full session set in scope; client doesn't need to rebuild the tree from a flat list.
|
|
144
|
+
|
|
145
|
+
**Failure mode:** API surface grows. The flat endpoint and tree endpoint must stay consistent. Cache invalidation logic must be updated for both. If the tree endpoint is slow (500 sessions), the flat endpoint is still fast -- users may not understand why.
|
|
146
|
+
|
|
147
|
+
**Repo pattern:** Departs from the existing pattern (all console endpoints return flat projection DTOs). Adds server complexity for a problem that client-side pure functions can solve with zero HTTP overhead.
|
|
148
|
+
|
|
149
|
+
**Gains:** Tree is always consistent (parent and children computed together). Client rendering is simpler -- just map the tree response.
|
|
150
|
+
|
|
151
|
+
**Losses:** API surface growth. Additional React Query hook. More complex cache invalidation. Server-side complexity for a problem solvable client-side.
|
|
152
|
+
|
|
153
|
+
**Scope:** Too broad -- the flat list already contains all the data needed to build the tree client-side.
|
|
154
|
+
|
|
155
|
+
**Philosophy:** Conflicts with YAGNI (new endpoint not needed). Conflicts with validate-at-boundaries (boundary moved to server when client can handle it).
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Comparison and Recommendation
|
|
160
|
+
|
|
161
|
+
| Tension | A | B | C |
|
|
162
|
+
|---|---|---|---|
|
|
163
|
+
| API stability | Resolves | Resolves | Adds new endpoint |
|
|
164
|
+
| Tree rendering quality | Fails (duplication) | Resolves | Resolves |
|
|
165
|
+
| Orphan handling | None | Explicit | Server-side |
|
|
166
|
+
| Filter+tree interaction | Broken | Manageable | Handled server-side |
|
|
167
|
+
| Repo pattern fit | Abuses GROUP_AXES | Follows pure-function pattern | Departs from flat-DTO pattern |
|
|
168
|
+
| Reversibility | Easy | Easy | Medium |
|
|
169
|
+
| Philosophy fit | Partial | Full | Partial |
|
|
170
|
+
|
|
171
|
+
**Recommendation: Candidate B.**
|
|
172
|
+
|
|
173
|
+
B resolves all real tensions without overbuilding. It follows the existing pure-function/presenter split the repo already practices. `buildSessionTree()` is pure and testable independently of React. The filter-with-tree interaction is manageable with a contained change to `filterSessions()`. The 2-level tree constraint matches the backlog's examples exactly.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Self-Critique
|
|
178
|
+
|
|
179
|
+
**Strongest argument against B:** What if the Phase 2 UX design requires 3-level trees (coordinator → child coordinator → grandchild)? B explicitly excludes this by using `readonly children: readonly ConsoleSessionSummary[]` instead of `readonly children: readonly SessionTreeNode[]`. Changing to recursive SessionTreeNode later is a contained change, but it would require updating the SessionTreeView component too.
|
|
180
|
+
|
|
181
|
+
**Pivot conditions:**
|
|
182
|
+
- If Phase 2 UX requires >2 levels: change `SessionTreeNode.children` to `readonly SessionTreeNode[]` and make `SessionTreeView` recursive. The `buildSessionTree()` function would need to become recursive as well.
|
|
183
|
+
- If filter+tree interaction proves too complex: ship tree view without filter support in tree mode, show a "tree view disabled while filtered" state.
|
|
184
|
+
- If the type mirror sync problem grows: introduce codegen for the type mirror (separate from this feature).
|
|
185
|
+
|
|
186
|
+
**Assumption that would invalidate B:** If the flat `/api/v2/sessions` endpoint doesn't return all sessions needed to build a complete tree (e.g., if sessions are paginated server-side before reaching the client). Currently MAX_SESSIONS_TO_LOAD=500 applies before the response; if a coordinator and its children are all within the 500-session window, the tree will be complete. If the coordinator is old but children are recent, the tree will be incomplete -- but this degrades gracefully (children show as roots).
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Open Questions for the Main Agent
|
|
191
|
+
|
|
192
|
+
1. **Filter behavior in tree mode**: When filtering by status=in_progress and the coordinator is complete but has an in_progress child -- should the coordinator appear? Proposed: yes, include parent when any child matches. Is this the right UX?
|
|
193
|
+
|
|
194
|
+
2. **Tree mode default or opt-in**: Should the tree view be the default mode, or should users opt in via a toggle? Given that zero sessions have parentSessionId today, defaulting to tree view would show an identical flat list -- tree mode only activates when coordinator sessions exist.
|
|
195
|
+
|
|
196
|
+
3. **Connector line style**: Simple `border-left` CSS on the indented container, or tree-line SVG connectors like `TreeLine.tsx` (which already exists in `console/src/components/TreeLine.tsx`)? The existing component should be used if it fits.
|