@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.
Files changed (105) hide show
  1. package/dist/cli/commands/init.js +0 -3
  2. package/dist/cli-worktrain.js +48 -11
  3. package/dist/cli.js +0 -18
  4. package/dist/config/app-config.d.ts +0 -16
  5. package/dist/config/app-config.js +0 -14
  6. package/dist/config/config-file.js +0 -3
  7. package/dist/console-ui/assets/index-DGj8EsFR.css +1 -0
  8. package/dist/console-ui/assets/index-DwfWMKvv.js +28 -0
  9. package/dist/console-ui/index.html +2 -2
  10. package/dist/context-assembly/deps.d.ts +8 -0
  11. package/dist/context-assembly/deps.js +2 -0
  12. package/dist/context-assembly/index.d.ts +6 -0
  13. package/dist/context-assembly/index.js +50 -0
  14. package/dist/context-assembly/infra.d.ts +3 -0
  15. package/dist/context-assembly/infra.js +154 -0
  16. package/dist/context-assembly/types.d.ts +30 -0
  17. package/dist/context-assembly/types.js +2 -0
  18. package/dist/coordinators/pr-review.d.ts +20 -1
  19. package/dist/coordinators/pr-review.js +189 -4
  20. package/dist/daemon/daemon-events.d.ts +9 -1
  21. package/dist/daemon/soul-template.d.ts +2 -2
  22. package/dist/daemon/soul-template.js +11 -1
  23. package/dist/daemon/workflow-runner.d.ts +14 -1
  24. package/dist/daemon/workflow-runner.js +406 -25
  25. package/dist/di/container.js +1 -25
  26. package/dist/di/tokens.d.ts +0 -3
  27. package/dist/di/tokens.js +0 -3
  28. package/dist/domain/execution/state.d.ts +6 -6
  29. package/dist/engine/engine-factory.js +0 -1
  30. package/dist/infrastructure/console-defaults.d.ts +1 -0
  31. package/dist/infrastructure/console-defaults.js +4 -0
  32. package/dist/infrastructure/session/index.d.ts +0 -1
  33. package/dist/infrastructure/session/index.js +1 -3
  34. package/dist/manifest.json +138 -122
  35. package/dist/mcp/handlers/session.d.ts +1 -0
  36. package/dist/mcp/handlers/session.js +61 -13
  37. package/dist/mcp/handlers/v2-workflow.d.ts +2 -2
  38. package/dist/mcp/output-schemas.d.ts +234 -234
  39. package/dist/mcp/server.js +1 -18
  40. package/dist/mcp/tools.d.ts +2 -2
  41. package/dist/mcp/transports/http-entry.js +0 -2
  42. package/dist/mcp/transports/stdio-entry.js +1 -2
  43. package/dist/mcp/types.d.ts +0 -2
  44. package/dist/mcp/v2/tools.d.ts +24 -24
  45. package/dist/trigger/daemon-console.d.ts +2 -0
  46. package/dist/trigger/daemon-console.js +1 -1
  47. package/dist/trigger/trigger-listener.d.ts +2 -0
  48. package/dist/trigger/trigger-listener.js +3 -1
  49. package/dist/trigger/trigger-router.d.ts +4 -3
  50. package/dist/trigger/trigger-router.js +4 -3
  51. package/dist/trigger/trigger-store.js +17 -4
  52. package/dist/v2/durable-core/schemas/artifacts/assessment.d.ts +2 -2
  53. package/dist/v2/durable-core/schemas/artifacts/coordinator-signal.d.ts +2 -2
  54. package/dist/v2/durable-core/schemas/artifacts/loop-control.d.ts +6 -6
  55. package/dist/v2/durable-core/schemas/artifacts/review-verdict.d.ts +6 -6
  56. package/dist/v2/durable-core/schemas/compiled-workflow/index.d.ts +56 -56
  57. package/dist/v2/durable-core/schemas/execution-snapshot/blocked-snapshot.d.ts +83 -83
  58. package/dist/v2/durable-core/schemas/execution-snapshot/execution-snapshot.v1.d.ts +1024 -1024
  59. package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +2336 -2336
  60. package/dist/v2/durable-core/schemas/session/dag-topology.d.ts +6 -6
  61. package/dist/v2/durable-core/schemas/session/events.d.ts +339 -339
  62. package/dist/v2/durable-core/schemas/session/gaps.d.ts +30 -30
  63. package/dist/v2/durable-core/schemas/session/manifest.d.ts +6 -6
  64. package/dist/v2/durable-core/schemas/session/outputs.d.ts +8 -8
  65. package/dist/v2/durable-core/schemas/session/validation-event.d.ts +3 -3
  66. package/dist/v2/usecases/console-routes.d.ts +2 -1
  67. package/dist/v2/usecases/console-routes.js +29 -5
  68. package/dist/v2/usecases/console-service.js +14 -0
  69. package/dist/v2/usecases/console-types.d.ts +1 -0
  70. package/docs/authoring.md +16 -16
  71. package/docs/design/context-assembly-design-candidates.md +199 -0
  72. package/docs/design/context-assembly-implementation-plan.md +211 -0
  73. package/docs/design/context-assembly-review-findings.md +112 -0
  74. package/docs/design/coordinator-message-queue-drain-plan.md +241 -0
  75. package/docs/design/coordinator-message-queue-drain-review.md +120 -0
  76. package/docs/design/coordinator-message-queue-drain.md +289 -0
  77. package/docs/design/shaping-workflow-external-research.md +119 -0
  78. package/docs/discovery/late-bound-goals-impl-plan.md +147 -0
  79. package/docs/discovery/late-bound-goals-review.md +82 -0
  80. package/docs/discovery/late-bound-goals.md +118 -0
  81. package/docs/discovery/steer-endpoint-design-candidates.md +288 -0
  82. package/docs/discovery/steer-endpoint-design-review-findings.md +104 -0
  83. package/docs/discovery/steer-endpoint-implementation-plan.md +284 -0
  84. package/docs/ideas/backlog.md +356 -0
  85. package/docs/ideas/design-candidates-console-session-tree-impl.md +64 -0
  86. package/docs/ideas/design-candidates-session-tree-view.md +196 -0
  87. package/docs/ideas/design-review-findings-console-session-tree-impl.md +75 -0
  88. package/docs/ideas/design-review-findings-session-tree-view.md +88 -0
  89. package/docs/ideas/implementation_plan_session_tree_view.md +238 -0
  90. package/package.json +2 -1
  91. package/spec/authoring-spec.json +16 -16
  92. package/spec/shape.schema.json +178 -0
  93. package/spec/workflow-tags.json +232 -47
  94. package/workflows/coding-task-workflow-agentic.json +491 -480
  95. package/workflows/wr.shaping.json +182 -0
  96. package/dist/console-ui/assets/index-8dh0Psu-.css +0 -1
  97. package/dist/console-ui/assets/index-CXWCAonr.js +0 -28
  98. package/dist/infrastructure/session/DashboardHeartbeat.d.ts +0 -8
  99. package/dist/infrastructure/session/DashboardHeartbeat.js +0 -39
  100. package/dist/infrastructure/session/DashboardLockRelease.d.ts +0 -2
  101. package/dist/infrastructure/session/DashboardLockRelease.js +0 -29
  102. package/dist/infrastructure/session/HttpServer.d.ts +0 -60
  103. package/dist/infrastructure/session/HttpServer.js +0 -912
  104. package/workflows/coding-task-workflow-agentic.lean.v2.json +0 -648
  105. package/workflows/coding-task-workflow-agentic.v2.json +0 -324
