@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,199 @@
1
+ # Design Candidates: Context Assembly Layer v1
2
+
3
+ *Generated during coding-task-workflow-agentic session | 2026-04-19*
4
+
5
+ ---
6
+
7
+ ## Problem Understanding
8
+
9
+ ### Core Tensions
10
+
11
+ 1. **Clean DI vs. pragmatic inline construction**: `LocalSessionSummaryProviderV2` is
12
+ wired through the full DI container in the MCP server process. The coordinator runs
13
+ as a CLI in a separate process. We cannot share the DI container. The coordinator
14
+ composition root (`src/cli-worktrain.ts`) must construct the provider inline.
15
+
16
+ 2. **Neverthrow ResultAsync vs. custom Result**: Storage layer uses neverthrow. New
17
+ `src/context-assembly/` uses the repo's custom `Result<T,E>` (`kind: 'ok'|'err'`).
18
+ The bridge must happen at the `infra.ts` boundary.
19
+
20
+ 3. **Partial failure semantics**: Each source (gitDiff, priorNotes) must fail
21
+ independently without aborting context assembly. `Promise.all()` is safe because
22
+ each source always resolves to a `Result` (never throws) when implemented correctly.
23
+
24
+ 4. **Backward compatibility vs. interface evolution**: Adding `contextAssembler?` as
25
+ optional to `CoordinatorDeps` and optional 4th arg to `spawnSession` keeps all
26
+ existing tests and callers working without modification.
27
+
28
+ ### Likely Seam
29
+
30
+ The real seam is `CoordinatorDeps.spawnSession`. The coordinator calls this to dispatch
31
+ a session. Context bundle assembly belongs immediately before this call.
32
+
33
+ ### What Makes This Hard
34
+
35
+ - `LocalSessionEventLogStoreV2` requires `FileSystemPortV2` (a composite of 5 sub-ports
36
+ including write/fd ops). But `load()` only calls `readFileUtf8`. All write methods
37
+ can be stubs for read-only use, but this is a partial lie against the interface contract.
38
+ - `LocalDirectoryListingV2` takes `DirectoryListingOpsPortV2` (separate, simpler) --
39
+ NOT the full `FileSystemPortV2`. These are separate ports.
40
+ - Two Result type systems (neverthrow vs custom) must coexist without leaking across
41
+ the `infra.ts` boundary.
42
+ - `Sha256PortV2` is injected into `LocalSessionEventLogStoreV2` but only called in
43
+ `append()`, not `load()`. Safe to stub.
44
+
45
+ ---
46
+
47
+ ## Philosophy Constraints
48
+
49
+ From `CLAUDE.md` (system-wide agent instructions):
50
+
51
+ - **Errors are data**: Use `Result<T,E>` values. No throwing for expected failures.
52
+ - **DI for boundaries**: Inject I/O effects; keep core logic testable.
53
+ - **Prefer fakes over mocks**: Tests use realistic substitutes (no `vi.mock()`).
54
+ - **YAGNI with discipline**: No speculative abstractions.
55
+ - **Architectural fixes over patches**: Reuse canonical parsing logic.
56
+ - **Immutability by default**: `readonly` on all fields.
57
+
58
+ **Potential conflict**: Creating write stubs on `FileSystemPortV2` is pragmatic but
59
+ technically lies about the contract. The pitch (R1 mitigation) explicitly endorses this
60
+ approach. Acceptable given the inline-construction pattern is already acknowledged as
61
+ a special case.
62
+
63
+ ---
64
+
65
+ ## Impact Surface
66
+
67
+ - `src/coordinators/pr-review.ts`: `CoordinatorDeps.spawnSession` (add optional 4th arg),
68
+ `contextAssembler?` optional field. Backward-compatible.
69
+ - `src/daemon/workflow-runner.ts`: `buildSystemPrompt()` reads `trigger.context['assembledContextSummary']`.
70
+ No signature change needed -- `trigger.context` already typed as `Readonly<Record<string,unknown>>`.
71
+ - `src/cli-worktrain.ts`: `spawnSession` impl forwards 4th arg; `contextAssembler` wired.
72
+ - All existing tests that construct `CoordinatorDeps` fakes: safe (optional fields).
73
+ - No changes to `src/mcp/` or `src/v2/durable-core/`.
74
+
75
+ ---
76
+
77
+ ## Candidates
78
+
79
+ ### Candidate A: Real local infra classes with minimal read-only shims (SELECTED)
80
+
81
+ **Summary**: Construct `LocalDataDirV2`, `LocalDirectoryListingV2`, and
82
+ `LocalSessionEventLogStoreV2` directly inside `infra.ts`. Create inline adapters:
83
+ - `DirectoryListingOpsPortV2`: wraps `fs.promises.readdir` + `stat` with `okAsync/errAsync`
84
+ - `FileSystemPortV2`: only `readFileUtf8` is real; write/fd methods throw 'context-assembly: write ops not supported'
85
+ - `Sha256PortV2`: stub that throws (never called during `load()`)
86
+
87
+ **Tensions resolved**: R1 (DI wiring scope). Uses battle-tested session loading and
88
+ projection code. Neverthrow/custom Result bridge stays at `infra.ts` boundary only.
89
+
90
+ **Tensions accepted**: Write stubs on `FileSystemPortV2` are a partial lie.
91
+
92
+ **Boundary solved at**: `src/context-assembly/infra.ts` -- coordinator composition root.
93
+
94
+ **Why this boundary is best-fit**: The coordinator is already a composition root
95
+ (`src/cli-worktrain.ts` wires all real deps). `infra.ts` is a thin adapter layer that
96
+ belongs exactly here.
97
+
98
+ **Failure mode**: If `LocalSessionEventLogStoreV2.load()` ever internally calls a write
99
+ method (unlikely but possible in future refactors), `infra.ts` would throw at runtime
100
+ rather than returning `err()`. This would be caught immediately by tests.
101
+
102
+ **Repo-pattern relationship**: Follows existing local infra pattern exactly.
103
+ `LocalDataDirV2`, `LocalDirectoryListingV2`, `LocalSessionEventLogStoreV2` are already
104
+ the canonical local implementations.
105
+
106
+ **Gains**: Reuses battle-tested store loading and projection code. No risk of format
107
+ divergence if session file structure changes.
108
+
109
+ **Losses**: Write stubs feel slightly dishonest. Minor complexity in `infra.ts`.
110
+
111
+ **Scope judgment**: Best-fit. Stays within `src/context-assembly/` with no v2 changes.
112
+
113
+ **Philosophy fit**: Honors DI for boundaries (composition root pattern), errors-as-data
114
+ (Result bridge), architectural fixes over patches (reuses canonical code). Minor
115
+ tension with YAGNI (write stubs) and interface honesty.
116
+
117
+ ---
118
+
119
+ ### Candidate B: Read session files directly with fs.promises
120
+
121
+ **Summary**: Skip the port hierarchy. Read `manifest.jsonl` and `events/*.jsonl` directly
122
+ using `fs.promises.readFile`. Parse manually or reuse just the schema validators.
123
+
124
+ **Tensions resolved**: R1 completely -- no DI wiring at all.
125
+
126
+ **Tensions accepted**: Duplicates session reading logic. Any change to session file
127
+ format requires updates in two places.
128
+
129
+ **Failure mode**: Silent breakage if session file format changes.
130
+
131
+ **Repo-pattern relationship**: Departs from established pattern (re-implements existing logic).
132
+
133
+ **Scope judgment**: Too narrow -- creates a fragile parallel implementation.
134
+
135
+ **Philosophy fit**: Conflicts with 'architectural fixes over patches'.
136
+
137
+ ---
138
+
139
+ ### Candidate C: Add createLocalSessionSummaryProvider() factory to v2 module
140
+
141
+ **Summary**: Add a `createLocalSessionSummaryProvider()` factory function to
142
+ `src/v2/infra/local/session-summary-provider/index.ts` that wires real deps and
143
+ returns a `SessionSummaryProviderPortV2`. Import and call from `infra.ts`.
144
+
145
+ **Tensions resolved**: Cleanest DI wiring.
146
+
147
+ **Tensions accepted**: Modifies an existing v2 infra module.
148
+
149
+ **Failure mode**: Entangles CLI-coordinator concerns into the storage layer.
150
+
151
+ **Scope judgment**: Slightly too broad -- modifies v2 module with no functional benefit
152
+ over Candidate A.
153
+
154
+ ---
155
+
156
+ ## Comparison and Recommendation
157
+
158
+ **Recommendation: Candidate A**
159
+
160
+ All candidates agree on the high-level approach (4 new files, 3 modified files, exact
161
+ pitch interfaces). The only real design decision is `infra.ts` implementation.
162
+
163
+ Candidate A wins because:
164
+ 1. It reuses battle-tested code for session reading and health projection
165
+ 2. It stays within `src/context-assembly/` -- no v2 module changes
166
+ 3. The write stubs are safe: `load()` provably does not call write methods
167
+ 4. The pitch explicitly endorses this approach as R1 mitigation
168
+
169
+ ---
170
+
171
+ ## Self-Critique
172
+
173
+ **Strongest counter-argument**: The write stubs on `FileSystemPortV2` create a silent
174
+ contract violation. If a future `load()` implementation adds a write call internally,
175
+ the error would surface at runtime (throw) rather than compile-time.
176
+
177
+ **Narrower option that lost**: Candidate B (raw fs.promises). Lost because it duplicates
178
+ canonical parsing logic and diverges from existing patterns.
179
+
180
+ **Broader option and evidence required**: Candidate C would be justified if multiple
181
+ other CLI commands needed access to session summaries -- adding a factory to the v2
182
+ module would be worth the scope increase. No evidence of this need in v1.
183
+
184
+ **Assumption that would invalidate this design**: If `LocalSessionEventLogStoreV2.load()`
185
+ internally calls any of the stubbed write methods (currently provably false by code
186
+ inspection of `loadImpl()`, `loadValidatedPrefixImpl()`, `readManifestOrEmpty()`).
187
+
188
+ ---
189
+
190
+ ## Open Questions for Main Agent
191
+
192
+ 1. Verify: does `Sha256PortV2` have a simple interface that can be satisfied with a
193
+ 1-line `node:crypto` implementation? (If so, use real impl rather than throw stub.)
194
+ 2. Verify: does the `FileSystemPortV2` shim need anything beyond `readFileUtf8` for
195
+ the `load()` path? (Current analysis: no -- `readManifestOrEmpty` and segment loading
196
+ both use `readFileUtf8` only.)
197
+ 3. The re-review spawn (line ~1265 in pr-review.ts) should forward `spawnContext` from
198
+ the outer scope per the pitch. Confirm this is the correct approach (vs. reassembling
199
+ context for re-reviews).
@@ -0,0 +1,211 @@
1
+ # Implementation Plan: Context Assembly Layer v1
2
+
3
+ *Authored during coding-task-workflow-agentic session | 2026-04-19*
4
+
5
+ ---
6
+
7
+ ## Problem Statement
8
+
9
+ When the PR review coordinator dispatches a session, it passes only a goal string.
10
+ The agent spends its first 2-3 turns re-establishing context (re-reading PR diff,
11
+ starting cold with no memory of prior reviews). Before spawning each session, the
12
+ coordinator should assemble a context bundle (git diff summary + prior session notes)
13
+ and inject it into the system prompt via the existing `trigger.context` map.
14
+
15
+ ---
16
+
17
+ ## Acceptance Criteria
18
+
19
+ 1. `npm run build` compiles clean with zero TypeScript errors
20
+ 2. `npx vitest run tests/unit/context-assembly.test.ts` -- all 5 tests pass
21
+ 3. `npx vitest run` -- no regressions in full test suite
22
+ 4. New module `src/context-assembly/` exists with 4 files
23
+ 5. `CoordinatorDeps.contextAssembler?` optional field present
24
+ 6. `CoordinatorDeps.spawnSession` accepts optional 4th `context?` arg
25
+ 7. `buildSystemPrompt()` injects `## Prior Context` section when `assembledContextSummary` present in trigger context
26
+ 8. `src/cli-worktrain.ts` wires `contextAssembler` and forwards `context` through HTTP body
27
+
28
+ ---
29
+
30
+ ## Non-Goals
31
+
32
+ 1. No URL fetching from PR description bodies (v2)
33
+ 2. No full git diff content -- `--stat` output only
34
+ 3. No changes to `src/mcp/` or `src/v2/durable-core/`
35
+ 4. No knowledge graph source (`ts-morph` stays in devDependencies)
36
+ 5. No per-coordinator custom renderers
37
+ 6. No context window budget trimming
38
+ 7. No context assembly in `TriggerRouter.route()`
39
+ 8. No HTTP endpoint schema changes
40
+ 9. No workspace-scoped session filtering (v1 returns global sessions)
41
+
42
+ ---
43
+
44
+ ## Philosophy-Driven Constraints
45
+
46
+ - All interface fields `readonly` (immutability by default)
47
+ - `Result<T,E>` (`kind: 'ok'|'err'`) for all new code; bridge from neverthrow at `infra.ts` boundary
48
+ - Factory function `createContextAssembler()`, not a class
49
+ - `renderContextBundle()` is pure (no I/O)
50
+ - `ContextAssemblerDeps` interface contains all I/O; core has zero direct imports of `fs`/`execFile`
51
+ - Tests: fake deps, no `vi.mock()`, vitest `describe/it/expect`
52
+ - Import paths use `.js` extension (TypeScript ESM)
53
+ - JSDoc `WHY` comments on non-obvious decisions
54
+
55
+ ---
56
+
57
+ ## Invariants
58
+
59
+ 1. **Partial failure**: `gitDiff` and `priorSessionNotes` fail independently; session always starts
60
+ 2. **Zero agent turn cost**: Context injected in system prompt before turn 1
61
+ 3. **Coordinator-controlled opt-in**: Only coordinators calling `assembler.assemble()` get enriched context
62
+ 4. **Prior notes recency limit**: At most 3 prior sessions
63
+ 5. **Git summary size**: File names and change counts only (no full diff)
64
+ 6. **No engine/MCP changes**: `src/v2/durable-core/` and `src/mcp/` untouched
65
+ 7. **Optional contextAssembler**: Backward-compatible -- all existing `CoordinatorDeps` fakes work
66
+
67
+ ---
68
+
69
+ ## Selected Approach
70
+
71
+ Use 4 new files in `src/context-assembly/` + 3 modified files, exactly as specified in
72
+ the pitch. `infra.ts` uses a direct `SessionEventLogReadonlyStorePortV2` adapter
73
+ (implementing only `load()` and `loadValidatedPrefix()` via `fs.promises` + existing Zod
74
+ schemas) instead of `LocalSessionEventLogStoreV2`. This eliminates the need for
75
+ `FileSystemPortV2` and `Sha256PortV2` shims.
76
+
77
+ **Runner-up**: Use `LocalSessionEventLogStoreV2` with write stubs. Rejected: write stubs
78
+ partially violate the interface contract even though they're provably unreachable.
79
+
80
+ ---
81
+
82
+ ## Vertical Slices
83
+
84
+ ### Slice 1: New module `src/context-assembly/` (4 files)
85
+
86
+ **Files to create**:
87
+ - `src/context-assembly/types.ts` (~60 LOC) - AssemblyTask, SessionNote, ContextBundle, RenderOpts, ContextAssembler interface
88
+ - `src/context-assembly/deps.ts` (~35 LOC) - ContextAssemblerDeps interface (execGit, execGh, listRecentSessions, nowIso)
89
+ - `src/context-assembly/index.ts` (~80 LOC) - createContextAssembler(), renderContextBundle(), private helpers
90
+ - `src/context-assembly/infra.ts` (~80 LOC) - createListRecentSessions() adapter
91
+
92
+ **Done when**: All 4 files compile clean in isolation
93
+
94
+ **Key implementation details**:
95
+ - `types.ts`: import `Result` from `'../runtime/result.js'`
96
+ - `index.ts`: factory function `createContextAssembler(deps)`, pure `renderContextBundle(bundle, _opts?)`
97
+ - `infra.ts`: `SessionEventLogReadonlyStorePortV2` adapter using `fs.promises.readFile` + `ManifestRecordV1Schema` + `DomainEventV1Schema`; `LocalDataDirV2(process.env)` + `LocalDirectoryListingV2(directoryListingOpsAdapter)` wired into `LocalSessionSummaryProviderV2`
98
+ - `infra.ts`: `_workspacePath` named with leading underscore (intentional non-use in v1)
99
+ - Bridge: `const result = await provider.loadHealthySummaries(); if (result.isErr()) return err(...);`
100
+
101
+ ### Slice 2: Modify `src/coordinators/pr-review.ts`
102
+
103
+ **Changes**:
104
+ 1. Add `contextAssembler?: ContextAssembler` field to `CoordinatorDeps` (after `spawnSession`, before `awaitSessions`)
105
+ 2. Extend `spawnSession` signature: add optional 4th arg `context?: Readonly<Record<string, unknown>>`
106
+ 3. Add context assembly block before `spawnSession` call in `runPrReviewCoordinator` for-loop
107
+ 4. Forward same `spawnContext` to re-review spawn (~line 1265)
108
+ 5. Do NOT add context assembly to fix-agent spawn
109
+
110
+ **Done when**: File compiles clean; existing tests still pass
111
+
112
+ ### Slice 3: Modify `src/daemon/workflow-runner.ts`
113
+
114
+ **Change**: Insert 6-line block in `buildSystemPrompt()` BEFORE the `referenceUrls` block
115
+
116
+ **Done when**: File compiles clean; `buildSystemPrompt` tests (if any) still pass
117
+
118
+ ### Slice 4: Modify `src/cli-worktrain.ts`
119
+
120
+ **Changes**:
121
+ 1. Add imports for `createContextAssembler` and `createListRecentSessions`
122
+ 2. Extend `spawnSession` lambda to accept optional 4th `context?` arg and include it in JSON body
123
+ 3. Add `contextAssembler: createContextAssembler({...})` to deps object (after `spawnSession`)
124
+ 4. Wire `execGit`, `execGh` inline using `promisify(execFile)`
125
+
126
+ **Done when**: File compiles clean
127
+
128
+ ### Slice 5: Tests
129
+
130
+ **File**: `tests/unit/context-assembly.test.ts`
131
+
132
+ **Tests**:
133
+ 1. `assemble()` with both sources succeeding -- returns bundle with `kind: 'ok'` on both fields
134
+ 2. `assemble()` with gitSummary failing -- priorNotes still returns `ok([])`; gitDiff returns `kind: 'err'`
135
+ 3. `renderBundle()` produces correct markdown sections (headings, git diff in code block)
136
+ 4. `renderBundle()` with all-failed bundle -- returns empty string
137
+ 5. `listRecentSessions` with no sessions directory -- returns `ok([])`
138
+
139
+ **Done when**: All 5 tests pass with `npx vitest run tests/unit/context-assembly.test.ts`
140
+
141
+ ---
142
+
143
+ ## Test Design
144
+
145
+ ### Fake deps for context assembler
146
+
147
+ ```typescript
148
+ function makeFakeDeps(overrides: Partial<ContextAssemblerDeps> = {}): ContextAssemblerDeps {
149
+ return {
150
+ execGit: async () => ({ kind: 'ok', value: 'src/foo.ts | 5 ++\n' }),
151
+ execGh: async () => ({ kind: 'err', error: 'gh not available' }),
152
+ listRecentSessions: async () => ({ kind: 'ok', value: [] }),
153
+ nowIso: () => '2026-04-19T00:00:00.000Z',
154
+ ...overrides,
155
+ };
156
+ }
157
+ ```
158
+
159
+ ### Test for no sessions directory
160
+
161
+ Uses a real temp directory (with no sessions subdirectory) to test the graceful `ok([])` return.
162
+
163
+ ---
164
+
165
+ ## Risk Register
166
+
167
+ | Risk | Likelihood | Impact | Mitigation |
168
+ |---|---|---|---|
169
+ | `infra.ts` SessionEventLogReadonlyStorePortV2 adapter has parsing bugs | Low | Medium | Tests verify behavior with fake data; manifest format well-documented |
170
+ | `spawnSession` 4th arg breaks existing tests | Very Low | Low | Optional arg; TypeScript enforces backward compat |
171
+ | `contextAssembler` not optional breaks existing tests | Very Low | Low | `?` makes it optional; existing fakes compile without it |
172
+ | Full test suite regressions | Very Low | High | Run `npx vitest run` before PR |
173
+
174
+ ---
175
+
176
+ ## PR Packaging Strategy
177
+
178
+ **Single PR**: `feat/context-assembly-layer`
179
+
180
+ All 7 file changes ship together. They form a single coherent feature -- the 4 new
181
+ files are meaningless without the 3 modifications, and the modifications are safe
182
+ with or without the new files.
183
+
184
+ **Commit message**: `feat(coordinator): add context assembly layer -- git summary and prior session notes`
185
+
186
+ ---
187
+
188
+ ## Philosophy Alignment Per Slice
189
+
190
+ | Slice | Principle | Status |
191
+ |---|---|---|
192
+ | Slice 1 (types) | Make illegal states unrepresentable (discriminated union) | Satisfied |
193
+ | Slice 1 (index) | Functional/declarative (factory fn, pure render) | Satisfied |
194
+ | Slice 1 (infra) | DI for boundaries (all I/O behind interface) | Satisfied |
195
+ | Slice 1 (infra) | Architectural fixes over patches (reuse projection code) | Satisfied |
196
+ | Slice 2 (coordinator) | Errors are data (Result<T,E>) | Satisfied |
197
+ | Slice 2 (coordinator) | Backward compatibility via optional fields | Satisfied |
198
+ | Slice 3 (workflow-runner) | Validate at boundaries (type check before inject) | Satisfied |
199
+ | Slice 5 (tests) | Prefer fakes over mocks | Satisfied |
200
+
201
+ ---
202
+
203
+ ## Open Questions (Residual)
204
+
205
+ None. All questions were resolved during design review:
206
+ 1. `ManifestRecordV1Schema` and `DomainEventV1Schema` -- confirmed exported
207
+ 2. `asSessionId` -- confirmed exported from ids/index.ts
208
+ 3. Re-review spawn context -- confirmed: forward same `spawnContext` from outer scope
209
+
210
+ **`unresolvedUnknownCount`**: 0
211
+ **`planConfidenceBand`**: High
@@ -0,0 +1,112 @@
1
+ # Design Review Findings: Context Assembly Layer v1
2
+
3
+ *Generated during coding-task-workflow-agentic session | 2026-04-19*
4
+
5
+ ---
6
+
7
+ ## Tradeoff Review
8
+
9
+ | Tradeoff | Verdict | Condition for Failure |
10
+ |---|---|---|
11
+ | FileSystemPortV2 write stubs | RESOLVED -- switching to direct SessionEventLogReadonlyStorePortV2 adapter | N/A |
12
+ | Inline DI wiring in infra.ts | Acceptable -- explicitly endorsed by pitch R1 mitigation | N/A |
13
+ | Global session list (no workspace filtering) | Accepted -- v2 improvement documented in pitch | Multiple simultaneous workspaces with unrelated sessions |
14
+ | Promise.all partial failure | Adequately handled -- each source returns Result, try/catch in infra | N/A |
15
+
16
+ ---
17
+
18
+ ## Failure Mode Review
19
+
20
+ | Failure Mode | Covered By | Residual Risk |
21
+ |---|---|---|
22
+ | Write stubs called | Outer try/catch in createListRecentSessions | Low -- FM1 resolved by switching adapter |
23
+ | Git commands fail | execGit/execGh return err(), fallback chain | None |
24
+ | Sessions dir missing | LocalDirectoryListingV2 returns [] for FS_NOT_FOUND | None |
25
+ | Corrupt session data | LocalSessionSummaryProviderV2 graceful skip | None |
26
+ | spawnSession backward compat | Optional 4th arg | None |
27
+ | contextAssembler not provided | Optional field + if-guard in coordinator | None |
28
+
29
+ ---
30
+
31
+ ## Runner-Up / Simpler Alternative Review
32
+
33
+ **Runner-up** (Candidate B: raw fs.promises) has no elements worth borrowing into the
34
+ selected design.
35
+
36
+ **Simpler variant identified**: Use a direct `SessionEventLogReadonlyStorePortV2`
37
+ adapter in `infra.ts` instead of `LocalSessionEventLogStoreV2`. This eliminates all
38
+ `FileSystemPortV2` and `Sha256PortV2` stubs while still reusing `LocalSessionSummaryProviderV2`
39
+ and the full projection pipeline. The adapter implements only `load()` and
40
+ `loadValidatedPrefix()` using `fs.promises` + existing Zod schemas (`ManifestRecordV1Schema`,
41
+ `DomainEventV1Schema`). Approximately 40 lines. This is strictly cleaner.
42
+
43
+ **Recommendation**: Use the simpler `SessionEventLogReadonlyStorePortV2` adapter approach.
44
+
45
+ ---
46
+
47
+ ## Philosophy Alignment
48
+
49
+ | Principle | Alignment |
50
+ |---|---|
51
+ | Errors are data | STRONG -- Result<T,E> throughout |
52
+ | DI for boundaries | STRONG -- ContextAssemblerDeps, inline construction accepted for separate process |
53
+ | Prefer fakes over mocks | STRONG -- test spec uses fake deps |
54
+ | Immutability by default | STRONG -- readonly everywhere |
55
+ | Make illegal states unrepresentable | STRONG -- AssemblyTask discriminated union |
56
+ | Functional/declarative | STRONG -- factory functions, pure renderContextBundle |
57
+ | YAGNI | MILD TENSION -- empty RenderOpts interface (accepted, documented) |
58
+ | Architectural fixes over patches | STRONG with adapter approach |
59
+
60
+ ---
61
+
62
+ ## Findings
63
+
64
+ ### YELLOW: Empty RenderOpts interface
65
+
66
+ **Severity**: Yellow (minor)
67
+
68
+ **Finding**: `RenderOpts` is an empty interface in v1. TypeScript `{}` and `interface RenderOpts {}` are equivalent. The empty interface is only useful as a future extension point.
69
+
70
+ **Assessment**: Acceptable -- the pitch explicitly documents this as an intentional v1 placeholder. No action needed.
71
+
72
+ ### YELLOW: Global session filtering (no workspace scope)
73
+
74
+ **Severity**: Yellow (minor, known)
75
+
76
+ **Finding**: `listRecentSessions` returns the 3 most recent sessions globally, not scoped to the coordinator's workspace path. For users running multiple workspaces, prior notes from unrelated workspaces may appear.
77
+
78
+ **Assessment**: Acceptable for v1 -- pitch explicitly documents this as a known limitation. The `_workspacePath` parameter should be named with a leading `_` to signal intentional non-use.
79
+
80
+ ### GREEN: All other design elements
81
+
82
+ Tradeoffs, failure modes, and philosophy alignment are all sound. No RED or ORANGE findings.
83
+
84
+ ---
85
+
86
+ ## Recommended Revisions
87
+
88
+ 1. **Use direct `SessionEventLogReadonlyStorePortV2` adapter** in `infra.ts` instead of
89
+ `LocalSessionEventLogStoreV2`. Eliminates all write stubs. Implements `load()` using
90
+ `fs.promises.readFile` + `ManifestRecordV1Schema` + `DomainEventV1Schema` parsing.
91
+ Approximately 40 lines.
92
+
93
+ 2. **Name unused parameter** `_workspacePath` in `createListRecentSessions` to signal
94
+ intentional non-use.
95
+
96
+ ---
97
+
98
+ ## Residual Concerns
99
+
100
+ 1. **Import path for Zod schemas**: `ManifestRecordV1Schema` and `DomainEventV1Schema`
101
+ must be importable from `src/v2/durable-core/schemas/session/index.ts`. Verify before
102
+ implementing that these schemas are exported.
103
+
104
+ 2. **`asSessionId` usage**: The `SessionEventLogReadonlyStorePortV2.load()` adapter
105
+ receives a `SessionId` branded type. When reading session directories and mapping
106
+ entry names to `SessionId`, the `asSessionId` import from
107
+ `src/v2/durable-core/ids/index.ts` must be used correctly.
108
+
109
+ 3. **Neverthrow ResultAsync wrapping**: The adapter must return neverthrow `ResultAsync`
110
+ objects (using `okAsync`/`errAsync` from neverthrow), not the custom `Result` type.
111
+ The bridge from neverthrow to custom Result happens in the `createListRecentSessions`
112
+ function via `const result = await resultAsync; if (result.isErr()) return err(...)`.