| ... | @@ -1,421 +0,0 @@ |
| 1 | # PR #89 Review Feedback Resolution Plan |
| 2 | |
| 3 | ## Context |
| 4 | - Source: GitHub review threads/comments on PR `#89` (`Add server-side orchestration engine with event sourcing`). |
| 5 | - Triage baseline: |
| 6 | - 147 threads, 149 comments total. |
| 7 | - 54 active unresolved threads. |
| 8 | - Deduped to 47 unique active issues. |
| 9 | - Filtered invalid/wrong claims: 2 issue clusters. |
| 10 | - Net actionable backlog: 45 unique issues. |
| 11 | |
| 12 | ## Objectives |
| 13 | 1. Remove high-risk runtime failure modes first (worker/fiber death, unrecoverable ingestion stalls). |
| 14 | 2. Preserve event-sourcing correctness under failure/retry (projection, receipts, checkpoint ordering). |
| 15 | 3. Resolve client-visible correctness regressions (branch/session races, markdown link parsing). |
| 16 | 4. Close low-signal or duplicate review noise with explicit disposition. |
| 17 | |
| 18 | ## Non-Goals |
| 19 | - No protocol reintroduction for removed `providers.event` channel (invalid comment cluster). |
| 20 | - No architecture rewrite beyond targeted hardening and correctness fixes needed to land PR safely. |
| 21 | |
| 22 | ## Execution Order |
| 23 | 1. **Phase A (P0): Runtime fault tolerance** |
| 24 | 2. **Phase B (P0/P1): Checkpoint consistency + sequencing** |
| 25 | 3. **Phase C (P1): Persistence/projection correctness** |
| 26 | 4. **Phase D (P1/P2): Runtime/platform hardening** |
| 27 | 5. **Phase E (P1/P2): Web/client correctness** |
| 28 | 6. **Phase F (P2): Cleanup/typing hygiene + review thread closure** |
| 29 | |
| 30 | --- |
| 31 | |
| 32 | ## Phase A (P0) - Runtime Fault Tolerance |
| 33 | |
| 34 | ### A1) Provider runtime ingestion loop can die permanently on one dispatch error |
| 35 | - Issue: |
| 36 | - `ProviderRuntimeIngestion` uses `Effect.forever(Queue.take(...).flatMap(processEvent))` without local recovery. A single failure can terminate the worker fiber. |
| 37 | - Planned resolution: |
| 38 | - Wrap per-item processing with `Effect.catch` (don't use `catchAllCause` as that is a signal for detects which should not be recovered from) and structured logging (`event.type`, `sessionId`, `threadId if known`). |
| 39 | - Keep queue consumer alive after failures. |
| 40 | - Add a bounded error counter metric/log cadence to avoid log spam. |
| 41 | - Target files: |
| 42 | - `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts` |
| 43 | - `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts` |
| 44 | - Tests: |
| 45 | - Add test where first dispatch fails, second event still processes successfully. |
| 46 | - Assert worker remains alive and queue continues draining. |
| 47 | |
| 48 | ### A2) Provider command reactor loop can die permanently on provider-side failure |
| 49 | - Issue: |
| 50 | - `ProviderCommandReactor` queue consumer also runs forever with no protection around `processDomainEvent`. |
| 51 | - Planned resolution: |
| 52 | - Apply same per-item fault boundary as A1. |
| 53 | - Differentiate retryable provider errors vs invariant errors for log level (`warn` vs `error`). |
| 54 | - Target files: |
| 55 | - `apps/server/src/orchestration/Layers/ProviderCommandReactor.ts` |
| 56 | - `apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts` |
| 57 | - Tests: |
| 58 | - Inject provider service failure for one event and verify subsequent events still execute. |
| 59 | |
| 60 | ### A3) Checkpoint reactor worker can die and halt checkpointing for process lifetime |
| 61 | - Issue: |
| 62 | - `CheckpointReactor` queue worker has no internal error boundary; checkpoint/git failures can stop all future checkpoint handling. |
| 63 | - Planned resolution: |
| 64 | - Add per-item recovery with contextual logging. |
| 65 | - Keep worker alive for transient checkpoint/git errors. |
| 66 | - Optionally emit `thread.activity.append` error activity for visibility when checkpoint operations fail. |
| 67 | - Target files: |
| 68 | - `apps/server/src/orchestration/Layers/CheckpointReactor.ts` |
| 69 | - `apps/server/src/orchestration/Layers/CheckpointReactor.test.ts` |
| 70 | - Tests: |
| 71 | - Simulate failing `CheckpointStore` call on one event; verify next event still processes. |
| 72 | |
| 73 | ### A4) WS message handler can surface unhandled rejection at runtime |
| 74 | - Issue: |
| 75 | - `Effect.runPromise(handleMessage(...))` path flagged for potential unhandled rejection. |
| 76 | - Planned resolution: |
| 77 | - Add explicit `.catch` with structured logging on message processing invocation path. |
| 78 | - Ensure one bad message cannot destabilize the WebSocket server. |
| 79 | - Target files: |
| 80 | - `apps/server/src/wsServer.ts` |
| 81 | - `apps/server/src/wsServer.test.ts` |
| 82 | - Tests: |
| 83 | - Add regression test forcing `handleMessage` error and assert server still accepts/processes subsequent messages. |
| 84 | |
| 85 | --- |
| 86 | |
| 87 | ## Phase B (P0/P1) - Checkpoint Consistency + Sequencing |
| 88 | |
| 89 | ### B1) Revert flow uses forked completion dispatch and can expose stale read model window |
| 90 | - Issue: |
| 91 | - `thread.revert.complete` dispatch is forked in reactor path, allowing downstream logic to observe stale state during revert completion window. |
| 92 | - Planned resolution: |
| 93 | - Make revert completion dispatch part of the same serialized effect flow (await it rather than fire-and-forget). |
| 94 | - Preserve ordering guarantees: fs restore/provider rollback/delete ref -> completion command -> next dependent work. |
| 95 | - Target files: |
| 96 | - `apps/server/src/orchestration/Layers/CheckpointReactor.ts` |
| 97 | - `apps/server/src/orchestration/Layers/CheckpointReactor.test.ts` |
| 98 | - Tests: |
| 99 | - Add ordering test ensuring revert completion is visible before subsequent checkpoint-dependent processing. |
| 100 | |
| 101 | ### B2) Turn-count inference can be wrong when positional index diverges from authoritative count |
| 102 | - Issue: |
| 103 | - `inferCheckpointTurnCountByTurnId` fallback behavior may infer count from position, leading to incorrect revert semantics. |
| 104 | - Planned resolution: |
| 105 | - Use authoritative checkpoint metadata when available. |
| 106 | - Restrict positional fallback to explicit best-effort mode and prevent it from driving destructive operations. |
| 107 | - Target files: |
| 108 | - `apps/web/src/session-logic.ts` |
| 109 | - `apps/web/src/session-logic.test.ts` |
| 110 | - Tests: |
| 111 | - Add cases where positional order differs from persisted turn count and assert correct count resolution. |
| 112 | |
| 113 | ### B3) Inconsistent input normalization across checkpoint store methods |
| 114 | - Issue: |
| 115 | - Some `CheckpointStore` methods trim/normalize input while others do not, creating inconsistent ref/cwd behavior. |
| 116 | - Planned resolution: |
| 117 | - Introduce shared normalization utility and apply uniformly across all public `CheckpointStore` methods. |
| 118 | - Keep normalization semantics explicit and covered by tests. |
| 119 | - Target files: |
| 120 | - `apps/server/src/checkpointing/Layers/CheckpointStore.ts` |
| 121 | - existing checkpointing tests (and add targeted method-level tests) |
| 122 | - Tests: |
| 123 | - Parameterized tests that call all methods with leading/trailing whitespace and assert consistent behavior. |
| 124 | |
| 125 | ### B4) Duplicate checkpoint-ref generation logic across runtime and tests |
| 126 | - Issue: |
| 127 | - `checkpointRefForThreadTurn` logic is duplicated across multiple files, creating drift risk. |
| 128 | - Planned resolution: |
| 129 | - Extract a single shared helper module for checkpoint ref generation. |
| 130 | - Migrate runtime and test callsites to helper. |
| 131 | - Target files: |
| 132 | - `apps/server/src/checkpointing/*` or `apps/server/src/orchestration/*` shared helper (new) |
| 133 | - `apps/server/src/wsServer.ts` |
| 134 | - `apps/server/src/orchestration/Layers/CheckpointReactor.ts` |
| 135 | - impacted tests/integration harnesses |
| 136 | - Tests: |
| 137 | - Add dedicated helper tests for deterministic encoding format and stability. |
| 138 | |
| 139 | --- |
| 140 | |
| 141 | ## Phase C (P1) - Persistence / Projection Correctness |
| 142 | |
| 143 | ### C1) Event append and projection update can diverge on mid-pipeline failure |
| 144 | - Issue: |
| 145 | - If event append succeeds and projection/update fails, state can become desynchronized. |
| 146 | - Planned resolution: |
| 147 | - Rework processing boundary so in-memory and persisted projection updates happen in a single coherent success path. |
| 148 | - If projection fails, fail command deterministically and record consistent receipt/error path. |
| 149 | - Target files: |
| 150 | - `apps/server/src/orchestration/Layers/OrchestrationEngine.ts` |
| 151 | - `apps/server/src/orchestration/Layers/ProjectionPipeline.ts` |
| 152 | - related persistence layer files as needed |
| 153 | - Tests: |
| 154 | - Simulate projection failure post-append and verify deterministic observable state + receipt behavior. |
| 155 | |
| 156 | ### C2) Engine error handling/receipt semantics are incomplete for non-invariant failures |
| 157 | - Issue: |
| 158 | - Non-invariant failures may not persist failure receipt consistently, weakening idempotency/retry behavior. |
| 159 | - Planned resolution: |
| 160 | - Define explicit receipt policy for invariant vs infrastructure failures. |
| 161 | - Persist rejected receipt shape with error classification and retryability signal. |
| 162 | - Target files: |
| 163 | - `apps/server/src/orchestration/Layers/OrchestrationEngine.ts` |
| 164 | - `apps/server/src/persistence/Layers/OrchestrationCommandReceipts.ts` |
| 165 | - `apps/server/src/orchestration/*test.ts` |
| 166 | - Tests: |
| 167 | - Retry same `commandId` after failure and assert deterministic replay/idempotency behavior. |
| 168 | |
| 169 | ### C3) Projection pipeline fallback can preserve orphaned messages (FK risk) |
| 170 | - Issue: |
| 171 | - Gap-filling fallback logic can retain messages tied to removed turns. |
| 172 | - Planned resolution: |
| 173 | - Remove fallback or strictly filter fallback messages by retained turn ids. |
| 174 | - Validate FK-safe mutation path. |
| 175 | - Target files: |
| 176 | - `apps/server/src/orchestration/Layers/ProjectionPipeline.ts` |
| 177 | - `apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts` |
| 178 | - Tests: |
| 179 | - Revert/delete turn scenarios proving no orphaned message rows remain. |
| 180 | |
| 181 | ### C4) Snapshot projector barrier includes projectors not read by snapshot query |
| 182 | - Issue: |
| 183 | - Barrier can wait for projector state not represented in returned payload, causing sequence mismatch/replay confusion. |
| 184 | - Planned resolution: |
| 185 | - Align `REQUIRED_SNAPSHOT_PROJECTORS` with actual snapshot query surface. |
| 186 | - Add explicit comment/contract for inclusion criteria. |
| 187 | - Target files: |
| 188 | - `apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts` |
| 189 | - `apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts` |
| 190 | - Tests: |
| 191 | - Validate `snapshotSequence` monotonic correctness against included tables/data only. |
| 192 | |
| 193 | ### C5) NodeSqlite client resource/typing hazards |
| 194 | - Issue: |
| 195 | - `executeUnprepared` statement lifecycle and return-shape comments indicate leak/type hazards. |
| 196 | - Planned resolution: |
| 197 | - Wrap prepare/finalize in `Effect.acquireUseRelease`. |
| 198 | - Correct return typing contract and caller expectations. |
| 199 | - Target files: |
| 200 | - `apps/server/src/persistence/NodeSqliteClient.ts` |
| 201 | - `apps/server/src/persistence/NodeSqliteClient.test.ts` |
| 202 | - Tests: |
| 203 | - Resource cleanup test (finalize always invoked). |
| 204 | - Return-shape test for `run` paths. |
| 205 | |
| 206 | ### C6) Event metadata defaults created at module load are fragile |
| 207 | - Issue: |
| 208 | - Shared `defaultMetadata` object pattern risks stale defaults if future event construction forgets overrides. |
| 209 | - Planned resolution: |
| 210 | - Replace static default object with per-event metadata factory function. |
| 211 | - Avoid mutable shared default structures. |
| 212 | - Target files: |
| 213 | - `apps/server/src/orchestration/decider.ts` |
| 214 | - decider/orchestration tests |
| 215 | - Tests: |
| 216 | - Ensure unique event metadata (`eventId`, timestamps) per decision invocation. |
| 217 | |
| 218 | ### C7) Provider session directory read-modify-write lost update risk |
| 219 | - Issue: |
| 220 | - Concurrent upserts can clobber fields without transactional/version guard. |
| 221 | - Planned resolution: |
| 222 | - Introduce transaction or optimistic version check around merge-upsert path. |
| 223 | - Target files: |
| 224 | - `apps/server/src/provider/Layers/ProviderSessionDirectory.ts` |
| 225 | - corresponding test file(s) |
| 226 | - Tests: |
| 227 | - Concurrency test with competing updates preserving expected merged state. |
| 228 | |
| 229 | --- |
| 230 | |
| 231 | ## Phase D (P1/P2) - Runtime / Platform Hardening |
| 232 | |
| 233 | ### D1) WS upgrade reject path socket safety |
| 234 | - Issue: |
| 235 | - Potential `EPIPE/ECONNRESET` during reject write path and response flush concerns. |
| 236 | - Planned resolution: |
| 237 | - Attach socket error listener in reject path and use `socket.end` where appropriate. |
| 238 | - Keep behavior deterministic under disconnect-mid-handshake. |
| 239 | - Target files: |
| 240 | - `apps/server/src/wsServer.ts` |
| 241 | - `apps/server/src/wsServer.test.ts` |
| 242 | - Tests: |
| 243 | - Add handshake-disconnect scenario confirming no crash. |
| 244 | |
| 245 | ### D2) PTY adapter platform selection and lifecycle races |
| 246 | - Issue: |
| 247 | - Bun+Windows selection can hit `Effect.die`. |
| 248 | - `didExit` guard can drop buffered output. |
| 249 | - executable-fix flag can be set before successful chmod. |
| 250 | - Planned resolution: |
| 251 | - Gate Bun PTY path by platform capability. |
| 252 | - Adjust output/exit ordering to avoid dropped data. |
| 253 | - Move helper-executable flag set after successful chmod. |
| 254 | - Target files: |
| 255 | - `apps/server/src/ptyAdapter.ts` |
| 256 | - `apps/server/src/ptyAdapter.test.ts` |
| 257 | - Tests: |
| 258 | - Platform selection unit test. |
| 259 | - Output-after-exit race regression test. |
| 260 | - chmod failure retry behavior test. |
| 261 | |
| 262 | ### D3) Shell PATH probing robustness |
| 263 | - Issue: |
| 264 | - `-il` can pollute output; fish shell PATH formatting may not be colon-separated. |
| 265 | - Planned resolution: |
| 266 | - Switch probing flags/commands to deterministic non-interactive mode. |
| 267 | - Add fish-specific join handling or fallback validation. |
| 268 | - Target files: |
| 269 | - `apps/server/src/os-jank.ts` |
| 270 | - add/extend tests |
| 271 | - Tests: |
| 272 | - Parsing tests for bash/zsh/fish simulated outputs. |
| 273 | |
| 274 | ### D4) Effect callback cleanup omissions |
| 275 | - Issue: |
| 276 | - Missing cleanup function can leak server handles on interruption. |
| 277 | - Planned resolution: |
| 278 | - Ensure `Effect.callback` paths return cleanup that closes listeners/servers. |
| 279 | - Target files: |
| 280 | - `apps/server/src/config.ts` |
| 281 | - relevant tests |
| 282 | - Tests: |
| 283 | - Start/interrupt lifecycle test verifies no leaked handles. |
| 284 | |
| 285 | --- |
| 286 | |
| 287 | ## Phase E (P1/P2) - Web / Client Correctness |
| 288 | |
| 289 | ### E1) Pending approvals indicator is non-functional |
| 290 | - Issue: |
| 291 | - `pendingApprovalByThreadId` map is currently static/empty, so badge never reflects real state. |
| 292 | - Planned resolution: |
| 293 | - Derive map from authoritative read model activity/pending approval state. |
| 294 | - Remove dead map initialization pattern. |
| 295 | - Target files: |
| 296 | - `apps/web/src/components/Sidebar.tsx` |
| 297 | - add sidebar logic tests |
| 298 | - Tests: |
| 299 | - Given approval activity in thread, badge appears and clears appropriately. |
| 300 | |
| 301 | ### E2) Branch toolbar dual-write can cause flicker/inconsistent session state |
| 302 | - Issue: |
| 303 | - Local `SET_THREAD_BRANCH` + server command dispatch creates temporary contradictory state. |
| 304 | - Planned resolution: |
| 305 | - Move to single-source update path: server command + read-model sync. |
| 306 | - Keep optimistic UI only if it is reconciled safely. |
| 307 | - Target files: |
| 308 | - `apps/web/src/components/BranchToolbar.tsx` |
| 309 | - `apps/web/src/components/BranchToolbar.logic.test.ts` |
| 310 | - Tests: |
| 311 | - Ensure no local stale session overwrite when server model update arrives. |
| 312 | |
| 313 | ### E3) Markdown link parser false-positive scheme + double decode |
| 314 | - Issue: |
| 315 | - `script.ts:10` misclassified as external scheme. |
| 316 | - file URL path can be decoded twice. |
| 317 | - Planned resolution: |
| 318 | - Tighten external scheme detection (`://` or equivalent guard). |
| 319 | - Avoid second decode when source was already parsed from file URL. |
| 320 | - Target files: |
| 321 | - `apps/web/src/markdown-links.ts` |
| 322 | - `apps/web/src/markdown-links.test.ts` |
| 323 | - Tests: |
| 324 | - Add tests for `script.ts:10`, percent-encoded file names, and valid external schemes. |
| 325 | |
| 326 | ### E4) WS transport request/error edge cases |
| 327 | - Issue: |
| 328 | - Spreading params into body can override reserved keys. |
| 329 | - malformed error payload can resolve instead of reject. |
| 330 | - Planned resolution: |
| 331 | - Normalize request envelope shape to prevent key collisions. |
| 332 | - Harden error decoding fallback so promise rejects on any error object. |
| 333 | - Target files: |
| 334 | - `apps/web/src/wsTransport.ts` |
| 335 | - add/extend wsTransport tests |
| 336 | - Tests: |
| 337 | - Reserved key collision case. |
| 338 | - Error object without message still rejects with fallback. |
| 339 | |
| 340 | ### E5) Sidebar width bootstrap from localStorage can default incorrectly |
| 341 | - Issue: |
| 342 | - `Number(null) === 0` can force unintended min width on first load. |
| 343 | - Planned resolution: |
| 344 | - Explicit null/empty guard before number parse. |
| 345 | - Target files: |
| 346 | - `apps/web/src/routes/_chat.$threadId.tsx` |
| 347 | - add route/component unit test |
| 348 | - Tests: |
| 349 | - First-load with missing key preserves default width behavior. |
| 350 | |
| 351 | --- |
| 352 | |
| 353 | ## Phase F (P2) - Cleanup / Typing Hygiene |
| 354 | |
| 355 | ### F1) Symbol-safe string interpolation for error messages |
| 356 | - Issue: |
| 357 | - Interpolating raw symbol causes TypeError in some error formatting paths. |
| 358 | - Planned resolution: |
| 359 | - Use `String(cause)`/safe coercion helper consistently. |
| 360 | - Target files: |
| 361 | - `apps/server/src/orchestration/Errors.ts` |
| 362 | - `apps/server/src/provider/Layers/ProviderService.ts` |
| 363 | - Tests: |
| 364 | - Add symbol/undefined/function cause formatting tests. |
| 365 | |
| 366 | ### F2) Remove redundant union members and dead error classes if truly unused |
| 367 | - Issue: |
| 368 | - Redundant type entries and potentially dead error classes increase noise. |
| 369 | - Planned resolution: |
| 370 | - Remove redundant union members. |
| 371 | - Either remove dead classes or document retained intent with usage boundary. |
| 372 | - Target files: |
| 373 | - `apps/server/src/checkpointing/Errors.ts` |
| 374 | - related type/tests |
| 375 | - Tests: |
| 376 | - Type-level and runtime regression checks around error mapping. |
| 377 | |
| 378 | ### F3) Documentation cleanup comments |
| 379 | - Issue: |
| 380 | - Review flagged temporary marker in docs. |
| 381 | - Planned resolution: |
| 382 | - Remove transient marker if no longer needed. |
| 383 | - Target files: |
| 384 | - `AGENTS.md` |
| 385 | |
| 386 | --- |
| 387 | |
| 388 | ## Review Comment Disposition Plan |
| 389 | |
| 390 | ### Duplicate clusters to close with one canonical fix comment |
| 391 | 1. `providers.event` restoration requests (same underlying claim). |
| 392 | 2. Build script `node scripts/bundle-client.ts` (same claim repeated). |
| 393 | 3. CheckpointStore normalization duplicates. |
| 394 | 4. `handledTurnStartKeys` growth duplicates. |
| 395 | 5. checkpoint-ref helper duplication variants. |
| 396 | |
| 397 | ### Invalid/wrong clusters to close as “no action” |
| 398 | 1. Reintroduce `providers.event` channel: |
| 399 | - Current contracts/channel surface does not define it. |
| 400 | - PR is intentionally orchestration-domain-event based. |
| 401 | 2. Build script cannot run `.ts` under Node: |
| 402 | - Repository pins modern Node and script executes successfully in current environment. |
| 403 | |
| 404 | --- |
| 405 | |
| 406 | ## Verification and Quality Gates |
| 407 | |
| 408 | ### Required per phase |
| 409 | 1. Implement targeted tests for backend behavior changes (no mocking out core business logic). |
| 410 | 2. Run affected package test suites (at minimum): |
| 411 | - `bun run --cwd apps/server test` |
| 412 | - `bun run --cwd apps/web test` |
| 413 | 3. Run root lint before marking phase complete: |
| 414 | - `bun run lint` |
| 415 | |
| 416 | ### Final pre-merge gate |
| 417 | 1. Full lint pass with no new warnings introduced by this work. |
| 418 | 2. Server tests pass for orchestration/reactor/checkpoint/persistence packages. |
| 419 | 3. Web tests pass for markdown/branch/sidebar/ws transport paths. |
| 420 | 4. Resolve or explicitly disposition all active review threads with links to fixes. |
| 421 | |