@@ -0,0 +1,75 @@
1
+ # Design Review Findings: Console Session Tree Implementation (Phase 3)
2
+
3
+ *2026-04-18 -- Covering Slice 5: SessionTreeView component*
4
+
5
+ ---
6
+
7
+ ## Tradeoff Review
8
+
9
+ | Tradeoff | Acceptable? | Condition for Failure | Notes |
10
+ |---|---|---|---|
11
+ | Transient expand state (resets on navigation) | Yes | Never -- no acceptance criterion requires persistence | Acceptable for MVP |
12
+ | Auto-expand only on initial render | Yes | User expects newly-in_progress coordinators to auto-expand during the page session | Known limitation, acceptable |
13
+ | Expand toggle outside ConsoleCard | Yes | Layout mismatch -- toggle disconnected from card visually | WorkspaceView.tsx proves the flex-row pattern works |
14
+
15
+ ---
16
+
17
+ ## Failure Mode Review
18
+
19
+ | Failure Mode | Handled? | Mitigation | Risk |
20
+ |---|---|---|---|
21
+ | Expand toggle triggers card navigation | Yes | Toggle is separate DOM button outside ConsoleCard | None |
22
+ | Coordinator with no children shows toggle | Yes | Hide toggle when children.length === 0 | None |
23
+ | cycle in parentSessionId | Yes | buildSessionTree() cycle guard | None |
24
+ | Newly in_progress coordinator won't auto-expand after initial render | Partial | Accepted for MVP; useState init only covers initial render | Low |
25
+ | TypeScript type errors | None expected | All types already defined in Slices 1-4 | None |
26
+
27
+ ---
28
+
29
+ ## Runner-Up / Simpler Alternative Review
30
+
31
+ **Simpler variant adopted:** Skip auto-expand entirely, or keep auto-expand for initial render only. Decision: keep auto-expand on initial render (simple useState initializer), accept that mid-session state changes won't auto-expand. Avoids useEffect/useMemo complexity.
32
+
33
+ ---
34
+
35
+ ## Philosophy Alignment
36
+
37
+ | Principle | Status |
38
+ |---|---|
39
+ | Pure functions in use-cases | Satisfied -- buildSessionTree() is pure |
40
+ | Immutability | Satisfied -- readonly types throughout |
41
+ | Compose with small functions | Satisfied -- SessionTreeView is a named function |
42
+ | YAGNI | Satisfied -- auto-expand is minimal |
43
+ | Functional/declarative | Acceptable tension -- useState Map is mutable but correct for React UI state |
44
+
45
+ ---
46
+
47
+ ## Findings
48
+
49
+ ### Red (must fix before implementation)
50
+
51
+ None.
52
+
53
+ ### Orange (should address)
54
+
55
+ **O1: Expand toggle placement requires specific layout**
56
+ The expand toggle must be in a flex row with the coordinator card, but the coordinator card itself must not be a parent of the toggle (avoids nested interactive elements). The pattern from WorkspaceView.tsx is `<div className="flex items-start gap-2">` containing `[toggle button]` then `[coordinator card button]`.
57
+
58
+ ### Yellow (advisory)
59
+
60
+ **Y1: Auto-expand fires only on initial render**
61
+ If a coordinator transitions to in_progress after the component first renders, it will not auto-expand. This is acceptable for MVP.
62
+
63
+ ---
64
+
65
+ ## Recommended Revisions
66
+
67
+ 1. Use `flex items-start gap-2` wrapper row for [expand-toggle, coordinator-card] to avoid nested interactive elements.
68
+ 2. Initialize expand state with `useState(() => new Map(roots.filter(...).map(n => [n.session.sessionId, true])))` -- function initializer to avoid re-running on re-renders.
69
+
70
+ ---
71
+
72
+ ## Residual Concerns
73
+
74
+ - Visual quality of the amber left border + [COORD] badge combination requires human visual review -- cannot be verified by TypeScript check.
75
+ - No real coordinator sessions exist locally, so end-to-end visual testing requires either manual mock data or running a spawn_agent workflow.
@@ -0,0 +1,88 @@
1
+ # Design Review Findings: Session Tree View in Console
2
+
3
+ *Discovery session: 2026-04-18*
4
+
5
+ ---
6
+
7
+ ## Tradeoff Review
8
+
9
+ | Tradeoff | Violates Acceptance Criteria? | Conditions for Failure | Verdict |
10
+ |---|---|---|---|
11
+ | Manual type mirror sync | No -- TypeScript catches at build time | Developer forgets to update client mirror | Acceptable -- compiler enforces |
12
+ | Incomplete tree when parent outside 500-session window | No -- degrades to orphan-as-root | Coordinator runs many children over days | Acceptable for MVP |
13
+ | 2-level tree max | No -- engine depth limit aligns (maxSubagentDepth=2) | If maxSubagentDepth raised in config | Acceptable -- contained change to fix |
14
+ | Tree mode opt-in (not default) | No -- flat view is current default | Users never discover toggle | Acceptable -- can add auto-suggest later |
15
+
16
+ ---
17
+
18
+ ## Failure Mode Review
19
+
20
+ | Failure Mode | Handled? | Missing Mitigation | Risk |
21
+ |---|---|---|---|
22
+ | Cyclic parentSessionId (self-parent) | Partial | Explicit cycle guard in buildSessionTree(): `if (parentId === session.sessionId) treat as root` | Low -- easy to add, must add |
23
+ | Orphaned child (parent outside window) | Yes | Optional: 'child session' badge. Low priority. | Low |
24
+ | Filter shows no results in tree mode | Yes -- existing empty-state UI handles it | None needed | Low |
25
+ | Tree toggle state lost on navigation | Not handled | Persist in sessionStorage or URL param. Low priority. | Low |
26
+ | Client mirror type not updated | Yes -- TypeScript build fails | None needed | None at runtime |
27
+
28
+ ---
29
+
30
+ ## Runner-Up / Simpler Alternative Review
31
+
32
+ **Runner-up (C, server-side tree):** No elements worth borrowing. Client already has all 500 sessions; server-side computation adds API surface with no benefit.
33
+
34
+ **Simpler variant adopted:** Tree mode and filter mode are mutually exclusive for MVP. When tree mode is active, status/search filters are disabled (or auto-cleared). This eliminates the filter+tree parentIdIndex complexity from filterSessions() entirely. The more complex filter-aware tree can be added later when coordinator sessions are common enough to justify it.
35
+
36
+ ---
37
+
38
+ ## Philosophy Alignment
39
+
40
+ **Satisfied:** Immutability, make illegal states unrepresentable, compose with small pure functions, validate at boundaries, errors as data, YAGNI.
41
+
42
+ **Under tension (acceptable):**
43
+ - Functional/declarative: buildSessionTree() is imperative-style but pure. Same-input/same-output invariant holds.
44
+ - Type mirror sync: two copies of ConsoleSessionSummary can diverge, but TypeScript build catches this before runtime.
45
+
46
+ **No risky philosophy tensions.**
47
+
48
+ ---
49
+
50
+ ## Findings
51
+
52
+ ### Red (must fix before implementation)
53
+
54
+ None.
55
+
56
+ ### Orange (should fix before implementation)
57
+
58
+ **O1: Cycle guard missing in design**
59
+ buildSessionTree() must guard against `parentSessionId === session.sessionId`. Without this, a session appears both as a root and as its own child. Add explicitly to implementation.
60
+
61
+ ### Yellow (low priority, note for future)
62
+
63
+ **Y1: Orphaned child has no visual indicator**
64
+ Children with a dangling parentSessionId (parent outside 500-session window) show as roots with no indication they're children. A small 'child session' badge would improve UX for multi-day coordinator runs. Not needed for MVP.
65
+
66
+ **Y2: Tree toggle state not persisted**
67
+ Navigating to session detail and back resets tree/flat mode. Could be persisted in sessionStorage or URL param. Not needed for MVP.
68
+
69
+ **Y3: Tree mode not auto-suggested when coordinator sessions exist**
70
+ Users may not discover the tree mode toggle. When any session has `parentSessionId != null`, show a subtle prompt or auto-activate tree mode. Not needed for MVP.
71
+
72
+ ---
73
+
74
+ ## Recommended Revisions
75
+
76
+ 1. **Add cycle guard to buildSessionTree():** Before adding a session to a parent's children array, check `parentSessionId !== session.sessionId`. Treat self-parenting sessions as roots.
77
+
78
+ 2. **Make filter mode and tree mode mutually exclusive:** When tree mode is activated, clear active filters (or disable the filter controls). When a filter is applied, tree mode is disabled. Show a clear UI state for this (e.g., "Filters disabled in tree view").
79
+
80
+ 3. **Document 2-level max as explicit constant:** Define `TREE_MAX_DEPTH = 2` in session-list-use-cases.ts with a comment explaining the engine's maxSubagentDepth alignment.
81
+
82
+ ---
83
+
84
+ ## Residual Concerns
85
+
86
+ 1. **Untestable in real UI until coordinator sessions are run.** Zero sessions have parentSessionId today. The implementation can be verified only via manual testing with artificially constructed session data or by running an actual spawn_agent workflow. Recommendation: add a simple unit test for buildSessionTree() with mock ConsoleSessionSummary objects in session-list-use-cases.test.ts (if the test file exists -- check before implementing).
87
+
88
+ 2. **Filter+tree interaction deferred, not designed.** The current design explicitly excludes filtering while in tree mode. If a future sprint adds filter+tree, the parentIdIndex approach was analyzed and is the right path -- but it must be designed and tested carefully to avoid surprising UX (parent shown in 'complete' filter even though it's in_progress).
@@ -0,0 +1,238 @@
1
+ # Implementation Plan: Session Tree View in Console
2
+
3
+ *Created: 2026-04-18*
4
+
5
+ ---
6
+
7
+ ## Problem Statement
8
+
9
+ WorkRail creates a separate session for every workflow run. When spawn_agent is used, a coordinator session creates multiple child sessions. Today, all sessions appear as a flat list in the console -- there is no visual grouping that shows coordinator-child relationships. The `parentSessionId` field exists in the `session_created` event schema and is written by `makeSpawnAgentTool`, but it is not surfaced in `ConsoleSessionSummary` or rendered in the console UI.
10
+
11
+ ---
12
+
13
+ ## Acceptance Criteria
14
+
15
+ 1. `ConsoleSessionSummary` includes a `parentSessionId: string | null` field. Root sessions have `null`; child sessions have the parent's session ID.
16
+ 2. `/api/v2/sessions` response includes `parentSessionId` on each session summary.
17
+ 3. The console SessionList includes a tree view mode toggle.
18
+ 4. When tree view is active: coordinator sessions (sessions that have children) display their child sessions indented below them, separated by a `TreeLine` connector.
19
+ 5. When tree view is active: filter controls are disabled with a clear UI indicator.
20
+ 6. When tree view is active: sessions with no children display identically to the flat view.
21
+ 7. Orphaned children (parent session not in the loaded 500-session set) display as root sessions in tree view.
22
+ 8. `buildSessionTree()` handles the case where `parentSessionId === sessionId` (self-parent) by treating the session as a root.
23
+ 9. TypeScript build passes with no new errors.
24
+
25
+ ---
26
+
27
+ ## Non-Goals
28
+
29
+ - No new HTTP endpoints (flat `/api/v2/sessions` is unchanged except for the new field).
30
+ - No multi-level (depth > 2) tree rendering for MVP.
31
+ - No filter+tree interaction for MVP (tree mode and filter mode are mutually exclusive).
32
+ - No pagination changes.
33
+ - No persistence of tree view toggle state.
34
+ - No automatic activation of tree mode.
35
+
36
+ ---
37
+
38
+ ## Philosophy-Driven Constraints
39
+
40
+ - **Immutability by default**: all new interfaces use `readonly`.
41
+ - **Pure functions in use-cases**: `buildSessionTree()` is a pure function with no side effects, in `session-list-use-cases.ts`. No business logic in React components.
42
+ - **Make illegal states unrepresentable**: coordinator session appears exactly once (as a root card). Cycle guard prevents self-parenting.
43
+ - **Explicit domain types**: `SessionTree` and `SessionTreeNode` are named interfaces, not anonymous objects.
44
+ - **YAGNI**: 2-level tree, mutually exclusive filter/tree modes, no new endpoints.
45
+ - **Errors as data**: `buildSessionTree()` returns an `orphanChildIds` set; never throws on missing parents.
46
+
47
+ ---
48
+
49
+ ## Invariants
50
+
51
+ 1. `parentSessionId` is `null` for root sessions, a non-empty string for child sessions.
52
+ 2. `buildSessionTree()` is a pure function: same input always produces the same output.
53
+ 3. A session with `parentSessionId === sessionId` is treated as a root (cycle guard).
54
+ 4. An orphaned child (parent not in the input sessions array) is included in `orphanChildIds` and rendered as a root.
55
+ 5. The flat `/api/v2/sessions` endpoint response is backward-compatible: `parentSessionId` is a new additive field.
56
+ 6. `console/src/api/types.ts` mirrors `src/v2/usecases/console-types.ts` for `ConsoleSessionSummary` -- both must be updated together.
57
+
58
+ ---
59
+
60
+ ## Selected Approach
61
+
62
+ **Candidate B: `buildSessionTree()` pure function + `SessionTreeView` presenter**
63
+
64
+ See `design-candidates-session-tree-view.md` for full analysis. Summary: add `parentSessionId` to the type chain (server + client mirror), extract it at the projection boundary in `console-service.ts`, build the tree client-side in a pure function, and render it in a new presenter component using the existing `TreeLine` connector component.
65
+
66
+ **Runner-up:** Candidate C (server-side tree endpoint) -- loses because the flat list already contains all data needed; a new endpoint adds API surface with no benefit.
67
+
68
+ ---
69
+
70
+ ## Vertical Slices
71
+
72
+ ### Slice 1: Add `parentSessionId` to server-side types and projection
73
+
74
+ **Files changed:**
75
+ - `src/v2/usecases/console-types.ts` -- add `readonly parentSessionId: string | null` to `ConsoleSessionSummary`
76
+ - `src/v2/usecases/console-service.ts` -- add `extractParentSessionId(events)` function, include field in `projectSessionSummary()` return value
77
+
78
+ **Acceptance criterion:** `GET /api/v2/sessions` response includes `parentSessionId: null` on all existing sessions. TypeScript build passes.
79
+
80
+ **Pattern to follow:** `extractGitBranch(events)` / `extractRepoRoot(events)` in `console-service.ts` -- scan events for `session_created`, return `data.parentSessionId ?? null`.
81
+
82
+ **Risk:** Low. Additive field, backward-compatible.
83
+
84
+ ---
85
+
86
+ ### Slice 2: Update client-side type mirror
87
+
88
+ **Files changed:**
89
+ - `console/src/api/types.ts` -- add `readonly parentSessionId: string | null` to `ConsoleSessionSummary`
90
+
91
+ **Acceptance criterion:** TypeScript build of the console package passes. The new field is accessible in all React components that receive `ConsoleSessionSummary`.
92
+
93
+ **Risk:** None. Compile-time only.
94
+
95
+ ---
96
+
97
+ ### Slice 3: Add `buildSessionTree()` to session-list-use-cases
98
+
99
+ **Files changed:**
100
+ - `console/src/views/session-list-use-cases.ts` -- add `SessionTreeNode`, `SessionTree` interfaces and `buildSessionTree()` function
101
+
102
+ **Implementation:**
103
+ ```typescript
104
+ export interface SessionTreeNode {
105
+ readonly session: ConsoleSessionSummary;
106
+ readonly children: readonly ConsoleSessionSummary[];
107
+ }
108
+
109
+ export interface SessionTree {
110
+ readonly roots: readonly SessionTreeNode[];
111
+ readonly orphanChildIds: ReadonlySet<string>;
112
+ }
113
+
114
+ export const TREE_MAX_DEPTH = 2; // aligns with engine's default maxSubagentDepth
115
+
116
+ export function buildSessionTree(sessions: readonly ConsoleSessionSummary[]): SessionTree {
117
+ // Build parent -> children index
118
+ // Cycle guard: skip if parentSessionId === sessionId
119
+ // Orphan detection: if parent not in session set, add to orphanChildIds
120
+ // Return roots (sessions with no parent, plus orphaned children as roots) with children attached
121
+ }
122
+ ```
123
+
124
+ **Acceptance criterion:** Unit test in `console/src/views/session-list-use-cases.test.ts` (create if not exists) covers: empty input, all roots, parent-child pairs, cycle detection, orphaned children.
125
+
126
+ **Risk:** Low. Pure function, no I/O.
127
+
128
+ ---
129
+
130
+ ### Slice 4: Add tree view mode to SessionList state and reducer
131
+
132
+ **Files changed:**
133
+ - `console/src/views/session-list-reducer.ts` -- add `viewMode: 'flat' | 'tree'` to state; add `view_mode_changed` action; when `view_mode_changed` to `tree`, clear filters
134
+ - `console/src/hooks/useSessionListViewModel.ts` (or equivalent) -- expose `viewMode` and `dispatch` for tree toggle
135
+
136
+ **Acceptance criterion:** Toggling to tree mode clears active status filter and search. State transitions are deterministic. TypeScript build passes.
137
+
138
+ **Risk:** Low. Reducer is pure, state change is straightforward.
139
+
140
+ ---
141
+
142
+ ### Slice 5: Add `SessionTreeView` component and tree toggle UI
143
+
144
+ **Files changed:**
145
+ - `console/src/views/SessionList.tsx` -- add tree/flat mode toggle button; when `viewMode === 'tree'`, render `SessionTreeView` instead of the flat list; show "Filters disabled in tree view" when tree mode active and filters would normally show
146
+ - New component `SessionTreeView` (inline in `SessionList.tsx` or in a new file) -- renders `SessionTreeNode[]` with `TreeLine` wrappers for indented children
147
+
148
+ **Acceptance criterion:**
149
+ - Toggle button visible in toolbar
150
+ - Clicking toggle switches between tree and flat mode
151
+ - In tree mode: coordinator sessions show children indented below using `TreeLine`
152
+ - In tree mode: filter controls show a disabled state or a note
153
+ - In tree mode with no coordinator sessions: renders identically to flat mode (no indentation)
154
+ - `TreeLine` amber connector lines appear between coordinator card and child cards
155
+
156
+ **Risk:** Medium. No real coordinator sessions exist for manual testing. Must use mock data or construct a test scenario.
157
+
158
+ ---
159
+
160
+ ## Test Design
161
+
162
+ ### Unit tests (`console/src/views/session-list-use-cases.test.ts`)
163
+
164
+ Add a new `describe('buildSessionTree')` block:
165
+ 1. Empty input -> `{ roots: [], orphanChildIds: Set() }`
166
+ 2. All root sessions (no parentSessionId) -> all in roots, no orphans
167
+ 3. One coordinator with two children -> coordinator root with two children attached
168
+ 4. Orphaned child (parent not in input) -> child in roots, parentId in orphanChildIds
169
+ 5. Cycle detection (parentSessionId === sessionId) -> treated as root
170
+ 6. Multiple coordinators with overlapping children (edge case -- shouldn't occur but test graceful handling)
171
+
172
+ ### Build verification
173
+
174
+ - `npm run build` (or equivalent) in both workrail root and console package must pass with no new TypeScript errors.
175
+
176
+ ### Manual verification
177
+
178
+ - Create a test session data fixture (mock two sessions with parent-child relationship) and verify tree rendering visually.
179
+ - Or: run a `spawn_agent` workflow in the daemon and observe the console.
180
+
181
+ ---
182
+
183
+ ## Risk Register
184
+
185
+ | Risk | Likelihood | Impact | Mitigation |
186
+ |---|---|---|---|
187
+ | Cycle in parentSessionId causes infinite loop | Low | High | Cycle guard in buildSessionTree() (Slice 3) |
188
+ | Client mirror not updated with server type | Low | Low | TypeScript build fails immediately |
189
+ | No real coordinator sessions to test against | High | Medium | Unit tests cover the logic; manual test with mock data |
190
+ | Phase 2 UX design requires depth-3 trees | Low | Medium | Change `children: readonly ConsoleSessionSummary[]` to `readonly SessionTreeNode[]`; contained change |
191
+ | Tree mode breaks existing GROUP_AXES grouping | None | N/A | Tree mode is a separate view mode; GROUP_AXES unchanged |
192
+
193
+ ---
194
+
195
+ ## PR Packaging Strategy
196
+
197
+ **Single PR: `feat/console-session-tree`**
198
+
199
+ All 5 slices in one PR. Rationale:
200
+ - Slices 1-2 (type changes) are tiny and safe but useless without the rendering
201
+ - Slices 3-4 (logic) are testable independently but nothing to show
202
+ - Slice 5 (UI) depends on all prior slices
203
+ - The entire change is low-risk and additive -- no breaking changes
204
+ - A single PR is easier to review as a coherent feature
205
+
206
+ ---
207
+
208
+ ## Philosophy Alignment Per Slice
209
+
210
+ | Slice | Principle | Status |
211
+ |---|---|---|
212
+ | 1 (server type + extraction) | Validate at boundaries | Satisfied -- extraction at service boundary |
213
+ | 1 | Compose with small pure functions | Satisfied -- extractParentSessionId follows extractRepoRoot pattern |
214
+ | 1 | Immutability | Satisfied -- readonly field |
215
+ | 2 (client mirror) | Make illegal states unrepresentable | Tension -- manual sync; TypeScript catches divergence at build time |
216
+ | 3 (buildSessionTree) | Pure function composition | Satisfied |
217
+ | 3 | Errors as data | Satisfied -- orphanChildIds, no throws |
218
+ | 3 | Make illegal states unrepresentable | Satisfied -- cycle guard prevents self-parenting |
219
+ | 4 (reducer) | Determinism over cleverness | Satisfied -- pure reducer |
220
+ | 4 | Functional/declarative | Satisfied |
221
+ | 5 (UI) | Compose with small pure functions | Satisfied -- SessionTreeView is a pure presenter |
222
+ | 5 | YAGNI | Satisfied -- 2-level only, no filter+tree |
223
+
224
+ ---
225
+
226
+ ## Follow-Up Tickets
227
+
228
+ - **Visual indicator on orphaned child sessions** (Y1): Add a small 'child session' badge to sessions in `orphanChildIds`. Low priority.
229
+ - **Persist tree toggle state** (Y2): Save to sessionStorage or URL param. Low priority.
230
+ - **Auto-suggest tree mode when coordinator sessions exist** (Y3): Detect `parentSessionId != null` in session list; show a subtle prompt. Low priority.
231
+ - **Filter+tree interaction** (design exists): Add `parentIdIndex` to `filterSessions()` so tree mode and filter mode are compatible. Design is documented in `design-candidates-session-tree-view.md`.
232
+
233
+ ---
234
+
235
+ ## Plan Confidence
236
+
237
+ - `unresolvedUnknownCount`: 1 (no real coordinator sessions to validate tree rendering end-to-end -- mitigated by unit tests)
238
+ - `planConfidenceBand`: High
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exaudeus/workrail",
3
- "version": "3.40.0",
3
+ "version": "3.42.0",
4
4
  "description": "Step-by-step workflow enforcement for AI agents via MCP",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -103,6 +103,7 @@
103
103
  "open": "^11.0.0",
104
104
  "reflect-metadata": "^0.2.0",
105
105
  "semver": "^7.7.2",
106
+ "tinyglobby": "^0.2.15",
106
107
  "tsconfig-paths": "^4.2.0",
107
108
  "tslib": "^2.8.1",
108
109
  "tsyringe": "^4.8.0",
@@ -321,7 +321,7 @@
321
321
  },
322
322
  {
323
323
  "kind": "example",
324
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
324
+ "path": "workflows/coding-task-workflow-agentic.json",
325
325
  "note": "Current modern example."
326
326
  }
327
327
  ],
@@ -421,7 +421,7 @@
421
421
  ],
422
422
  "exampleRefs": [
423
423
  {
424
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
424
+ "path": "workflows/coding-task-workflow-agentic.json",
425
425
  "note": "See the sharpened user-voiced prompts in the current lean coding workflow."
426
426
  }
427
427
  ]
@@ -443,7 +443,7 @@
443
443
  "sourceRefs": [
444
444
  {
445
445
  "kind": "example",
446
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
446
+ "path": "workflows/coding-task-workflow-agentic.json",
447
447
  "note": "Uses explicit capture footers and shape-preserving loop outputs."
448
448
  }
449
449
  ],
@@ -457,7 +457,7 @@
457
457
  ],
458
458
  "exampleRefs": [
459
459
  {
460
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
460
+ "path": "workflows/coding-task-workflow-agentic.json",
461
461
  "note": "Uses compact Capture footers and explicit loop-control wording."
462
462
  }
463
463
  ]
@@ -491,7 +491,7 @@
491
491
  },
492
492
  {
493
493
  "kind": "example",
494
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
494
+ "path": "workflows/coding-task-workflow-agentic.json",
495
495
  "note": "Uses prompt fragments to slim mode-specific prompt branches."
496
496
  }
497
497
  ],
@@ -506,7 +506,7 @@
506
506
  ],
507
507
  "exampleRefs": [
508
508
  {
509
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
509
+ "path": "workflows/coding-task-workflow-agentic.json",
510
510
  "note": "Uses prompt fragments and context templates to keep prompts slimmer at render time."
511
511
  }
512
512
  ]
@@ -867,7 +867,7 @@
867
867
  },
868
868
  {
869
869
  "kind": "example",
870
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
870
+ "path": "workflows/coding-task-workflow-agentic.json",
871
871
  "note": "Current loop decision prompts show shape-only output examples."
872
872
  }
873
873
  ],
@@ -881,7 +881,7 @@
881
881
  ],
882
882
  "exampleRefs": [
883
883
  {
884
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
884
+ "path": "workflows/coding-task-workflow-agentic.json",
885
885
  "note": "Current loop decision steps show shape-only output examples."
886
886
  }
887
887
  ]
@@ -934,7 +934,7 @@
934
934
  ],
935
935
  "exampleRefs": [
936
936
  {
937
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
937
+ "path": "workflows/coding-task-workflow-agentic.json",
938
938
  "note": "Phase 0 uses a context-clarity rubric instead of a vibes-only confidence flag."
939
939
  }
940
940
  ]
@@ -962,7 +962,7 @@
962
962
  "sourceRefs": [
963
963
  {
964
964
  "kind": "example",
965
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
965
+ "path": "workflows/coding-task-workflow-agentic.json",
966
966
  "note": "Uses confirmation for real review barriers like MultiPR checkpoints."
967
967
  }
968
968
  ],
@@ -1084,7 +1084,7 @@
1084
1084
  "sourceRefs": [
1085
1085
  {
1086
1086
  "kind": "example",
1087
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
1087
+ "path": "workflows/coding-task-workflow-agentic.json",
1088
1088
  "note": "Delegation checkpoints keep the main agent as the synthesizer and decision-maker."
1089
1089
  }
1090
1090
  ],
@@ -1120,7 +1120,7 @@
1120
1120
  ],
1121
1121
  "exampleRefs": [
1122
1122
  {
1123
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
1123
+ "path": "workflows/coding-task-workflow-agentic.json",
1124
1124
  "note": "Uses explicit challenge, audit, and verification barriers."
1125
1125
  }
1126
1126
  ]
@@ -1170,7 +1170,7 @@
1170
1170
  "sourceRefs": [
1171
1171
  {
1172
1172
  "kind": "example",
1173
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
1173
+ "path": "workflows/coding-task-workflow-agentic.json",
1174
1174
  "note": "Major synthesis checkpoints use Confirmed / Plausible / Rejected for adopted claims."
1175
1175
  }
1176
1176
  ],
@@ -1185,7 +1185,7 @@
1185
1185
  ],
1186
1186
  "exampleRefs": [
1187
1187
  {
1188
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
1188
+ "path": "workflows/coding-task-workflow-agentic.json",
1189
1189
  "note": "Major synthesis checkpoints use Confirmed / Plausible / Rejected for decision-driving findings."
1190
1190
  }
1191
1191
  ]
@@ -1288,7 +1288,7 @@
1288
1288
  ],
1289
1289
  "exampleRefs": [
1290
1290
  {
1291
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
1291
+ "path": "workflows/coding-task-workflow-agentic.json",
1292
1292
  "note": "Current example of modern prompt composition, delegation barriers, and loop semantics."
1293
1293
  }
1294
1294
  ]
@@ -1389,7 +1389,7 @@
1389
1389
  ],
1390
1390
  "exampleRefs": [
1391
1391
  {
1392
- "path": "workflows/coding-task-workflow-agentic.lean.v2.json",
1392
+ "path": "workflows/coding-task-workflow-agentic.json",
1393
1393
  "note": "Uses explicit spec vs implementation-plan ownership."
1394
1394
  }
1395
1395
  ]