authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-25 20:03:20-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-26 13:20:36-08:00
logfefea0bdf6b09a596b768047bc8432af48ba1123
tree60ebad2a77afb46b859803462ae961123d46eb44
parent6954d1b91c34a437df4f8cfd5141d1a585524424

start phase 1


16 files changed, 625 insertions(+), 4904 deletions(-)

.plans/16-pr89-review-remediation-phases.md+6-1
...@@ -1,5 +1,10 @@...@@ -1,5 +1,10 @@
1# PR #89 Review Remediation Plan (Phased)1# PR #89 Review Remediation Plan (Phased)
22
3## How To Use These Files
4
5- Working checklist with updateable status per item (single source of truth): `.plans/16c-pr89-remediation-checklist.md`
6- This file (`16-pr89-review-remediation-phases.md`): phase strategy and grouping.
7
3## Scope8## Scope
49
5- Source: GitHub review comments on PR #89 (`Add server-side orchestration engine with event sourcing`).10- Source: GitHub review comments on PR #89 (`Add server-side orchestration engine with event sourcing`).
...@@ -14,7 +19,7 @@...@@ -14,7 +19,7 @@
1419
15## Phase 0: Canonical Triage Lock20## Phase 0: Canonical Triage Lock
1621
17- Create a single tracking checklist for the 74 unique actionable findings.22- Create a single tracking checklist for the 53 currently actionable findings.
18- Map every duplicate thread to its canonical item.23- Map every duplicate thread to its canonical item.
19- Mark invalid/false-positive items with explicit rationale.24- Mark invalid/false-positive items with explicit rationale.
2025
.plans/16a-pr89-phase0-canonical-triage.json deleted-3694
...@@ -1,3694 +0,0 @@
1{
2 "pull_request": {
3 "number": 89,
4 "url": "https://github.com/pingdotgg/t3-code/pull/89",
5 "title": "Add server-side orchestration engine with event sourcing",
6 "state": "OPEN",
7 "owner": "pingdotgg",
8 "repo": "t3-code"
9 },
10 "phase0_generated_at": "2026-02-26T02:49:21.091145Z",
11 "summary": {
12 "total_threads": 185,
13 "actionable_canonical": 53,
14 "actionable_duplicates": 24,
15 "filtered_outdated": 94,
16 "filtered_resolved": 6,
17 "filtered_invalid": 8
18 },
19 "canonical_items": [
20 {
21 "canonical_id": "C001",
22 "area": "Event ordering and state consistency",
23 "title": "Non-atomic event appending can corrupt state on retry. If an error occurs mid-loop (lines 96-102) after some events are persisted but before the receipt is written, the command appears to fail. A retry generates new UUIDs via `crypto.randomUUID()` in the decider, appending duplicate events. Consider wrapping the loop in a transaction or using deterministic event IDs derived from `commandId`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
24 "severity": "High",
25 "path": "apps/server/src/orchestration/Layers/OrchestrationEngine.ts",
26 "line": 96,
27 "thread_ids": [
28 "PRRT_kwDORLtfbc5wzp4T"
29 ],
30 "thread_indexes": [
31 169
32 ],
33 "duplicate_count": 0,
34 "authors": [
35 "macroscopeapp"
36 ]
37 },
38 {
39 "canonical_id": "C002",
40 "area": "Runtime resilience and failure handling",
41 "title": "A dispatch error in `processEvent` will terminate the `Effect.forever` loop, permanently halting event ingestion. Consider adding error recovery (e.g., `Effect.catchAll` with logging) around `processEvent` so failures don't kill the fiber.",
42 "severity": "High",
43 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
44 "line": 333,
45 "thread_ids": [
46 "PRRT_kwDORLtfbc5wj4cH",
47 "PRRT_kwDORLtfbc5wnWwF",
48 "PRRT_kwDORLtfbc5wyTaP",
49 "PRRT_kwDORLtfbc5wzliw",
50 "PRRT_kwDORLtfbc5w0_g3",
51 "PRRT_kwDORLtfbc5w1HGT"
52 ],
53 "thread_indexes": [
54 119,
55 146,
56 158,
57 163,
58 170,
59 182
60 ],
61 "duplicate_count": 5,
62 "authors": [
63 "cursor",
64 "macroscopeapp"
65 ]
66 },
67 {
68 "canonical_id": "C003",
69 "area": "WebSocket robustness",
70 "title": "Consider attaching a no-op error listener before `socket.write` (e.g., `socket.on('error', () => {})`) to prevent an unhandled `EPIPE`/`ECONNRESET` from crashing the process if the client disconnects mid-handshake.",
71 "severity": "High",
72 "path": "apps/server/src/wsServer.ts",
73 "line": 75,
74 "thread_ids": [
75 "PRRT_kwDORLtfbc5v-cf4"
76 ],
77 "thread_indexes": [
78 72
79 ],
80 "duplicate_count": 0,
81 "authors": [
82 "macroscopeapp"
83 ]
84 },
85 {
86 "canonical_id": "C006",
87 "area": "Build/runtime portability",
88 "title": "Build script runs TypeScript file with bare node",
89 "severity": "Medium",
90 "path": "apps/server/package.json",
91 "line": 14,
92 "thread_ids": [
93 "PRRT_kwDORLtfbc5wxsO-"
94 ],
95 "thread_indexes": [
96 150
97 ],
98 "duplicate_count": 0,
99 "authors": [
100 "cursor"
101 ]
102 },
103 {
104 "canonical_id": "C007",
105 "area": "Build/runtime portability",
106 "title": "Build script uses `node` to run `.ts` file directly",
107 "severity": "Medium",
108 "path": "apps/server/package.json",
109 "line": 14,
110 "thread_ids": [
111 "PRRT_kwDORLtfbc5wiLNS",
112 "PRRT_kwDORLtfbc5wj5fl"
113 ],
114 "thread_indexes": [
115 106,
116 123
117 ],
118 "duplicate_count": 1,
119 "authors": [
120 "cursor"
121 ]
122 },
123 {
124 "canonical_id": "C008",
125 "area": "Checkpointing correctness",
126 "title": "Inconsistent input normalization across CheckpointStore methods",
127 "severity": "Medium",
128 "path": "apps/server/src/checkpointing/Layers/CheckpointStore.ts",
129 "line": 94,
130 "thread_ids": [
131 "PRRT_kwDORLtfbc5widJw",
132 "PRRT_kwDORLtfbc5wnWv_",
133 "PRRT_kwDORLtfbc5w0_g7",
134 "PRRT_kwDORLtfbc5w1C36"
135 ],
136 "thread_indexes": [
137 112,
138 145,
139 172,
140 176
141 ],
142 "duplicate_count": 3,
143 "authors": [
144 "cursor"
145 ]
146 },
147 {
148 "canonical_id": "C009",
149 "area": "Edge-case parsing/platform behavior",
150 "title": "Git's braced rename syntax (e.g., `src/{old => new}/file.ts`) isn't handled correctly. The current slice after ` => ` produces invalid paths like `new}/file.ts`. Consider expanding the braces to construct the full destination path. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
151 "severity": "Medium",
152 "path": "apps/server/src/git/Layers/GitCore.ts",
153 "line": 41,
154 "thread_ids": [
155 "PRRT_kwDORLtfbc5w1CxT"
156 ],
157 "thread_indexes": [
158 175
159 ],
160 "duplicate_count": 0,
161 "authors": [
162 "macroscopeapp"
163 ]
164 },
165 {
166 "canonical_id": "C010",
167 "area": "Edge-case parsing/platform behavior",
168 "title": "`loadCustomKeybindingsConfig` fails when the config file doesn't exist, which is expected for new users. Consider catching `ENOENT` and returning an empty array instead. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
169 "severity": "Medium",
170 "path": "apps/server/src/keybindings.ts",
171 "line": 418,
172 "thread_ids": [
173 "PRRT_kwDORLtfbc5wxvIJ"
174 ],
175 "thread_indexes": [
176 155
177 ],
178 "duplicate_count": 0,
179 "authors": [
180 "macroscopeapp"
181 ]
182 },
183 {
184 "canonical_id": "C011",
185 "area": "Other",
186 "title": "Template literal `${cause}` throws if `cause` is a `Symbol`. Consider using `String(cause)` instead.",
187 "severity": "Medium",
188 "path": "apps/server/src/orchestration/Errors.ts",
189 "line": 117,
190 "thread_ids": [
191 "PRRT_kwDORLtfbc5wBFv4"
192 ],
193 "thread_indexes": [
194 86
195 ],
196 "duplicate_count": 0,
197 "authors": [
198 "macroscopeapp"
199 ]
200 },
201 {
202 "canonical_id": "C012",
203 "area": "Runtime resilience and failure handling",
204 "title": "Forked revert dispatch risks read model inconsistency",
205 "severity": "Medium",
206 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
207 "line": 542,
208 "thread_ids": [
209 "PRRT_kwDORLtfbc5whszW",
210 "PRRT_kwDORLtfbc5wyTaS",
211 "PRRT_kwDORLtfbc5wzli0",
212 "PRRT_kwDORLtfbc5w0_g4",
213 "PRRT_kwDORLtfbc5w1HGX"
214 ],
215 "thread_indexes": [
216 92,
217 159,
218 165,
219 171,
220 184
221 ],
222 "duplicate_count": 4,
223 "authors": [
224 "cursor"
225 ]
226 },
227 {
228 "canonical_id": "C013",
229 "area": "Event ordering and state consistency",
230 "title": "If `projectionPipeline.projectEvent` fails after `eventStore.append` succeeds, the event is persisted but `readModel` isn't updated, causing desync. Consider updating the in-memory `readModel` immediately after append (before the external projection), so local state stays consistent regardless of downstream failures.",
231 "severity": "Medium",
232 "path": "apps/server/src/orchestration/Layers/OrchestrationEngine.ts",
233 "line": 99,
234 "thread_ids": [
235 "PRRT_kwDORLtfbc5whtrM"
236 ],
237 "thread_indexes": [
238 93
239 ],
240 "duplicate_count": 0,
241 "authors": [
242 "macroscopeapp"
243 ]
244 },
245 {
246 "canonical_id": "C014",
247 "area": "Event ordering and state consistency",
248 "title": "Engine error handler catches all errors including non-invariant ones",
249 "severity": "Medium",
250 "path": "apps/server/src/orchestration/Layers/OrchestrationEngine.ts",
251 "line": 144,
252 "thread_ids": [
253 "PRRT_kwDORLtfbc5wkPaJ"
254 ],
255 "thread_indexes": [
256 130
257 ],
258 "duplicate_count": 0,
259 "authors": [
260 "cursor"
261 ]
262 },
263 {
264 "canonical_id": "C015",
265 "area": "Event ordering and state consistency",
266 "title": "The gap-filling fallback logic can retain messages from turns that are about to be deleted, causing foreign key violations. Consider removing the fallback logic entirely, or filtering `fallbackUserMessages` and `fallbackAssistantMessages` to only include messages whose `turnId` is in `retainedTurnIds`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
267 "severity": "Medium",
268 "path": "apps/server/src/orchestration/Layers/ProjectionPipeline.ts",
269 "line": 99,
270 "thread_ids": [
271 "PRRT_kwDORLtfbc5whxJO"
272 ],
273 "thread_indexes": [
274 101
275 ],
276 "duplicate_count": 0,
277 "authors": [
278 "macroscopeapp"
279 ]
280 },
281 {
282 "canonical_id": "C016",
283 "area": "Event ordering and state consistency",
284 "title": "The in-memory `pendingTurnStartByThreadId` map isn't restored during bootstrap. If the service restarts after processing `thread.turn-start-requested` but before `thread.session-set`, the `userMessageId` and `startedAt` will be lost since bootstrap resumes *after* the committed sequence. Consider persisting this pending state or processing these two events atomically. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
285 "severity": "Medium",
286 "path": "apps/server/src/orchestration/Layers/ProjectionPipeline.ts",
287 "line": 490,
288 "thread_ids": [
289 "PRRT_kwDORLtfbc5wxvH8"
290 ],
291 "thread_indexes": [
292 151
293 ],
294 "duplicate_count": 0,
295 "authors": [
296 "macroscopeapp"
297 ]
298 },
299 {
300 "canonical_id": "C017",
301 "area": "Checkpointing correctness",
302 "title": "`REQUIRED_SNAPSHOT_PROJECTORS` includes `pending-approvals` and `thread-turns`, but `getSnapshot` doesn't query their data. If these projectors lag behind, the returned `snapshotSequence` will be lower than what the included data actually reflects, causing clients to replay already-applied events. Consider filtering `REQUIRED_SNAPSHOT_PROJECTORS` to only include projectors whose data is actually fetched in the snapshot. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
303 "severity": "Medium",
304 "path": "apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts",
305 "line": 71,
306 "thread_ids": [
307 "PRRT_kwDORLtfbc5wiLhQ"
308 ],
309 "thread_indexes": [
310 107
311 ],
312 "duplicate_count": 0,
313 "authors": [
314 "macroscopeapp"
315 ]
316 },
317 {
318 "canonical_id": "C018",
319 "area": "Memory/resource growth",
320 "title": "Unbounded memory growth in turn start deduplication set",
321 "severity": "Medium",
322 "path": "apps/server/src/orchestration/Layers/ProviderCommandReactor.ts",
323 "line": 84,
324 "thread_ids": [
325 "PRRT_kwDORLtfbc5whszQ",
326 "PRRT_kwDORLtfbc5wl2A8",
327 "PRRT_kwDORLtfbc5wyTaT",
328 "PRRT_kwDORLtfbc5wzliz",
329 "PRRT_kwDORLtfbc5w0_g-",
330 "PRRT_kwDORLtfbc5w1HGW"
331 ],
332 "thread_indexes": [
333 90,
334 140,
335 160,
336 164,
337 173,
338 183
339 ],
340 "duplicate_count": 5,
341 "authors": [
342 "cursor",
343 "macroscopeapp"
344 ]
345 },
346 {
347 "canonical_id": "C019",
348 "area": "Runtime resilience and failure handling",
349 "title": "ProviderRuntimeIngestion processes events for wrong thread on race",
350 "severity": "Medium",
351 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
352 "line": 178,
353 "thread_ids": [
354 "PRRT_kwDORLtfbc5wkPaL"
355 ],
356 "thread_indexes": [
357 131
358 ],
359 "duplicate_count": 0,
360 "authors": [
361 "cursor"
362 ]
363 },
364 {
365 "canonical_id": "C020",
366 "area": "Runtime resilience and failure handling",
367 "title": "On `message.completed`, the message ID is added to the set and `thread.message.assistant.complete` is dispatched. On `turn.completed`, the same set is iterated and `thread.message.assistant.complete` is dispatched again for each ID\u2014including already-completed ones. Consider removing message IDs from the set after dispatching on `message.completed`, or filtering out already-completed IDs before the `turn.completed` loop.",
368 "severity": "Medium",
369 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
370 "line": 266,
371 "thread_ids": [
372 "PRRT_kwDORLtfbc5w1GPr"
373 ],
374 "thread_indexes": [
375 181
376 ],
377 "duplicate_count": 0,
378 "authors": [
379 "macroscopeapp"
380 ]
381 },
382 {
383 "canonical_id": "C022",
384 "area": "Edge-case parsing/platform behavior",
385 "title": "Fish shell outputs `$PATH` as space-separated, not colon-separated. Consider checking if the shell is fish and using `string join : $PATH` instead, or validating the result contains colons before assigning.",
386 "severity": "Medium",
387 "path": "apps/server/src/os-jank.ts",
388 "line": 10,
389 "thread_ids": [
390 "PRRT_kwDORLtfbc5wkRZM"
391 ],
392 "thread_indexes": [
393 133
394 ],
395 "duplicate_count": 0,
396 "authors": [
397 "macroscopeapp"
398 ]
399 },
400 {
401 "canonical_id": "C023",
402 "area": "Edge-case parsing/platform behavior",
403 "title": "Using `-il` flags causes the shell to source profile scripts that may print banners or other text, polluting the captured `PATH`. Consider using `-lc` (login only, non-interactive) to reduce unwanted output.",
404 "severity": "Medium",
405 "path": "apps/server/src/os-jank.ts",
406 "line": 10,
407 "thread_ids": [
408 "PRRT_kwDORLtfbc5wj4cM"
409 ],
410 "thread_indexes": [
411 121
412 ],
413 "duplicate_count": 0,
414 "authors": [
415 "macroscopeapp"
416 ]
417 },
418 {
419 "canonical_id": "C024",
420 "area": "Other",
421 "title": "In `executeUnprepared`, preparing outside an effect and not closing the statement can cause uncaught errors and leaks. Suggest wrapping with `Effect.acquireUseRelease`: prepare in acquire (error-safe) and `finalize` in release.",
422 "severity": "Medium",
423 "path": "apps/server/src/persistence/NodeSqliteClient.ts",
424 "line": 145,
425 "thread_ids": [
426 "PRRT_kwDORLtfbc5whxX9"
427 ],
428 "thread_indexes": [
429 103
430 ],
431 "duplicate_count": 0,
432 "authors": [
433 "macroscopeapp"
434 ]
435 },
436 {
437 "canonical_id": "C026",
438 "area": "WebSocket robustness",
439 "title": "Consider adding `.catch(() => {})` after `Effect.runPromise(handleMessage(ws, raw))` to prevent unhandled rejections from crashing the server if `encodeResponse` or setup logic fails.",
440 "severity": "Medium",
441 "path": "apps/server/src/wsServer.ts",
442 "line": 545,
443 "thread_ids": [
444 "PRRT_kwDORLtfbc5wj4cE"
445 ],
446 "thread_indexes": [
447 118
448 ],
449 "duplicate_count": 0,
450 "authors": [
451 "macroscopeapp"
452 ]
453 },
454 {
455 "canonical_id": "C027",
456 "area": "Runtime resilience and failure handling",
457 "title": "WS message handler can cause unhandled promise rejection",
458 "severity": "Medium",
459 "path": "apps/server/src/wsServer.ts",
460 "line": 545,
461 "thread_ids": [
462 "PRRT_kwDORLtfbc5wyTaW",
463 "PRRT_kwDORLtfbc5wzli3"
464 ],
465 "thread_indexes": [
466 161,
467 167
468 ],
469 "duplicate_count": 1,
470 "authors": [
471 "cursor"
472 ]
473 },
474 {
475 "canonical_id": "C028",
476 "area": "Other",
477 "title": "Branch sync dispatches both server and stale local update",
478 "severity": "Medium",
479 "path": "apps/web/src/components/BranchToolbar.tsx",
480 "line": 102,
481 "thread_ids": [
482 "PRRT_kwDORLtfbc5v-XCu"
483 ],
484 "thread_indexes": [
485 60
486 ],
487 "duplicate_count": 0,
488 "authors": [
489 "cursor"
490 ]
491 },
492 {
493 "canonical_id": "C029",
494 "area": "Edge-case parsing/platform behavior",
495 "title": "`parseFileUrlHref` already decodes the path (line 46), but `safeDecode` is called again here, corrupting filenames containing `%` sequences. Consider skipping the decode when `fileUrlTarget` is non-null.",
496 "severity": "Medium",
497 "path": "apps/web/src/markdown-links.ts",
498 "line": 105,
499 "thread_ids": [
500 "PRRT_kwDORLtfbc5wnVsU"
501 ],
502 "thread_indexes": [
503 143
504 ],
505 "duplicate_count": 0,
506 "authors": [
507 "macroscopeapp"
508 ]
509 },
510 {
511 "canonical_id": "C030",
512 "area": "Edge-case parsing/platform behavior",
513 "title": "`EXTERNAL_SCHEME_PATTERN` matches `script.ts:10` as a scheme because `.ts:` looks like `scheme:`. Consider requiring `://` after the colon, or checking that what follows the colon is not just digits. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
514 "severity": "Medium",
515 "path": "apps/web/src/markdown-links.ts",
516 "line": 111,
517 "thread_ids": [
518 "PRRT_kwDORLtfbc5wnVsK"
519 ],
520 "thread_indexes": [
521 142
522 ],
523 "duplicate_count": 0,
524 "authors": [
525 "macroscopeapp"
526 ]
527 },
528 {
529 "canonical_id": "C032",
530 "area": "Other",
531 "title": "Dummy workflow marker accidentally committed to docs",
532 "severity": "Low",
533 "path": "AGENTS.md",
534 "line": 45,
535 "thread_ids": [
536 "PRRT_kwDORLtfbc5wlYgj",
537 "PRRT_kwDORLtfbc5w1C4H"
538 ],
539 "thread_indexes": [
540 137,
541 179
542 ],
543 "duplicate_count": 1,
544 "authors": [
545 "cursor"
546 ]
547 },
548 {
549 "canonical_id": "C033",
550 "area": "Checkpointing correctness",
551 "title": "Three error classes defined but never instantiated anywhere",
552 "severity": "Low",
553 "path": "apps/server/src/checkpointing/Errors.ts",
554 "line": 51,
555 "thread_ids": [
556 "PRRT_kwDORLtfbc5wlYgo"
557 ],
558 "thread_indexes": [
559 138
560 ],
561 "duplicate_count": 0,
562 "authors": [
563 "cursor"
564 ]
565 },
566 {
567 "canonical_id": "C034",
568 "area": "Checkpointing correctness",
569 "title": "Redundant `CheckpointInvariantError` in `CheckpointServiceError` union type",
570 "severity": "Low",
571 "path": "apps/server/src/checkpointing/Errors.ts",
572 "line": 79,
573 "thread_ids": [
574 "PRRT_kwDORLtfbc5wj5fn"
575 ],
576 "thread_indexes": [
577 124
578 ],
579 "duplicate_count": 0,
580 "authors": [
581 "cursor"
582 ]
583 },
584 {
585 "canonical_id": "C035",
586 "area": "Checkpointing correctness",
587 "title": "Redundant error type in CheckpointServiceError union definition",
588 "severity": "Low",
589 "path": "apps/server/src/checkpointing/Errors.ts",
590 "line": 79,
591 "thread_ids": [
592 "PRRT_kwDORLtfbc5wlYgs",
593 "PRRT_kwDORLtfbc5wxsO6",
594 "PRRT_kwDORLtfbc5w1C4B"
595 ],
596 "thread_indexes": [
597 139,
598 149,
599 178
600 ],
601 "duplicate_count": 2,
602 "authors": [
603 "cursor"
604 ]
605 },
606 {
607 "canonical_id": "C037",
608 "area": "Other",
609 "title": "`Effect.callback` should return a cleanup function to close the server(s) on fiber interruption. Without it, the `Net.Server` handles keep the process alive and leak the port if the effect is cancelled. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
610 "severity": "Low",
611 "path": "apps/server/src/config.ts",
612 "line": 41,
613 "thread_ids": [
614 "PRRT_kwDORLtfbc5wj4cO"
615 ],
616 "thread_indexes": [
617 122
618 ],
619 "duplicate_count": 0,
620 "authors": [
621 "macroscopeapp"
622 ]
623 },
624 {
625 "canonical_id": "C038",
626 "area": "Edge-case parsing/platform behavior",
627 "title": "Multi-byte UTF-8 characters split across chunks will be corrupted when decoding each chunk separately. Consider accumulating all chunks first, then decoding once, or use `TextDecoder` with `stream: true`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
628 "severity": "Low",
629 "path": "apps/server/src/git/Layers/CodexTextGeneration.ts",
630 "line": 136,
631 "thread_ids": [
632 "PRRT_kwDORLtfbc5w1GPo"
633 ],
634 "thread_indexes": [
635 180
636 ],
637 "duplicate_count": 0,
638 "authors": [
639 "macroscopeapp"
640 ]
641 },
642 {
643 "canonical_id": "C039",
644 "area": "Edge-case parsing/platform behavior",
645 "title": "The `+` key can be parsed (via trailing `+` handling) but cannot be encoded because `shortcut.key.includes(\"+\")` returns true for the literal `+` key. Consider checking `shortcut.key === \"+\"` separately and encoding it as `\"space\"` style (e.g., a special token), or adjusting the condition to allow the single `+` character. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
646 "severity": "Low",
647 "path": "apps/server/src/keybindings.ts",
648 "line": 352,
649 "thread_ids": [
650 "PRRT_kwDORLtfbc5wxvIB"
651 ],
652 "thread_indexes": [
653 154
654 ],
655 "duplicate_count": 0,
656 "authors": [
657 "macroscopeapp"
658 ]
659 },
660 {
661 "canonical_id": "C040",
662 "area": "Edge-case parsing/platform behavior",
663 "title": "`upsertKeybindingRule` has a race condition: concurrent calls read the same file state, then the last write overwrites earlier changes. Consider wrapping the read-modify-write sequence with `Effect.Semaphore` to serialize access. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
664 "severity": "Low",
665 "path": "apps/server/src/keybindings.ts",
666 "line": 488,
667 "thread_ids": [
668 "PRRT_kwDORLtfbc5wxvIA"
669 ],
670 "thread_indexes": [
671 153
672 ],
673 "duplicate_count": 0,
674 "authors": [
675 "macroscopeapp"
676 ]
677 },
678 {
679 "canonical_id": "C041",
680 "area": "Other",
681 "title": "Template literal interpolation throws `TypeError` if `cause` is a `Symbol`. Consider using `String(cause)` for safe coercion.",
682 "severity": "Low",
683 "path": "apps/server/src/orchestration/Errors.ts",
684 "line": 126,
685 "thread_ids": [
686 "PRRT_kwDORLtfbc5wBFWs"
687 ],
688 "thread_indexes": [
689 85
690 ],
691 "duplicate_count": 0,
692 "authors": [
693 "macroscopeapp"
694 ]
695 },
696 {
697 "canonical_id": "C042",
698 "area": "Runtime resilience and failure handling",
699 "title": "Duplicated `resolveThreadWorkspaceCwd` across three files",
700 "severity": "Low",
701 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
702 "line": 62,
703 "thread_ids": [
704 "PRRT_kwDORLtfbc5wzli2"
705 ],
706 "thread_indexes": [
707 166
708 ],
709 "duplicate_count": 0,
710 "authors": [
711 "cursor"
712 ]
713 },
714 {
715 "canonical_id": "C043",
716 "area": "Runtime resilience and failure handling",
717 "title": "Duplicated workspace CWD resolution logic across reactor modules",
718 "severity": "Low",
719 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
720 "line": 62,
721 "thread_ids": [
722 "PRRT_kwDORLtfbc5wnWwM",
723 "PRRT_kwDORLtfbc5w1C3-",
724 "PRRT_kwDORLtfbc5w1HGZ"
725 ],
726 "thread_indexes": [
727 147,
728 177,
729 185
730 ],
731 "duplicate_count": 2,
732 "authors": [
733 "cursor"
734 ]
735 },
736 {
737 "canonical_id": "C044",
738 "area": "Runtime resilience and failure handling",
739 "title": "Checkpoint reactor swallows diff errors silently for `turn.completed`",
740 "severity": "Low",
741 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
742 "line": 274,
743 "thread_ids": [
744 "PRRT_kwDORLtfbc5wkPaO"
745 ],
746 "thread_indexes": [
747 132
748 ],
749 "duplicate_count": 0,
750 "authors": [
751 "cursor"
752 ]
753 },
754 {
755 "canonical_id": "C045",
756 "area": "Runtime resilience and failure handling",
757 "title": "`truncateDetail` slices to `limit - 1` then appends `\"...\"` (3 chars), producing strings of length `limit + 2`. Consider slicing to `limit - 3` instead. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
758 "severity": "Low",
759 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
760 "line": 29,
761 "thread_ids": [
762 "PRRT_kwDORLtfbc5wzp4R"
763 ],
764 "thread_indexes": [
765 168
766 ],
767 "duplicate_count": 0,
768 "authors": [
769 "macroscopeapp"
770 ]
771 },
772 {
773 "canonical_id": "C046",
774 "area": "Runtime resilience and failure handling",
775 "title": "`latestMessageIdByTurnKey` is written to but never read, and `clearAssistantMessageIdsForTurn` doesn't clear its entries\u2014only `clearTurnStateForSession` does. Consider removing this map entirely if unused, or clearing it alongside `turnMessageIdsByTurnKey` in `clearAssistantMessageIdsForTurn`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
776 "severity": "Low",
777 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
778 "line": 133,
779 "thread_ids": [
780 "PRRT_kwDORLtfbc5wxvIQ"
781 ],
782 "thread_indexes": [
783 156
784 ],
785 "duplicate_count": 0,
786 "authors": [
787 "macroscopeapp"
788 ]
789 },
790 {
791 "canonical_id": "C047",
792 "area": "Other",
793 "title": "`SqlSchema.findOneOption` can produce both SQL errors and decode errors, but `mapError` wraps all as `PersistenceSqlError`. Consider distinguishing `ParseError` from SQL errors and mapping decode failures to `PersistenceDecodeError` instead. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
794 "severity": "Low",
795 "path": "apps/server/src/persistence/Layers/OrchestrationCommandReceipts.ts",
796 "line": 75,
797 "thread_ids": [
798 "PRRT_kwDORLtfbc5wiaR-"
799 ],
800 "thread_indexes": [
801 110
802 ],
803 "duplicate_count": 0,
804 "authors": [
805 "macroscopeapp"
806 ]
807 },
808 {
809 "canonical_id": "C048",
810 "area": "Other",
811 "title": "`statement.run()` returns a `RunResult` object, not an array. Casting it to `ReadonlyArray<any>` will cause runtime errors when consumers call array methods. Consider wrapping the result (e.g., `[result]`) or returning a properly typed object.",
812 "severity": "Low",
813 "path": "apps/server/src/persistence/NodeSqliteClient.ts",
814 "line": 99,
815 "thread_ids": [
816 "PRRT_kwDORLtfbc5wio-r"
817 ],
818 "thread_indexes": [
819 114
820 ],
821 "duplicate_count": 0,
822 "authors": [
823 "macroscopeapp"
824 ]
825 },
826 {
827 "canonical_id": "C049",
828 "area": "Other",
829 "title": "`JSON.stringify(cause)` returns `undefined` for `undefined`, functions, or symbols, violating the `string` return type. Consider coercing the result to a string (e.g., `String(JSON.stringify(cause))`) or adding a fallback.",
830 "severity": "Low",
831 "path": "apps/server/src/provider/Layers/ProviderService.ts",
832 "line": 59,
833 "thread_ids": [
834 "PRRT_kwDORLtfbc5wnVsI"
835 ],
836 "thread_indexes": [
837 141
838 ],
839 "duplicate_count": 0,
840 "authors": [
841 "macroscopeapp"
842 ]
843 },
844 {
845 "canonical_id": "C050",
846 "area": "Other",
847 "title": "The read-modify-write pattern (`getBySessionId` \u2192 merge \u2192 `upsert`) is susceptible to lost updates under concurrent writes. Consider wrapping in a transaction or adding optimistic concurrency control (e.g., version field) if concurrent session updates are expected. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
848 "severity": "Low",
849 "path": "apps/server/src/provider/Layers/ProviderSessionDirectory.ts",
850 "line": 94,
851 "thread_ids": [
852 "PRRT_kwDORLtfbc5wiLhY"
853 ],
854 "thread_indexes": [
855 108
856 ],
857 "duplicate_count": 0,
858 "authors": [
859 "macroscopeapp"
860 ]
861 },
862 {
863 "canonical_id": "C051",
864 "area": "Other",
865 "title": "Using `??` for `providerThreadId` and `adapterKey` makes it impossible to clear these fields by passing `null`, since `null ?? existing` evaluates to `existing`. Consider using explicit `undefined` checks (like `resumeCursor` does) if clearing should be supported. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
866 "severity": "Low",
867 "path": "apps/server/src/provider/Layers/ProviderSessionDirectory.ts",
868 "line": 119,
869 "thread_ids": [
870 "PRRT_kwDORLtfbc5wxvH9"
871 ],
872 "thread_indexes": [
873 152
874 ],
875 "duplicate_count": 0,
876 "authors": [
877 "macroscopeapp"
878 ]
879 },
880 {
881 "canonical_id": "C052",
882 "area": "Other",
883 "title": "Race condition: `processHandle` may be `null` when `data` callback fires, since it's assigned after `Bun.spawn` returns. Consider initializing `BunPtyProcess` first, then passing it to the callback to avoid losing initial output. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
884 "severity": "Low",
885 "path": "apps/server/src/terminal/Layers/BunPTY.ts",
886 "line": 97,
887 "thread_ids": [
888 "PRRT_kwDORLtfbc5w1CxE"
889 ],
890 "thread_indexes": [
891 174
892 ],
893 "duplicate_count": 0,
894 "authors": [
895 "macroscopeapp"
896 ]
897 },
898 {
899 "canonical_id": "C053",
900 "area": "WebSocket robustness",
901 "title": "Consider using `socket.end(response)` instead of `socket.write(response)` + `socket.destroy()` to ensure the HTTP error response is fully flushed before closing the connection.",
902 "severity": "Low",
903 "path": "apps/server/src/wsServer.ts",
904 "line": 83,
905 "thread_ids": [
906 "PRRT_kwDORLtfbc5v-WPD"
907 ],
908 "thread_indexes": [
909 57
910 ],
911 "duplicate_count": 0,
912 "authors": [
913 "macroscopeapp"
914 ]
915 },
916 {
917 "canonical_id": "C054",
918 "area": "WebSocket robustness",
919 "title": "When array chunks contain a multi-byte UTF-8 character split across boundaries, decoding each chunk separately produces replacement characters. Consider using `Buffer.concat()` on all chunks before calling `.toString(\"utf8\")`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
920 "severity": "Low",
921 "path": "apps/server/src/wsServer.ts",
922 "line": 104,
923 "thread_ids": [
924 "PRRT_kwDORLtfbc5whtrR"
925 ],
926 "thread_indexes": [
927 96
928 ],
929 "duplicate_count": 0,
930 "authors": [
931 "macroscopeapp"
932 ]
933 },
934 {
935 "canonical_id": "C056",
936 "area": "Other",
937 "title": "When `onOpenChange` is provided without `open`, the internal `_open` state never updates because `setOpenProp` takes precedence. Consider calling `_setOpen` when `openProp === undefined`, regardless of whether `setOpenProp` exists.",
938 "severity": "Low",
939 "path": "apps/web/src/components/ui/sidebar.tsx",
940 "line": 114,
941 "thread_ids": [
942 "PRRT_kwDORLtfbc5wxvIq"
943 ],
944 "thread_indexes": [
945 157
946 ],
947 "duplicate_count": 0,
948 "authors": [
949 "macroscopeapp"
950 ]
951 },
952 {
953 "canonical_id": "C057",
954 "area": "Other",
955 "title": "The `resizable` object is recreated on every render, causing `SidebarRail`'s `useEffect` to repeatedly read localStorage and update the DOM. Consider memoizing the object with `useMemo`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
956 "severity": "Low",
957 "path": "apps/web/src/routes/_chat.$threadId.tsx",
958 "line": 105,
959 "thread_ids": [
960 "PRRT_kwDORLtfbc5wyWz4"
961 ],
962 "thread_indexes": [
963 162
964 ],
965 "duplicate_count": 0,
966 "authors": [
967 "macroscopeapp"
968 ]
969 },
970 {
971 "canonical_id": "C058",
972 "area": "Other",
973 "title": "When `localStorage.getItem()` returns `null`, `Number(null)` evaluates to `0`, which passes `Number.isFinite(0)`. This causes the sidebar to clamp to `minWidth` on first load, overriding the `DIFF_INLINE_DEFAULT_WIDTH` CSS clamp. Consider checking for `null` or empty string before parsing, e.g. guard with `storedWidth === null || storedWidth === ''`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
974 "severity": "Low",
975 "path": "apps/web/src/routes/_chat.$threadId.tsx",
976 "line": 122,
977 "thread_ids": [
978 "PRRT_kwDORLtfbc5wnVsX"
979 ],
980 "thread_indexes": [
981 144
982 ],
983 "duplicate_count": 0,
984 "authors": [
985 "macroscopeapp"
986 ]
987 },
988 {
989 "canonical_id": "C059",
990 "area": "WebSocket robustness",
991 "title": "Suggestion: don\u2019t spread `params` into `body`; it can override `_tag` and mishandle non-object values. Keep `_tag` separate and nest `params` under a single key (e.g., `data`), or validate `params` is a plain object.",
992 "severity": "Low",
993 "path": "apps/web/src/wsTransport.ts",
994 "line": 59,
995 "thread_ids": [
996 "PRRT_kwDORLtfbc5whtrN"
997 ],
998 "thread_indexes": [
999 94
1000 ],
1001 "duplicate_count": 0,
1002 "authors": [
1003 "macroscopeapp"
1004 ]
1005 },
1006 {
1007 "canonical_id": "C060",
1008 "area": "Other",
1009 "title": "`defaultModel` should be `Schema.optional(Schema.NullOr(Schema.String))` to allow clearing the value. Currently there's no way to reset it to `null` since omitting means \"no change\" in patch semantics. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
1010 "severity": "Low",
1011 "path": "packages/contracts/src/orchestration.ts",
1012 "line": 253,
1013 "thread_ids": [
1014 "PRRT_kwDORLtfbc5whxJC"
1015 ],
1016 "thread_indexes": [
1017 100
1018 ],
1019 "duplicate_count": 0,
1020 "authors": [
1021 "macroscopeapp"
1022 ]
1023 }
1024 ],
1025 "threads": [
1026 {
1027 "index": 1,
1028 "thread_id": "PRRT_kwDORLtfbc5v8_mw",
1029 "path": "apps/server/src/wsServer.ts",
1030 "line": null,
1031 "is_outdated": true,
1032 "is_resolved": false,
1033 "author": "greptile-apps",
1034 "title": "calls can execute concurrently for the same thread, causing out-of-order event processing. If two provider events arrive rapidly (e.g., textDelta then turn/completed), the commands may reach the queue in unpredictable order. While the Effect queue serializes command processing, the async IIFE allows the event handler to return immediately and process the next provider event before previous dispatches complete. This can cause logical inconsistencies in message streaming or session state updates. Consider wrapping the entire event handler in a per-thread mutex or processing provider events sequentially.",
1035 "severity": "Unknown",
1036 "has_false_positive_signal": false,
1037 "phase0_status": "filtered_outdated",
1038 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1039 },
1040 {
1041 "index": 2,
1042 "thread_id": "PRRT_kwDORLtfbc5v8_m4",
1043 "path": "apps/server/src/wsServer.ts",
1044 "line": null,
1045 "is_outdated": true,
1046 "is_resolved": false,
1047 "author": "greptile-apps",
1048 "title": "returns the current in-memory read model, but it may not reflect recently dispatched commands that are still queued. If a provider event arrives before the previous command's read model update propagates, `thread` lookup may fail or use stale data. This is especially problematic during rapid event streams where session creation and message events arrive in quick succession.",
1049 "severity": "Unknown",
1050 "has_false_positive_signal": false,
1051 "phase0_status": "filtered_outdated",
1052 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1053 },
1054 {
1055 "index": 3,
1056 "thread_id": "PRRT_kwDORLtfbc5v8_m_",
1057 "path": "apps/server/src/orchestration/engine.ts",
1058 "line": null,
1059 "is_outdated": true,
1060 "is_resolved": false,
1061 "author": "greptile-apps",
1062 "title": "catches an error during event processing (line 201-205), it creates a new Error and calls `envelope.reject()`, but the original error is not logged. This makes debugging event processing failures very difficult. Since the `catch` callback returns `undefined`, the stream continues processing subsequent commands even after failures, which may lead to inconsistent state if error recovery is needed.",
1063 "severity": "Unknown",
1064 "has_false_positive_signal": false,
1065 "phase0_status": "filtered_outdated",
1066 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1067 },
1068 {
1069 "index": 4,
1070 "thread_id": "PRRT_kwDORLtfbc5v8_nN",
1071 "path": "apps/server/src/orchestration/engine.ts",
1072 "line": null,
1073 "is_outdated": true,
1074 "is_resolved": false,
1075 "author": "greptile-apps",
1076 "title": "runs PubSub publishes in parallel with synchronous listener callbacks (lines 193-198), but the listeners fire before waiting for PubSub to complete. If PubSub publish fails, listeners already received the update, creating inconsistency between Effect subscribers and direct listeners. The envelope is also resolved (line 199) before verifying both PubSub publishes succeeded.",
1077 "severity": "Unknown",
1078 "has_false_positive_signal": false,
1079 "phase0_status": "filtered_outdated",
1080 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1081 },
1082 {
1083 "index": 5,
1084 "thread_id": "PRRT_kwDORLtfbc5v8_nb",
1085 "path": "apps/server/src/orchestration/reducer.ts",
1086 "line": null,
1087 "is_outdated": true,
1088 "is_resolved": false,
1089 "author": "greptile-apps",
1090 "title": "is false and `message.text.length > 0`, the entire text is replaced (line 150). However, if `message.text` is empty string and `streaming` is false (like the turn/completed event at wsServer.ts:198), it preserves `entry.text` (line 152). This creates an edge case: if a streaming message never sent any text deltas, then receives a finalizing empty-text event, the message keeps empty text from initialization instead of the intended final state.",
1091 "severity": "Unknown",
1092 "has_false_positive_signal": false,
1093 "phase0_status": "filtered_outdated",
1094 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1095 },
1096 {
1097 "index": 6,
1098 "thread_id": "PRRT_kwDORLtfbc5v8_zA",
1099 "path": "apps/web/src/routes/__root.tsx",
1100 "line": null,
1101 "is_outdated": true,
1102 "is_resolved": true,
1103 "author": "macroscopeapp",
1104 "title": "Race condition: if `getSnapshot()` resolves after `onReadModel` fires, stale data overwrites newer state. Also, pending promises from previous effect runs can overwrite current state. Consider adding a `canceled` flag that's set in the cleanup, and check it before dispatching. \u2705 Resolved in b2f29834553747157b9eba6a5fa9de1b84693dce",
1105 "severity": "High",
1106 "has_false_positive_signal": false,
1107 "phase0_status": "filtered_outdated",
1108 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1109 },
1110 {
1111 "index": 7,
1112 "thread_id": "PRRT_kwDORLtfbc5v8_zC",
1113 "path": "apps/server/src/orchestration/engine.ts",
1114 "line": null,
1115 "is_outdated": true,
1116 "is_resolved": true,
1117 "author": "macroscopeapp",
1118 "title": "`replayEvents` uses the default `limit` of 1,000 from `readFromSequence`, which may silently truncate results. Consider passing `Number.MAX_SAFE_INTEGER` (like `readAll` does) to return all events. Code refactored as of 9ef01eb97e9a4e7e174e383350febd08951a0dc3",
1119 "severity": "High",
1120 "has_false_positive_signal": false,
1121 "phase0_status": "filtered_outdated",
1122 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1123 },
1124 {
1125 "index": 8,
1126 "thread_id": "PRRT_kwDORLtfbc5v8_zD",
1127 "path": "apps/server/src/orchestration/engine.ts",
1128 "line": null,
1129 "is_outdated": true,
1130 "is_resolved": true,
1131 "author": "macroscopeapp",
1132 "title": "Consider draining the `commandQueue` and rejecting pending envelopes in `stop()` after interrupting the fiber. Otherwise, callers awaiting `dispatch()` will hang indefinitely. Code refactored as of 9ef01eb97e9a4e7e174e383350febd08951a0dc3",
1133 "severity": "High",
1134 "has_false_positive_signal": false,
1135 "phase0_status": "filtered_outdated",
1136 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1137 },
1138 {
1139 "index": 9,
1140 "thread_id": "PRRT_kwDORLtfbc5v8_zE",
1141 "path": "apps/server/src/orchestration/engine.ts",
1142 "line": null,
1143 "is_outdated": true,
1144 "is_resolved": true,
1145 "author": "macroscopeapp",
1146 "title": "Listener exceptions after line 187 cause `envelope.reject()` to fire despite the event already being persisted and `readModel` updated. Consider moving listener notifications outside the `try` block (or catching their errors separately) so that persistence success always resolves the promise, preventing duplicate events from retry attempts. Code refactored as of 9ef01eb97e9a4e7e174e383350febd08951a0dc3",
1147 "severity": "High",
1148 "has_false_positive_signal": false,
1149 "phase0_status": "filtered_outdated",
1150 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1151 },
1152 {
1153 "index": 10,
1154 "thread_id": "PRRT_kwDORLtfbc5v8_zH",
1155 "path": "apps/server/src/orchestration/eventStore.ts",
1156 "line": null,
1157 "is_outdated": true,
1158 "is_resolved": true,
1159 "author": "macroscopeapp",
1160 "title": "`readAll()` loads the entire event table into memory synchronously, which will eventually cause OOM as the store grows. Consider using pagination or streaming, or document why unbounded loading is acceptable for the expected dataset size. Code refactored as of 10bd27da3d7c869d7ea9c4bd824f9ee730bd1792",
1161 "severity": "Medium",
1162 "has_false_positive_signal": false,
1163 "phase0_status": "filtered_outdated",
1164 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1165 },
1166 {
1167 "index": 11,
1168 "thread_id": "PRRT_kwDORLtfbc5v8_zJ",
1169 "path": "apps/server/src/orchestration/engine.ts",
1170 "line": null,
1171 "is_outdated": true,
1172 "is_resolved": true,
1173 "author": "macroscopeapp",
1174 "title": "`readAll()` loads the entire event history into memory at startup. As the event store grows, this will eventually exceed heap limits and crash the process. Consider paginating the replay (e.g., fetch events in batches of 1000 and reduce incrementally). Code refactored as of 9ef01eb97e9a4e7e174e383350febd08951a0dc3",
1175 "severity": "Medium",
1176 "has_false_positive_signal": false,
1177 "phase0_status": "filtered_outdated",
1178 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1179 },
1180 {
1181 "index": 12,
1182 "thread_id": "PRRT_kwDORLtfbc5v8_zK",
1183 "path": "apps/web/src/session-logic.ts",
1184 "line": null,
1185 "is_outdated": true,
1186 "is_resolved": true,
1187 "author": "macroscopeapp",
1188 "title": "The regex `(.+?)` stops at the first ` b/`, so paths containing ` b/` (e.g., `folder b/file`) are parsed incorrectly. Consider using the fact that source and destination paths are identical for most cases, or parsing from the `---`/`+++` lines instead. No longer relevant as of ef341394699edcf9ab21af4a7bbf0186a87ebce5",
1189 "severity": "Medium",
1190 "has_false_positive_signal": false,
1191 "phase0_status": "filtered_outdated",
1192 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1193 },
1194 {
1195 "index": 13,
1196 "thread_id": "PRRT_kwDORLtfbc5v9AdO",
1197 "path": "apps/server/src/wsServer.ts",
1198 "line": null,
1199 "is_outdated": true,
1200 "is_resolved": true,
1201 "author": "cursor",
1202 "title": "Failed turn sets session status to \"ready\" instead of \"error\"",
1203 "severity": "High",
1204 "has_false_positive_signal": false,
1205 "phase0_status": "filtered_outdated",
1206 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1207 },
1208 {
1209 "index": 14,
1210 "thread_id": "PRRT_kwDORLtfbc5v9AdQ",
1211 "path": "apps/web/src/components/Sidebar.tsx",
1212 "line": 252,
1213 "is_outdated": true,
1214 "is_resolved": true,
1215 "author": "cursor",
1216 "title": "Duplicate project created in Electron add-project flow",
1217 "severity": "High",
1218 "has_false_positive_signal": false,
1219 "phase0_status": "filtered_outdated",
1220 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1221 },
1222 {
1223 "index": 15,
1224 "thread_id": "PRRT_kwDORLtfbc5v9AdS",
1225 "path": "apps/web/src/components/Sidebar.tsx",
1226 "line": null,
1227 "is_outdated": true,
1228 "is_resolved": true,
1229 "author": "cursor",
1230 "title": "Navigation races ahead of thread creation in store",
1231 "severity": "Medium",
1232 "has_false_positive_signal": false,
1233 "phase0_status": "filtered_outdated",
1234 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1235 },
1236 {
1237 "index": 16,
1238 "thread_id": "PRRT_kwDORLtfbc5v9AdT",
1239 "path": "apps/server/src/wsServer.ts",
1240 "line": null,
1241 "is_outdated": true,
1242 "is_resolved": true,
1243 "author": "cursor",
1244 "title": "Session close/exit events not reflected in orchestration state",
1245 "severity": "Medium",
1246 "has_false_positive_signal": false,
1247 "phase0_status": "filtered_outdated",
1248 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1249 },
1250 {
1251 "index": 17,
1252 "thread_id": "PRRT_kwDORLtfbc5v9AdU",
1253 "path": "apps/server/src/wsServer.ts",
1254 "line": null,
1255 "is_outdated": true,
1256 "is_resolved": true,
1257 "author": "cursor",
1258 "title": "Double `getSnapshot()` call may produce inconsistent data",
1259 "severity": "Low",
1260 "has_false_positive_signal": false,
1261 "phase0_status": "filtered_outdated",
1262 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1263 },
1264 {
1265 "index": 18,
1266 "thread_id": "PRRT_kwDORLtfbc5v9AdW",
1267 "path": "apps/web/src/session-logic.ts",
1268 "line": null,
1269 "is_outdated": true,
1270 "is_resolved": true,
1271 "author": "cursor",
1272 "title": "Timeline entry IDs lose uniqueness-guaranteeing prefix",
1273 "severity": "Low",
1274 "has_false_positive_signal": false,
1275 "phase0_status": "filtered_outdated",
1276 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1277 },
1278 {
1279 "index": 19,
1280 "thread_id": "PRRT_kwDORLtfbc5v9AdX",
1281 "path": "apps/web/src/components/ChatView.tsx",
1282 "line": null,
1283 "is_outdated": true,
1284 "is_resolved": true,
1285 "author": "cursor",
1286 "title": "Supervised mode approval prompts never shown to users",
1287 "severity": "High",
1288 "has_false_positive_signal": false,
1289 "phase0_status": "filtered_outdated",
1290 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1291 },
1292 {
1293 "index": 20,
1294 "thread_id": "PRRT_kwDORLtfbc5v9AdY",
1295 "path": "apps/web/src/components/ChatView.tsx",
1296 "line": null,
1297 "is_outdated": true,
1298 "is_resolved": true,
1299 "author": "cursor",
1300 "title": "Checkpoint revert doesn't update orchestration read model state",
1301 "severity": "High",
1302 "has_false_positive_signal": false,
1303 "phase0_status": "filtered_outdated",
1304 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1305 },
1306 {
1307 "index": 21,
1308 "thread_id": "PRRT_kwDORLtfbc5v9AdZ",
1309 "path": "apps/server/src/wsServer.test.ts",
1310 "line": null,
1311 "is_outdated": true,
1312 "is_resolved": true,
1313 "author": "cursor",
1314 "title": "Test helper omits `stateDir`, writing SQLite to home directory",
1315 "severity": "Medium",
1316 "has_false_positive_signal": false,
1317 "phase0_status": "filtered_outdated",
1318 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1319 },
1320 {
1321 "index": 22,
1322 "thread_id": "PRRT_kwDORLtfbc5v9Ada",
1323 "path": "apps/server/src/orchestration/engine.ts",
1324 "line": null,
1325 "is_outdated": true,
1326 "is_resolved": true,
1327 "author": "cursor",
1328 "title": "SQLite database connection never closed on engine stop",
1329 "severity": "Medium",
1330 "has_false_positive_signal": false,
1331 "phase0_status": "filtered_outdated",
1332 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1333 },
1334 {
1335 "index": 23,
1336 "thread_id": "PRRT_kwDORLtfbc5v9Anx",
1337 "path": "apps/server/package.json",
1338 "line": null,
1339 "is_outdated": true,
1340 "is_resolved": false,
1341 "author": "coderabbitai",
1342 "title": "What typically goes wrong",
1343 "severity": "High",
1344 "has_false_positive_signal": false,
1345 "phase0_status": "filtered_outdated",
1346 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1347 },
1348 {
1349 "index": 24,
1350 "thread_id": "PRRT_kwDORLtfbc5v9Any",
1351 "path": "apps/server/src/orchestration/reducer.ts",
1352 "line": null,
1353 "is_outdated": true,
1354 "is_resolved": true,
1355 "author": "coderabbitai",
1356 "title": "is missing, prefer a deterministic fallback (e.g., `event.eventId`) or drop the event. <details> <summary>\ud83d\udd27 Suggested fix (deterministic IDs)</summary>",
1357 "severity": "High",
1358 "has_false_positive_signal": false,
1359 "phase0_status": "filtered_outdated",
1360 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1361 },
1362 {
1363 "index": 25,
1364 "thread_id": "PRRT_kwDORLtfbc5v9Anz",
1365 "path": "apps/server/src/wsServer.test.ts",
1366 "line": null,
1367 "is_outdated": true,
1368 "is_resolved": false,
1369 "author": "coderabbitai",
1370 "title": "for runtime setup. Clean up external resources in `finally` blocks.\" <details> <summary>\ud83e\udd16 Prompt for AI Agents</summary>",
1371 "severity": "Medium",
1372 "has_false_positive_signal": false,
1373 "phase0_status": "filtered_outdated",
1374 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1375 },
1376 {
1377 "index": 26,
1378 "thread_id": "PRRT_kwDORLtfbc5v9An0",
1379 "path": "apps/server/src/wsServer.ts",
1380 "line": null,
1381 "is_outdated": true,
1382 "is_resolved": false,
1383 "author": "coderabbitai",
1384 "title": "stream expected by the web app and hides orchestration dispatch failures, which makes state drift hard to debug. Please broadcast the provider event and log failures. <details> <summary>\ud83d\udd27 Suggested fix (broadcast + logging)</summary>",
1385 "severity": "High",
1386 "has_false_positive_signal": false,
1387 "phase0_status": "filtered_outdated",
1388 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1389 },
1390 {
1391 "index": 27,
1392 "thread_id": "PRRT_kwDORLtfbc5v9An1",
1393 "path": "apps/server/src/wsServer.ts",
1394 "line": null,
1395 "is_outdated": true,
1396 "is_resolved": false,
1397 "author": "coderabbitai",
1398 "title": "rejects but the server is already listening; this leaves a partially running server with no orchestration worker. <details> <summary>\ud83d\udd27 Suggested fix</summary>",
1399 "severity": "High",
1400 "has_false_positive_signal": false,
1401 "phase0_status": "filtered_outdated",
1402 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1403 },
1404 {
1405 "index": 28,
1406 "thread_id": "PRRT_kwDORLtfbc5v9An3",
1407 "path": "apps/web/src/components/ChatView.tsx",
1408 "line": null,
1409 "is_outdated": true,
1410 "is_resolved": false,
1411 "author": "coderabbitai",
1412 "title": "and `derivePendingApprovals` means work log entries and pending approvals will never render, and it effectively drops provider event stream consumption. Please wire these to the provider event feed (or the orchestration read-model) instead of hard-coded empties. As per coding guidelines: \"Web app must consume provider event streams via WebSocket push on channel `providers.event`\". <details> <summary>\ud83e\udd16 Prompt for AI Agents</summary>",
1413 "severity": "High",
1414 "has_false_positive_signal": false,
1415 "phase0_status": "filtered_outdated",
1416 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1417 },
1418 {
1419 "index": 29,
1420 "thread_id": "PRRT_kwDORLtfbc5v9An4",
1421 "path": "apps/web/src/components/ChatView.tsx",
1422 "line": null,
1423 "is_outdated": true,
1424 "is_resolved": false,
1425 "author": "coderabbitai",
1426 "title": "command (SendMessageCommandSchema) omits attachments\u2014only text is sent to orchestration. While attachments are correctly forwarded to the provider API separately (line 1752), the composer images are cleared immediately (line 1723) before attachment metadata can be persisted to the message itself. Since OrchestrationMessageSchema has no attachments field and the read-model's messages derive from orchestration events, user images will vanish on page refresh or if the read-model becomes the authoritative source. To preserve attachments: either add attachment metadata to SendMessageCommandSchema and the message reducer, or maintain a local optimistic state mapping userMessageId \u2192 attachments until the read-model is updated to include attachment data. <details> <summary>\ud83e\udd16 Prompt for AI Agents</summary>",
1427 "severity": "High",
1428 "has_false_positive_signal": false,
1429 "phase0_status": "filtered_outdated",
1430 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1431 },
1432 {
1433 "index": 30,
1434 "thread_id": "PRRT_kwDORLtfbc5v9An5",
1435 "path": "apps/web/src/components/Sidebar.tsx",
1436 "line": null,
1437 "is_outdated": true,
1438 "is_resolved": false,
1439 "author": "coderabbitai",
1440 "title": "is never populated.** This map is created once and never updated, so the \u201cAwaiting response\u201d pill will never show. Reintroduce `derivePendingApprovals` or feed this from the read-model/provider events. <details> <summary>\ud83e\udd16 Prompt for AI Agents</summary>",
1441 "severity": "Medium",
1442 "has_false_positive_signal": false,
1443 "phase0_status": "filtered_outdated",
1444 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1445 },
1446 {
1447 "index": 31,
1448 "thread_id": "PRRT_kwDORLtfbc5v9An7",
1449 "path": "apps/web/src/routes/__root.tsx",
1450 "line": 140,
1451 "is_outdated": false,
1452 "is_resolved": false,
1453 "author": "coderabbitai",
1454 "title": "channel.** The web app currently does not consume provider event streams via the `providers.event` channel. While the orchestration API has `onReadModel()` and `onDomainEvent()` subscriptions, the providers event stream subscription is missing. Add a subscription to `ORCHESTRATION_WS_CHANNELS.event` (or the appropriate providers event channel from contracts) in `wsNativeApi.ts` and expose it via the `api.providers` object to comply with the guideline requirement. <details> <summary>\ud83e\udd16 Prompt for AI Agents</summary>",
1455 "severity": "High",
1456 "has_false_positive_signal": false,
1457 "phase0_status": "filtered_invalid",
1458 "phase0_reason": "Invalid by design: provider runtime events are intentionally surfaced through orchestration domain events (`orchestration.domainEvent`), not a separate `providers.event` WebSocket channel in the current architecture."
1459 },
1460 {
1461 "index": 32,
1462 "thread_id": "PRRT_kwDORLtfbc5v9An8",
1463 "path": "apps/web/src/session-logic.ts",
1464 "line": null,
1465 "is_outdated": true,
1466 "is_resolved": false,
1467 "author": "coderabbitai",
1468 "title": "and `deriveWorkLogEntries` are actively consumed in ChatView.tsx but always return empty arrays. They're called with empty event arrays and never process actual data, so pending approvals and work log entries will never appear in the UI. `deriveTurnDiffSummaries` is not consumed anywhere\u2014it's been superseded by the `useTurnDiffSummaries` hook, which reads directly from the orchestration read model (`activeThread.turnDiffSummaries`). Consider removing `derivePendingApprovals` and `deriveWorkLogEntries` entirely or implement them to consume from the orchestration read model, matching the pattern established by `useTurnDiffSummaries`. <details> <summary>\ud83e\udd16 Prompt for AI Agents</summary>",
1469 "severity": "High",
1470 "has_false_positive_signal": false,
1471 "phase0_status": "filtered_outdated",
1472 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1473 },
1474 {
1475 "index": 33,
1476 "thread_id": "PRRT_kwDORLtfbc5v9An9",
1477 "path": "apps/web/src/session-logic.ts",
1478 "line": null,
1479 "is_outdated": true,
1480 "is_resolved": false,
1481 "author": "coderabbitai",
1482 "title": "(e.g., orchestration turn-diff types) instead of app-local interfaces to keep contracts aligned. As per coding guidelines: `apps/**/*.{ts,tsx}: Use Zod schemas from packages/contracts for shared type contracts covering provider events, WebSocket protocol, and model/session types`. <details> <summary>\ud83e\udd16 Prompt for AI Agents</summary>",
1483 "severity": "High",
1484 "has_false_positive_signal": false,
1485 "phase0_status": "filtered_outdated",
1486 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1487 },
1488 {
1489 "index": 34,
1490 "thread_id": "PRRT_kwDORLtfbc5v9An-",
1491 "path": "apps/web/src/wsNativeApi.ts",
1492 "line": 165,
1493 "is_outdated": false,
1494 "is_resolved": false,
1495 "author": "coderabbitai",
1496 "title": "push stream, and the callbacks rely on unchecked casts. Please reintroduce a `providers.onEvent` (or equivalent) that subscribes to the `providers.event` channel and decode read-model/domain-event payloads using the shared contracts schemas before invoking callbacks. As per coding guidelines: \"Web app must consume provider event streams via WebSocket push on channel `providers.event`\" and \"Use Zod schemas from `packages/contracts` for shared type contracts covering provider events, WebSocket protocol, and model/session types\". <details> <summary>\ud83e\udd16 Prompt for AI Agents</summary>",
1497 "severity": "High",
1498 "has_false_positive_signal": false,
1499 "phase0_status": "filtered_invalid",
1500 "phase0_reason": "Invalid by design: provider runtime events are intentionally surfaced through orchestration domain events (`orchestration.domainEvent`), not a separate `providers.event` WebSocket channel in the current architecture."
1501 },
1502 {
1503 "index": 35,
1504 "thread_id": "PRRT_kwDORLtfbc5v9RP6",
1505 "path": "apps/server/src/orchestration/reducer.ts",
1506 "line": null,
1507 "is_outdated": true,
1508 "is_resolved": true,
1509 "author": "macroscopeapp",
1510 "title": "After sorting `turnDiffSummaries`, `latestTurnId` should come from the last element of the sorted array, not unconditionally from the incoming event. Out-of-order events would desync `latestTurnId` from the actual latest summary. No longer relevant as of b2f29834553747157b9eba6a5fa9de1b84693dce",
1511 "severity": "High",
1512 "has_false_positive_signal": false,
1513 "phase0_status": "filtered_outdated",
1514 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1515 },
1516 {
1517 "index": 36,
1518 "thread_id": "PRRT_kwDORLtfbc5v9RP8",
1519 "path": "apps/server/src/orchestration/engine.ts",
1520 "line": null,
1521 "is_outdated": true,
1522 "is_resolved": true,
1523 "author": "macroscopeapp",
1524 "title": "A single command processing error terminates the worker stream, causing all subsequent commands to hang. Consider wrapping the `Effect.tryPromise` in `Effect.catchAll(() => Effect.succeed(undefined))` so the stream continues processing after failures. \u2705 Addressed in c3b7cc713c7f67d5130096bb0f03e14485e26e72",
1525 "severity": "High",
1526 "has_false_positive_signal": false,
1527 "phase0_status": "filtered_outdated",
1528 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1529 },
1530 {
1531 "index": 37,
1532 "thread_id": "PRRT_kwDORLtfbc5v9RtX",
1533 "path": "apps/server/src/orchestration/engine.ts",
1534 "line": null,
1535 "is_outdated": true,
1536 "is_resolved": false,
1537 "author": "coderabbitai",
1538 "title": "around lines 235 - 240, The loops invoking this.domainEventListeners and this.readModelListeners should wrap each listener call in a try/catch so a thrown error from one listener does not stop subsequent listeners or cause envelope rejection; update the blocks around listener(savedEvent) and listener(this.readModel) to call each listener inside a try { await/ call } catch (err) { processLogger.error(...) or this.logger.error(...) with context including the listener identity and the savedEvent/readModel } and continue to the next listener, ensuring errors are isolated and logged.",
1539 "severity": "High",
1540 "has_false_positive_signal": false,
1541 "phase0_status": "filtered_outdated",
1542 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1543 },
1544 {
1545 "index": 38,
1546 "thread_id": "PRRT_kwDORLtfbc5v9Rtc",
1547 "path": "apps/server/src/orchestration/eventStore.ts",
1548 "line": null,
1549 "is_outdated": true,
1550 "is_resolved": false,
1551 "author": "coderabbitai",
1552 "title": "with a Bun-compatible SQLite driver.** The `node:sqlite` module is a Node.js 22.6+ built-in that Bun does not support. CI runs exclusively on Bun 1.3.9, and `OrchestrationEngine` (which uses `SqliteEventStore`) is imported in `wsServer.ts` and tested in `engine.test.ts`. When `bun run test` executes in CI, it will fail to load this code with \"No such built-in module: node:sqlite\". Switch to `bun:sqlite` (which is built-in to Bun) or add a packaged SQLite driver (e.g., `better-sqlite3`, `sql.js`) to apps/server dependencies and abstract the driver behind an interface for portability. <details> <summary>\ud83e\udd16 Prompt for AI Agents</summary>",
1553 "severity": "High",
1554 "has_false_positive_signal": false,
1555 "phase0_status": "filtered_outdated",
1556 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1557 },
1558 {
1559 "index": 39,
1560 "thread_id": "PRRT_kwDORLtfbc5v9SCj",
1561 "path": "apps/server/src/wsServer.ts",
1562 "line": null,
1563 "is_outdated": true,
1564 "is_resolved": false,
1565 "author": "cursor",
1566 "title": "HTTP server accepts connections before engine is ready",
1567 "severity": "Medium",
1568 "has_false_positive_signal": false,
1569 "phase0_status": "filtered_outdated",
1570 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1571 },
1572 {
1573 "index": 40,
1574 "thread_id": "PRRT_kwDORLtfbc5v9SCk",
1575 "path": "apps/server/src/orchestration/engine.ts",
1576 "line": null,
1577 "is_outdated": true,
1578 "is_resolved": false,
1579 "author": "cursor",
1580 "title": "Unused PubSub instances published to without subscribers",
1581 "severity": "Low",
1582 "has_false_positive_signal": false,
1583 "phase0_status": "filtered_outdated",
1584 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1585 },
1586 {
1587 "index": 41,
1588 "thread_id": "PRRT_kwDORLtfbc5v9SCp",
1589 "path": "apps/web/src/session-logic.ts",
1590 "line": 127,
1591 "is_outdated": false,
1592 "is_resolved": false,
1593 "author": "cursor",
1594 "title": "Revert uses wrong turn count from positional inference",
1595 "severity": "Medium",
1596 "has_false_positive_signal": false,
1597 "phase0_status": "filtered_invalid",
1598 "phase0_reason": "Invalid after post-rewrite audit: Revert now uses explicit checkpointTurnCount first; positional fallback is non-primary."
1599 },
1600 {
1601 "index": 42,
1602 "thread_id": "PRRT_kwDORLtfbc5v9SCt",
1603 "path": "apps/web/src/components/ChatView.tsx",
1604 "line": null,
1605 "is_outdated": true,
1606 "is_resolved": false,
1607 "author": "cursor",
1608 "title": "`syncCheckpointTurnCounts` discards result, becoming a no-op",
1609 "severity": "Low",
1610 "has_false_positive_signal": false,
1611 "phase0_status": "filtered_outdated",
1612 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1613 },
1614 {
1615 "index": 43,
1616 "thread_id": "PRRT_kwDORLtfbc5v9gBJ",
1617 "path": "apps/server/src/orchestration/engine.ts",
1618 "line": null,
1619 "is_outdated": true,
1620 "is_resolved": true,
1621 "author": "macroscopeapp",
1622 "title": "Consider adding a guard in `dispatch` to throw if the engine hasn't been started, since the worker fiber won't be running to process commands, causing `Deferred.await` to hang indefinitely. \u2705 Addressed in 9ef01eb97e9a4e7e174e383350febd08951a0dc3",
1623 "severity": "High",
1624 "has_false_positive_signal": false,
1625 "phase0_status": "filtered_outdated",
1626 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1627 },
1628 {
1629 "index": 44,
1630 "thread_id": "PRRT_kwDORLtfbc5v9gX0",
1631 "path": "apps/web/src/components/Sidebar.tsx",
1632 "line": null,
1633 "is_outdated": true,
1634 "is_resolved": false,
1635 "author": "cursor",
1636 "title": "Double project deletion dispatch in Electron mode",
1637 "severity": "Low",
1638 "has_false_positive_signal": false,
1639 "phase0_status": "filtered_outdated",
1640 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1641 },
1642 {
1643 "index": 45,
1644 "thread_id": "PRRT_kwDORLtfbc5v9gX1",
1645 "path": "apps/server/src/wsServer.ts",
1646 "line": null,
1647 "is_outdated": true,
1648 "is_resolved": false,
1649 "author": "cursor",
1650 "title": "Every streaming token persisted as separate database event",
1651 "severity": "High",
1652 "has_false_positive_signal": false,
1653 "phase0_status": "filtered_outdated",
1654 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1655 },
1656 {
1657 "index": 46,
1658 "thread_id": "PRRT_kwDORLtfbc5v9gX2",
1659 "path": "apps/server/src/orchestration/reducer.ts",
1660 "line": null,
1661 "is_outdated": true,
1662 "is_resolved": false,
1663 "author": "cursor",
1664 "title": "Reducer overwrites Codex runtime thread ID with orchestration ID",
1665 "severity": "High",
1666 "has_false_positive_signal": false,
1667 "phase0_status": "filtered_outdated",
1668 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1669 },
1670 {
1671 "index": 47,
1672 "thread_id": "PRRT_kwDORLtfbc5v9gX4",
1673 "path": "apps/web/src/routes/__root.tsx",
1674 "line": null,
1675 "is_outdated": true,
1676 "is_resolved": false,
1677 "author": "cursor",
1678 "title": "Bootstrap creates duplicate project on server restart",
1679 "severity": "Medium",
1680 "has_false_positive_signal": false,
1681 "phase0_status": "filtered_outdated",
1682 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1683 },
1684 {
1685 "index": 48,
1686 "thread_id": "PRRT_kwDORLtfbc5v9gcy",
1687 "path": "apps/server/src/orchestration/Layer.ts",
1688 "line": null,
1689 "is_outdated": true,
1690 "is_resolved": false,
1691 "author": "coderabbitai",
1692 "title": "with `catchAll` to avoid catching defects.** Using `catchAllCause` will catch both expected errors and defects (bugs). Per project conventions, use `catchAll` or `mapError` instead so that genuine bugs propagate as failures rather than being silently caught and turned into user-facing errors. <details> <summary>\ud83d\udee1\ufe0f Proposed fix</summary>",
1693 "severity": "High",
1694 "has_false_positive_signal": false,
1695 "phase0_status": "filtered_outdated",
1696 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1697 },
1698 {
1699 "index": 49,
1700 "thread_id": "PRRT_kwDORLtfbc5v9gc0",
1701 "path": "apps/server/src/persistence/Layers/OrchestrationEvents.ts",
1702 "line": null,
1703 "is_outdated": true,
1704 "is_resolved": false,
1705 "author": "coderabbitai",
1706 "title": "from DB operations\u2014it masks expected failures as defects.** `JSON.parse` and `Schema.decodeUnknownSync` can throw expected errors (invalid payloads, malformed JSON). Converting these to defects via `orDie` crashes orchestration flows instead of handling them predictably. Update `OrchestrationEventRepositoryShape` to return typed errors (e.g., `Effect.Effect<OrchestrationEvent, ParseError | ValidationError>`) and remove `orDie` from both `append` and `readFromSequence`. <details> <summary>Suggested starting point</summary>",
1707 "severity": "High",
1708 "has_false_positive_signal": false,
1709 "phase0_status": "filtered_outdated",
1710 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1711 },
1712 {
1713 "index": 50,
1714 "thread_id": "PRRT_kwDORLtfbc5v9gc2",
1715 "path": "apps/server/src/persistence/Layers/OrchestrationEvents.ts",
1716 "line": null,
1717 "is_outdated": true,
1718 "is_resolved": true,
1719 "author": "coderabbitai",
1720 "title": "\ud83d\udca1 Result: In **Effect (TypeScript)**, `Effect.try` has two overloads\u2014one that takes just a thunk, and one that takes an object with `try` and a `catch` handler. The **`catch` handler signature** is: - `catch: (error: unknown) => E` (i.e., it receives the thrown value as `unknown` and must map it to your typed error `E`). [1] Type definition (from the docs):",
1721 "severity": "High",
1722 "has_false_positive_signal": false,
1723 "phase0_status": "filtered_outdated",
1724 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1725 },
1726 {
1727 "index": 51,
1728 "thread_id": "PRRT_kwDORLtfbc5v9gc3",
1729 "path": "apps/server/src/wsServer.ts",
1730 "line": null,
1731 "is_outdated": true,
1732 "is_resolved": true,
1733 "author": "coderabbitai",
1734 "title": "is called twice; the sequence can change between calls. Grab it once and reuse it to keep the push consistent. <details> <summary>\ud83d\udd27 Suggested fix</summary>",
1735 "severity": "Medium",
1736 "has_false_positive_signal": false,
1737 "phase0_status": "filtered_outdated",
1738 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1739 },
1740 {
1741 "index": 52,
1742 "thread_id": "PRRT_kwDORLtfbc5v-WO9",
1743 "path": "apps/web/src/routes/__root.tsx",
1744 "line": null,
1745 "is_outdated": true,
1746 "is_resolved": true,
1747 "author": "macroscopeapp",
1748 "title": "The `state.threads` check inside the async `.then()` uses a stale closure from when the effect ran, not the current state. Consider moving the thread check inside the `.then()` by re-reading from the store, or fetching threads from the API directly. No longer relevant as of b2f29834553747157b9eba6a5fa9de1b84693dce",
1749 "severity": "Medium",
1750 "has_false_positive_signal": false,
1751 "phase0_status": "filtered_outdated",
1752 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1753 },
1754 {
1755 "index": 53,
1756 "thread_id": "PRRT_kwDORLtfbc5v-WO-",
1757 "path": "apps/server/src/orchestration/Layer.ts",
1758 "line": null,
1759 "is_outdated": true,
1760 "is_resolved": true,
1761 "author": "macroscopeapp",
1762 "title": "`bootstrapReadModel` should reset `this.readModel` before replaying events. If it fails mid-replay and `start()` is retried, events get applied twice to the partially-updated model. Code refactored as of 9ef01eb97e9a4e7e174e383350febd08951a0dc3",
1763 "severity": "High",
1764 "has_false_positive_signal": false,
1765 "phase0_status": "filtered_outdated",
1766 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1767 },
1768 {
1769 "index": 54,
1770 "thread_id": "PRRT_kwDORLtfbc5v-WPA",
1771 "path": "apps/server/src/persistence/Layers/OrchestrationEvents.ts",
1772 "line": null,
1773 "is_outdated": true,
1774 "is_resolved": true,
1775 "author": "macroscopeapp",
1776 "title": "Avoid sync throws outside the Effect pipeline: both `Schema.decodeUnknownSync` and `JSON.stringify` can crash execution. Suggest using effectful decoding and wrapping serialization in `Effect.try`/`Effect.sync`, handling failures per-row so one bad record doesn\u2019t take down the whole operation. \u2705 Addressed in 2c8e35ceb8aebd0a0db03970193dbbdd66247df4",
1777 "severity": "High",
1778 "has_false_positive_signal": false,
1779 "phase0_status": "filtered_outdated",
1780 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1781 },
1782 {
1783 "index": 55,
1784 "thread_id": "PRRT_kwDORLtfbc5v-WPB",
1785 "path": "apps/server/src/wsServer.ts",
1786 "line": null,
1787 "is_outdated": true,
1788 "is_resolved": true,
1789 "author": "macroscopeapp",
1790 "title": "After `providerManager.startSession` returns, the `sessionId` is never dispatched to `OrchestrationEngine`, so `thread.session?.sessionId` remains unset. The `providerManager.on(\"event\")` listener then fails to find the thread and silently drops all streaming events. Consider dispatching a `thread.session` command after `startSession` to link the session with the thread. \u2705 Resolved in af53ae8b4845852a320f108dda30b32afc3c4029",
1791 "severity": "High",
1792 "has_false_positive_signal": false,
1793 "phase0_status": "filtered_outdated",
1794 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1795 },
1796 {
1797 "index": 56,
1798 "thread_id": "PRRT_kwDORLtfbc5v-WPC",
1799 "path": "apps/server/src/orchestration/engine.ts",
1800 "line": null,
1801 "is_outdated": true,
1802 "is_resolved": true,
1803 "author": "macroscopeapp",
1804 "title": "Race condition: `stop()` called during `start()`'s `bootstrapReadModel()` sees `workerFiber` as `null` and returns immediately, then `start()` resumes and spawns a worker anyway. Consider adding a stopping flag or mutex to coordinate the lifecycle. \u2705 Addressed in 9ef01eb97e9a4e7e174e383350febd08951a0dc3",
1805 "severity": "High",
1806 "has_false_positive_signal": false,
1807 "phase0_status": "filtered_outdated",
1808 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1809 },
1810 {
1811 "index": 57,
1812 "thread_id": "PRRT_kwDORLtfbc5v-WPD",
1813 "path": "apps/server/src/wsServer.ts",
1814 "line": 83,
1815 "is_outdated": false,
1816 "is_resolved": false,
1817 "author": "macroscopeapp",
1818 "title": "Consider using `socket.end(response)` instead of `socket.write(response)` + `socket.destroy()` to ensure the HTTP error response is fully flushed before closing the connection.",
1819 "severity": "Low",
1820 "has_false_positive_signal": false,
1821 "canonical_id": "C053",
1822 "phase0_status": "actionable_canonical",
1823 "phase0_reason": "Canonical actionable item."
1824 },
1825 {
1826 "index": 58,
1827 "thread_id": "PRRT_kwDORLtfbc5v-XCq",
1828 "path": "apps/server/src/wsServer.ts",
1829 "line": null,
1830 "is_outdated": true,
1831 "is_resolved": false,
1832 "author": "cursor",
1833 "title": "Provider events lost before session association completes",
1834 "severity": "High",
1835 "has_false_positive_signal": false,
1836 "phase0_status": "filtered_outdated",
1837 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1838 },
1839 {
1840 "index": 59,
1841 "thread_id": "PRRT_kwDORLtfbc5v-XCs",
1842 "path": "apps/server/src/wsServer.ts",
1843 "line": null,
1844 "is_outdated": true,
1845 "is_resolved": false,
1846 "author": "cursor",
1847 "title": "Streaming deltas persisted as individual events cause unbounded growth",
1848 "severity": "High",
1849 "has_false_positive_signal": false,
1850 "phase0_status": "filtered_outdated",
1851 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1852 },
1853 {
1854 "index": 60,
1855 "thread_id": "PRRT_kwDORLtfbc5v-XCu",
1856 "path": "apps/web/src/components/BranchToolbar.tsx",
1857 "line": 102,
1858 "is_outdated": false,
1859 "is_resolved": false,
1860 "author": "cursor",
1861 "title": "Branch sync dispatches both server and stale local update",
1862 "severity": "Medium",
1863 "has_false_positive_signal": false,
1864 "canonical_id": "C028",
1865 "phase0_status": "actionable_canonical",
1866 "phase0_reason": "Canonical actionable item."
1867 },
1868 {
1869 "index": 61,
1870 "thread_id": "PRRT_kwDORLtfbc5v-XVl",
1871 "path": "apps/server/src/orchestration/engine.test.ts",
1872 "line": null,
1873 "is_outdated": true,
1874 "is_resolved": false,
1875 "author": "coderabbitai",
1876 "title": "usage in `try/finally` or use a helper that guarantees cleanup. Based on learnings: \"For integration tests: Prefer deterministic inputs and explicit state checks; avoid relying on logs or timing assumptions. Keep fixtures cohesive and reuse identities across sequential tests. Synchronize on durable workflow status or snapshot state. Assert on final state and side effects. Use stable helper scripts like `SHELL_SETUP_SCRIPT` for runtime setup. Clean up external resources in `finally` blocks.\" <details> <summary>\ud83e\udd16 Prompt for AI Agents</summary>",
1877 "severity": "Medium",
1878 "has_false_positive_signal": false,
1879 "phase0_status": "filtered_outdated",
1880 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1881 },
1882 {
1883 "index": 62,
1884 "thread_id": "PRRT_kwDORLtfbc5v-XVo",
1885 "path": "apps/server/src/orchestration/engine.ts",
1886 "line": null,
1887 "is_outdated": true,
1888 "is_resolved": false,
1889 "author": "coderabbitai",
1890 "title": "runs while `workerFiber` is null, the command is queued but never processed, and the returned promise hangs indefinitely. Reject with a clear error (or enforce start) when the engine isn\u2019t running. As per coding guidelines: \"Maintain predictable behavior under load and during failures (session restarts, reconnects, partial streams)\". <details> <summary>\ud83d\udee1\ufe0f Suggested guard</summary>",
1891 "severity": "High",
1892 "has_false_positive_signal": false,
1893 "phase0_status": "filtered_outdated",
1894 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1895 },
1896 {
1897 "index": 63,
1898 "thread_id": "PRRT_kwDORLtfbc5v-XVt",
1899 "path": "apps/server/src/orchestration/reducer.test.ts",
1900 "line": null,
1901 "is_outdated": true,
1902 "is_resolved": false,
1903 "author": "coderabbitai",
1904 "title": "\ud83d\udca1 Result: For testing **Effect** code with **Vitest** in TypeScript, the standard helper library is **`@effect/vitest`**. It lets you write tests that *return an `Effect`* (instead of manually running it) and gives you a test environment (e.g. `TestClock`) by default. [1][2] ## Install",
1905 "severity": "High",
1906 "has_false_positive_signal": false,
1907 "phase0_status": "filtered_outdated",
1908 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1909 },
1910 {
1911 "index": 64,
1912 "thread_id": "PRRT_kwDORLtfbc5v-XVx",
1913 "path": "apps/server/src/persistence/Layers/OrchestrationEvents.ts",
1914 "line": null,
1915 "is_outdated": true,
1916 "is_resolved": false,
1917 "author": "coderabbitai",
1918 "title": "and `decodeUnknownSync` both throw uncaught exceptions; when wrapped with `Effect.orDie`, any parse/validation failure crashes the process. For event sourcing replay, this is critical\u2014you must catch and properly handle corrupted rows instead of terminating. Use `Schema.decodeUnknown` (async variant, per project guidelines) wrapped in `Effect.try`, and avoid type assertions in Effect code. <details> <summary>Suggested approach</summary>",
1919 "severity": "High",
1920 "has_false_positive_signal": false,
1921 "phase0_status": "filtered_outdated",
1922 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1923 },
1924 {
1925 "index": 65,
1926 "thread_id": "PRRT_kwDORLtfbc5v-XVz",
1927 "path": "apps/server/src/persistence/Layers/Projects.test.ts",
1928 "line": null,
1929 "is_outdated": true,
1930 "is_resolved": false,
1931 "author": "coderabbitai",
1932 "title": "in a finally block to prevent resource leaks on assertion failure.** If an assertion fails before `dispose()` is called, the SQLite connection and Effect runtime will leak. Use `try/finally` to ensure cleanup always runs, consistent with other tests in the codebase (e.g., `terminalManager.test.ts`, `filesystemCheckpointStore.test.ts`). <details> <summary>\ud83e\uddf9 Example cleanup pattern</summary>",
1933 "severity": "Medium",
1934 "has_false_positive_signal": false,
1935 "phase0_status": "filtered_outdated",
1936 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1937 },
1938 {
1939 "index": 66,
1940 "thread_id": "PRRT_kwDORLtfbc5v-XV6",
1941 "path": "apps/server/src/persistence/Layers/Projects.ts",
1942 "line": null,
1943 "is_outdated": true,
1944 "is_resolved": false,
1945 "author": "coderabbitai",
1946 "title": "exists, but the race condition persists: between the `findProjectByCwd` check and `insertProject` call, a concurrent request can insert the same `cwd`, causing the insert to fail. The `.pipe(Effect.orDie)` converts this constraint violation into an unhandled error instead of gracefully returning the existing project. Under concurrent load, this violates the coding guideline to \"maintain predictable behavior under load and during failures.\" Wrap the insert in error handling to catch the constraint violation and re-fetch the existing row, or use a database upsert/on-conflict pattern to ensure atomic, race-free behavior. <details> <summary>\ud83e\udd16 Prompt for AI Agents</summary>",
1947 "severity": "High",
1948 "has_false_positive_signal": false,
1949 "phase0_status": "filtered_outdated",
1950 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1951 },
1952 {
1953 "index": 67,
1954 "thread_id": "PRRT_kwDORLtfbc5v-XV8",
1955 "path": "apps/server/src/persistence/Services/Projects.ts",
1956 "line": null,
1957 "is_outdated": true,
1958 "is_resolved": false,
1959 "author": "coderabbitai",
1960 "title": "and `pruneMissing()` for consistent error handling.** Both methods perform database operations (`listProjectRows()` and `deleteProject()`) that can fail, but lack error type parameters. Currently they use `Effect.orDie` to convert failures to defects (crashes) instead of returning typed errors. This is inconsistent with `add/remove/updateScripts` which have `Error` channels, and violates the guideline to maintain predictable behavior under failures. Update signatures to:",
1961 "severity": "High",
1962 "has_false_positive_signal": false,
1963 "phase0_status": "filtered_outdated",
1964 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1965 },
1966 {
1967 "index": 68,
1968 "thread_id": "PRRT_kwDORLtfbc5v-X3U",
1969 "path": "apps/server/src/orchestration/engine.ts",
1970 "line": null,
1971 "is_outdated": true,
1972 "is_resolved": true,
1973 "author": "macroscopeapp",
1974 "title": "Consider resetting `this.readModel` in `stop()` to prevent state corruption if `start()` is called again\u2014`bootstrapReadModel()` would otherwise re-apply all events on top of existing state. \u2705 Addressed in 9ef01eb97e9a4e7e174e383350febd08951a0dc3",
1975 "severity": "High",
1976 "has_false_positive_signal": false,
1977 "phase0_status": "filtered_outdated",
1978 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1979 },
1980 {
1981 "index": 69,
1982 "thread_id": "PRRT_kwDORLtfbc5v-X3W",
1983 "path": "apps/server/src/wsServer.ts",
1984 "line": null,
1985 "is_outdated": true,
1986 "is_resolved": true,
1987 "author": "macroscopeapp",
1988 "title": "If `orchestrationEngine.dispatch` fails after `providerManager.revertToCheckpoint` succeeds, the filesystem is reverted but the read model isn't updated, causing inconsistency. Consider either dispatching the state update first (since revert can be retried), or wrapping both in a transaction-like pattern that restores the checkpoint on dispatch failure. \u2705 Resolved in af53ae8b4845852a320f108dda30b32afc3c4029",
1989 "severity": "High",
1990 "has_false_positive_signal": false,
1991 "phase0_status": "filtered_outdated",
1992 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
1993 },
1994 {
1995 "index": 70,
1996 "thread_id": "PRRT_kwDORLtfbc5v-X3Y",
1997 "path": "packages/contracts/src/orchestration.ts",
1998 "line": 141,
1999 "is_outdated": false,
2000 "is_resolved": true,
2001 "author": "macroscopeapp",
2002 "title": "`streaming` is required here but optional in `SendMessageCommandSchema`. Messages created without this field will fail validation. Consider using `Schema.optional(Schema.Boolean)` for consistency. No longer relevant as of b2f29834553747157b9eba6a5fa9de1b84693dce",
2003 "severity": "Medium",
2004 "has_false_positive_signal": false,
2005 "phase0_status": "filtered_resolved",
2006 "phase0_reason": "Thread is already resolved on GitHub."
2007 },
2008 {
2009 "index": 71,
2010 "thread_id": "PRRT_kwDORLtfbc5v-Y_d",
2011 "path": "apps/server/src/orchestration/engine.ts",
2012 "line": null,
2013 "is_outdated": true,
2014 "is_resolved": false,
2015 "author": "coderabbitai",
2016 "title": "is called.** Interrupting the worker fiber leaves any queued command envelopes unprocessed, and their `Deferred` results will never resolve. Callers awaiting `dispatch()` will hang indefinitely after stop. Consider draining or failing pending commands before interrupting. <details> <summary>\ud83d\udee1\ufe0f Suggested approach: fail pending commands on stop</summary>",
2017 "severity": "Medium",
2018 "has_false_positive_signal": false,
2019 "phase0_status": "filtered_outdated",
2020 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2021 },
2022 {
2023 "index": 72,
2024 "thread_id": "PRRT_kwDORLtfbc5v-cf4",
2025 "path": "apps/server/src/wsServer.ts",
2026 "line": 75,
2027 "is_outdated": false,
2028 "is_resolved": false,
2029 "author": "macroscopeapp",
2030 "title": "Consider attaching a no-op error listener before `socket.write` (e.g., `socket.on('error', () => {})`) to prevent an unhandled `EPIPE`/`ECONNRESET` from crashing the process if the client disconnects mid-handshake.",
2031 "severity": "High",
2032 "has_false_positive_signal": false,
2033 "canonical_id": "C003",
2034 "phase0_status": "actionable_canonical",
2035 "phase0_reason": "Canonical actionable item."
2036 },
2037 {
2038 "index": 73,
2039 "thread_id": "PRRT_kwDORLtfbc5v-gEm",
2040 "path": "apps/server/src/orchestration/reducer.ts",
2041 "line": null,
2042 "is_outdated": true,
2043 "is_resolved": false,
2044 "author": "cursor",
2045 "title": "Reducer overwrites Codex thread ID with orchestration ID",
2046 "severity": "High",
2047 "has_false_positive_signal": false,
2048 "phase0_status": "filtered_outdated",
2049 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2050 },
2051 {
2052 "index": 74,
2053 "thread_id": "PRRT_kwDORLtfbc5v-gEo",
2054 "path": "apps/server/src/orchestration/reducer.ts",
2055 "line": null,
2056 "is_outdated": true,
2057 "is_resolved": false,
2058 "author": "cursor",
2059 "title": "Reducer never sets latestTurnStartedAt breaking completion summary",
2060 "severity": "Medium",
2061 "has_false_positive_signal": false,
2062 "phase0_status": "filtered_outdated",
2063 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2064 },
2065 {
2066 "index": 75,
2067 "thread_id": "PRRT_kwDORLtfbc5v-gEp",
2068 "path": "apps/web/src/routes/__root.tsx",
2069 "line": null,
2070 "is_outdated": true,
2071 "is_resolved": false,
2072 "author": "cursor",
2073 "title": "Stale closure creates duplicate threads on page refresh",
2074 "severity": "Medium",
2075 "has_false_positive_signal": false,
2076 "phase0_status": "filtered_outdated",
2077 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2078 },
2079 {
2080 "index": 76,
2081 "thread_id": "PRRT_kwDORLtfbc5v-iPH",
2082 "path": "apps/server/src/persistence/Layers/Projects.ts",
2083 "line": null,
2084 "is_outdated": true,
2085 "is_resolved": true,
2086 "author": "macroscopeapp",
2087 "title": "Consider distinguishing `ENOENT`/`ENOTDIR` (directory truly absent) from transient errors like `EACCES` or `EIO` in `isDirectory`. Currently, any error returns `false`, so `pruneMissing` may delete projects that are present but temporarily inaccessible. No longer relevant as of b2f29834553747157b9eba6a5fa9de1b84693dce",
2088 "severity": "High",
2089 "has_false_positive_signal": false,
2090 "phase0_status": "filtered_outdated",
2091 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2092 },
2093 {
2094 "index": 77,
2095 "thread_id": "PRRT_kwDORLtfbc5v-iPJ",
2096 "path": "apps/server/src/orchestration/Layer.ts",
2097 "line": null,
2098 "is_outdated": true,
2099 "is_resolved": true,
2100 "author": "macroscopeapp",
2101 "title": "Consider validating events through `reduceEvent` before persisting to avoid \"poison pill\" events. Currently, if `reduceEvent` fails after `eventStore.append`, the error is swallowed but the event remains in the database. On restart, `bootstrapReadModel` will fail on that same event, bricking the service. No longer relevant as of b2f29834553747157b9eba6a5fa9de1b84693dce",
2102 "severity": "High",
2103 "has_false_positive_signal": false,
2104 "phase0_status": "filtered_outdated",
2105 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2106 },
2107 {
2108 "index": 78,
2109 "thread_id": "PRRT_kwDORLtfbc5v-kr_",
2110 "path": "apps/server/src/orchestration/Layer.ts",
2111 "line": null,
2112 "is_outdated": true,
2113 "is_resolved": false,
2114 "author": "cursor",
2115 "title": "Listener failure falsely fails dispatch after event persistence",
2116 "severity": "High",
2117 "has_false_positive_signal": false,
2118 "phase0_status": "filtered_outdated",
2119 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2120 },
2121 {
2122 "index": 79,
2123 "thread_id": "PRRT_kwDORLtfbc5v-ksB",
2124 "path": "apps/server/src/orchestration/Layer.ts",
2125 "line": null,
2126 "is_outdated": true,
2127 "is_resolved": false,
2128 "author": "cursor",
2129 "title": "Unused PubSub instances created and published to",
2130 "severity": "Low",
2131 "has_false_positive_signal": false,
2132 "phase0_status": "filtered_outdated",
2133 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2134 },
2135 {
2136 "index": 80,
2137 "thread_id": "PRRT_kwDORLtfbc5wBEEV",
2138 "path": "apps/server/src/wsServer.ts",
2139 "line": null,
2140 "is_outdated": true,
2141 "is_resolved": false,
2142 "author": "macroscopeapp",
2143 "title": "Consider normalizing `params.cwd` before comparing with `entry.cwd`, since `ProjectRepository.add` normalizes paths (e.g., lowercasing on Windows). Otherwise, valid requests with different casing or formatting won't match and the git read model won't be updated. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
2144 "severity": "High",
2145 "has_false_positive_signal": false,
2146 "phase0_status": "filtered_outdated",
2147 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2148 },
2149 {
2150 "index": 81,
2151 "thread_id": "PRRT_kwDORLtfbc5wBEEX",
2152 "path": "apps/server/src/persistence/Layers/OrchestrationEvents.ts",
2153 "line": null,
2154 "is_outdated": true,
2155 "is_resolved": true,
2156 "author": "macroscopeapp",
2157 "title": "`readAll` uses `Number.MAX_SAFE_INTEGER` as the limit, which could exhaust memory as events grow. If this is intentional (e.g., for testing or small datasets), consider adding a comment documenting this constraint. Otherwise, consider implementing pagination or streaming. No longer relevant as of b2f29834553747157b9eba6a5fa9de1b84693dce",
2158 "severity": "Medium",
2159 "has_false_positive_signal": false,
2160 "phase0_status": "filtered_outdated",
2161 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2162 },
2163 {
2164 "index": 82,
2165 "thread_id": "PRRT_kwDORLtfbc5wBEEY",
2166 "path": "apps/server/src/orchestration/reducer.ts",
2167 "line": null,
2168 "is_outdated": true,
2169 "is_resolved": true,
2170 "author": "macroscopeapp",
2171 "title": "When a duplicate `thread.created` event is processed, the existing thread is fully replaced with a fresh object containing empty `messages`, `session`, and `turnDiffSummaries`. Consider returning the existing thread unchanged (or merging only metadata) to avoid data loss on event replay. No longer relevant as of b2f29834553747157b9eba6a5fa9de1b84693dce",
2172 "severity": "High",
2173 "has_false_positive_signal": false,
2174 "phase0_status": "filtered_outdated",
2175 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2176 },
2177 {
2178 "index": 83,
2179 "thread_id": "PRRT_kwDORLtfbc5wBEEZ",
2180 "path": "apps/server/src/orchestration/Layer.ts",
2181 "line": null,
2182 "is_outdated": true,
2183 "is_resolved": true,
2184 "author": "macroscopeapp",
2185 "title": "Listener failures abort the notification chain and fail the command's `Deferred`, even though the event was successfully persisted. Consider using `{ concurrency: \"unbounded\" }` with `Effect.forEach` and catching errors per-listener, or using `Effect.catchAll` after each notification call to log and continue. No longer relevant as of b2f29834553747157b9eba6a5fa9de1b84693dce",
2186 "severity": "High",
2187 "has_false_positive_signal": false,
2188 "phase0_status": "filtered_outdated",
2189 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2190 },
2191 {
2192 "index": 84,
2193 "thread_id": "PRRT_kwDORLtfbc5wBFWr",
2194 "path": "apps/server/src/orchestration/Layer.ts",
2195 "line": null,
2196 "is_outdated": true,
2197 "is_resolved": true,
2198 "author": "macroscopeapp",
2199 "title": "Consider guarding against duplicate `thread.create` commands\u2014currently, sending a create command with an existing thread ID will replace that thread's state (messages, session, etc.) with a fresh empty object. No longer relevant as of b2f29834553747157b9eba6a5fa9de1b84693dce",
2200 "severity": "Medium",
2201 "has_false_positive_signal": false,
2202 "phase0_status": "filtered_outdated",
2203 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2204 },
2205 {
2206 "index": 85,
2207 "thread_id": "PRRT_kwDORLtfbc5wBFWs",
2208 "path": "apps/server/src/orchestration/Errors.ts",
2209 "line": 126,
2210 "is_outdated": false,
2211 "is_resolved": false,
2212 "author": "macroscopeapp",
2213 "title": "Template literal interpolation throws `TypeError` if `cause` is a `Symbol`. Consider using `String(cause)` for safe coercion.",
2214 "severity": "Low",
2215 "has_false_positive_signal": false,
2216 "canonical_id": "C041",
2217 "phase0_status": "actionable_canonical",
2218 "phase0_reason": "Canonical actionable item."
2219 },
2220 {
2221 "index": 86,
2222 "thread_id": "PRRT_kwDORLtfbc5wBFv4",
2223 "path": "apps/server/src/orchestration/Errors.ts",
2224 "line": 117,
2225 "is_outdated": false,
2226 "is_resolved": false,
2227 "author": "macroscopeapp",
2228 "title": "Template literal `${cause}` throws if `cause` is a `Symbol`. Consider using `String(cause)` instead.",
2229 "severity": "Medium",
2230 "has_false_positive_signal": false,
2231 "canonical_id": "C011",
2232 "phase0_status": "actionable_canonical",
2233 "phase0_reason": "Canonical actionable item."
2234 },
2235 {
2236 "index": 87,
2237 "thread_id": "PRRT_kwDORLtfbc5wBGN5",
2238 "path": "apps/server/src/orchestration/Layer.ts",
2239 "line": null,
2240 "is_outdated": true,
2241 "is_resolved": false,
2242 "author": "cursor",
2243 "title": "Listener failure fails deferred after event already persisted",
2244 "severity": "Medium",
2245 "has_false_positive_signal": false,
2246 "phase0_status": "filtered_outdated",
2247 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2248 },
2249 {
2250 "index": 88,
2251 "thread_id": "PRRT_kwDORLtfbc5wBGN8",
2252 "path": "apps/server/src/orchestration/Layer.ts",
2253 "line": null,
2254 "is_outdated": true,
2255 "is_resolved": false,
2256 "author": "cursor",
2257 "title": "Unused PubSub instances created but never subscribed to",
2258 "severity": "Low",
2259 "has_false_positive_signal": false,
2260 "phase0_status": "filtered_outdated",
2261 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2262 },
2263 {
2264 "index": 89,
2265 "thread_id": "PRRT_kwDORLtfbc5wBGN-",
2266 "path": "apps/server/src/wsServer.ts",
2267 "line": null,
2268 "is_outdated": true,
2269 "is_resolved": false,
2270 "author": "cursor",
2271 "title": "Streaming text deltas create excessive persistent events",
2272 "severity": "Medium",
2273 "has_false_positive_signal": false,
2274 "phase0_status": "filtered_outdated",
2275 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2276 },
2277 {
2278 "index": 90,
2279 "thread_id": "PRRT_kwDORLtfbc5whszQ",
2280 "path": "apps/server/src/orchestration/Layers/ProviderCommandReactor.ts",
2281 "line": 84,
2282 "is_outdated": false,
2283 "is_resolved": false,
2284 "author": "cursor",
2285 "title": "Unbounded memory growth in turn start deduplication set",
2286 "severity": "Medium",
2287 "has_false_positive_signal": false,
2288 "canonical_id": "C018",
2289 "phase0_status": "actionable_canonical",
2290 "phase0_reason": "Canonical actionable item."
2291 },
2292 {
2293 "index": 91,
2294 "thread_id": "PRRT_kwDORLtfbc5whszT",
2295 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
2296 "line": null,
2297 "is_outdated": true,
2298 "is_resolved": false,
2299 "author": "cursor",
2300 "title": "Fallback session matching compares incompatible identity types",
2301 "severity": "Medium",
2302 "has_false_positive_signal": false,
2303 "phase0_status": "filtered_outdated",
2304 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2305 },
2306 {
2307 "index": 92,
2308 "thread_id": "PRRT_kwDORLtfbc5whszW",
2309 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
2310 "line": 542,
2311 "is_outdated": false,
2312 "is_resolved": false,
2313 "author": "cursor",
2314 "title": "Forked revert dispatch risks read model inconsistency",
2315 "severity": "Medium",
2316 "has_false_positive_signal": false,
2317 "canonical_id": "C012",
2318 "phase0_status": "actionable_canonical",
2319 "phase0_reason": "Canonical actionable item."
2320 },
2321 {
2322 "index": 93,
2323 "thread_id": "PRRT_kwDORLtfbc5whtrM",
2324 "path": "apps/server/src/orchestration/Layers/OrchestrationEngine.ts",
2325 "line": 99,
2326 "is_outdated": false,
2327 "is_resolved": false,
2328 "author": "macroscopeapp",
2329 "title": "If `projectionPipeline.projectEvent` fails after `eventStore.append` succeeds, the event is persisted but `readModel` isn't updated, causing desync. Consider updating the in-memory `readModel` immediately after append (before the external projection), so local state stays consistent regardless of downstream failures.",
2330 "severity": "Medium",
2331 "has_false_positive_signal": false,
2332 "canonical_id": "C013",
2333 "phase0_status": "actionable_canonical",
2334 "phase0_reason": "Canonical actionable item."
2335 },
2336 {
2337 "index": 94,
2338 "thread_id": "PRRT_kwDORLtfbc5whtrN",
2339 "path": "apps/web/src/wsTransport.ts",
2340 "line": 59,
2341 "is_outdated": false,
2342 "is_resolved": false,
2343 "author": "macroscopeapp",
2344 "title": "Suggestion: don\u2019t spread `params` into `body`; it can override `_tag` and mishandle non-object values. Keep `_tag` separate and nest `params` under a single key (e.g., `data`), or validate `params` is a plain object.",
2345 "severity": "Low",
2346 "has_false_positive_signal": false,
2347 "canonical_id": "C059",
2348 "phase0_status": "actionable_canonical",
2349 "phase0_reason": "Canonical actionable item."
2350 },
2351 {
2352 "index": 95,
2353 "thread_id": "PRRT_kwDORLtfbc5whtrP",
2354 "path": "apps/server/src/persistence/NodeSqliteClient.ts",
2355 "line": 108,
2356 "is_outdated": false,
2357 "is_resolved": true,
2358 "author": "macroscopeapp",
2359 "title": "`runValues` doesn't call `setReadBigInts` before execution, so cached statements may retain stale bigint settings from prior `runStatement` calls. Consider adding `statement.setReadBigInts(...)` similar to line 98. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
2360 "severity": "Low",
2361 "has_false_positive_signal": false,
2362 "phase0_status": "filtered_resolved",
2363 "phase0_reason": "Thread is already resolved on GitHub."
2364 },
2365 {
2366 "index": 96,
2367 "thread_id": "PRRT_kwDORLtfbc5whtrR",
2368 "path": "apps/server/src/wsServer.ts",
2369 "line": 104,
2370 "is_outdated": false,
2371 "is_resolved": false,
2372 "author": "macroscopeapp",
2373 "title": "When array chunks contain a multi-byte UTF-8 character split across boundaries, decoding each chunk separately produces replacement characters. Consider using `Buffer.concat()` on all chunks before calling `.toString(\"utf8\")`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
2374 "severity": "Low",
2375 "has_false_positive_signal": false,
2376 "canonical_id": "C054",
2377 "phase0_status": "actionable_canonical",
2378 "phase0_reason": "Canonical actionable item."
2379 },
2380 {
2381 "index": 97,
2382 "thread_id": "PRRT_kwDORLtfbc5whtrS",
2383 "path": "apps/server/src/main.ts",
2384 "line": null,
2385 "is_outdated": true,
2386 "is_resolved": true,
2387 "author": "macroscopeapp",
2388 "title": "On Windows, `~\\data` bypasses home expansion because only `~/` is checked. Consider also checking for `~\\` to avoid writing to `${CWD}/~` instead of the home directory. \u2705 Resolved in d102702d9556fac36379e13e7a89d21f230ebcb6",
2389 "severity": "Low",
2390 "has_false_positive_signal": false,
2391 "phase0_status": "filtered_outdated",
2392 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2393 },
2394 {
2395 "index": 98,
2396 "thread_id": "PRRT_kwDORLtfbc5whtrX",
2397 "path": "apps/server/src/persistence/NodeSqliteClient.ts",
2398 "line": null,
2399 "is_outdated": true,
2400 "is_resolved": true,
2401 "author": "macroscopeapp",
2402 "title": "Consider also checking `options.readonly` before enabling WAL mode\u2014changing journal mode on a read-only connection will throw. \u2705 Resolved in 3379feed8d37d82ba2415e39e8d50958ee680719",
2403 "severity": "Low",
2404 "has_false_positive_signal": false,
2405 "phase0_status": "filtered_outdated",
2406 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2407 },
2408 {
2409 "index": 99,
2410 "thread_id": "PRRT_kwDORLtfbc5whtrb",
2411 "path": "apps/server/src/main.ts",
2412 "line": null,
2413 "is_outdated": true,
2414 "is_resolved": true,
2415 "author": "macroscopeapp",
2416 "title": "The server shuts down immediately after starting because the scope closes when `makeServerProgram` returns. Consider adding `yield* Effect.never` after logging to keep the process alive. \u2705 Resolved in 68c8eaa6198d766a675eddfb5600482df885ea77",
2417 "severity": "High",
2418 "has_false_positive_signal": false,
2419 "phase0_status": "filtered_outdated",
2420 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2421 },
2422 {
2423 "index": 100,
2424 "thread_id": "PRRT_kwDORLtfbc5whxJC",
2425 "path": "packages/contracts/src/orchestration.ts",
2426 "line": 253,
2427 "is_outdated": false,
2428 "is_resolved": false,
2429 "author": "macroscopeapp",
2430 "title": "`defaultModel` should be `Schema.optional(Schema.NullOr(Schema.String))` to allow clearing the value. Currently there's no way to reset it to `null` since omitting means \"no change\" in patch semantics. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
2431 "severity": "Low",
2432 "has_false_positive_signal": false,
2433 "canonical_id": "C060",
2434 "phase0_status": "actionable_canonical",
2435 "phase0_reason": "Canonical actionable item."
2436 },
2437 {
2438 "index": 101,
2439 "thread_id": "PRRT_kwDORLtfbc5whxJO",
2440 "path": "apps/server/src/orchestration/Layers/ProjectionPipeline.ts",
2441 "line": 99,
2442 "is_outdated": false,
2443 "is_resolved": false,
2444 "author": "macroscopeapp",
2445 "title": "The gap-filling fallback logic can retain messages from turns that are about to be deleted, causing foreign key violations. Consider removing the fallback logic entirely, or filtering `fallbackUserMessages` and `fallbackAssistantMessages` to only include messages whose `turnId` is in `retainedTurnIds`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
2446 "severity": "Medium",
2447 "has_false_positive_signal": false,
2448 "canonical_id": "C015",
2449 "phase0_status": "actionable_canonical",
2450 "phase0_reason": "Canonical actionable item."
2451 },
2452 {
2453 "index": 102,
2454 "thread_id": "PRRT_kwDORLtfbc5whxXx",
2455 "path": "apps/server/src/persistence/Layers/Sqlite.ts",
2456 "line": 29,
2457 "is_outdated": false,
2458 "is_resolved": true,
2459 "author": "macroscopeapp",
2460 "title": "The `setup` layer always sets `journal_mode = WAL`, ignoring the `disableWAL` config. Consider passing the config to `setup` and conditionally executing the pragma. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
2461 "severity": "Low",
2462 "has_false_positive_signal": false,
2463 "phase0_status": "filtered_resolved",
2464 "phase0_reason": "Thread is already resolved on GitHub."
2465 },
2466 {
2467 "index": 103,
2468 "thread_id": "PRRT_kwDORLtfbc5whxX9",
2469 "path": "apps/server/src/persistence/NodeSqliteClient.ts",
2470 "line": 145,
2471 "is_outdated": false,
2472 "is_resolved": false,
2473 "author": "macroscopeapp",
2474 "title": "In `executeUnprepared`, preparing outside an effect and not closing the statement can cause uncaught errors and leaks. Suggest wrapping with `Effect.acquireUseRelease`: prepare in acquire (error-safe) and `finalize` in release.",
2475 "severity": "Medium",
2476 "has_false_positive_signal": false,
2477 "canonical_id": "C024",
2478 "phase0_status": "actionable_canonical",
2479 "phase0_reason": "Canonical actionable item."
2480 },
2481 {
2482 "index": 104,
2483 "thread_id": "PRRT_kwDORLtfbc5wiAo6",
2484 "path": "apps/server/src/main.ts",
2485 "line": null,
2486 "is_outdated": true,
2487 "is_resolved": true,
2488 "author": "macroscopeapp",
2489 "title": "There's a TOCTOU race between closing the test server and the actual `server.start()` call in `makeServerProgram`. Consider either passing the bound server directly to the caller, or documenting this as an accepted limitation with a retry strategy at the call site. No longer relevant as of 69d5caa0f94522b5c90285aa752d9410f756a86e",
2490 "severity": "Low",
2491 "has_false_positive_signal": false,
2492 "phase0_status": "filtered_outdated",
2493 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2494 },
2495 {
2496 "index": 105,
2497 "thread_id": "PRRT_kwDORLtfbc5wiLNN",
2498 "path": "apps/server/integration/OrchestrationEngineHarness.integration.ts",
2499 "line": null,
2500 "is_outdated": true,
2501 "is_resolved": false,
2502 "author": "cursor",
2503 "title": "Duplicate checkpoint ref generation logic across four files",
2504 "severity": "Medium",
2505 "has_false_positive_signal": false,
2506 "phase0_status": "filtered_outdated",
2507 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2508 },
2509 {
2510 "index": 106,
2511 "thread_id": "PRRT_kwDORLtfbc5wiLNS",
2512 "path": "apps/server/package.json",
2513 "line": 14,
2514 "is_outdated": false,
2515 "is_resolved": false,
2516 "author": "cursor",
2517 "title": "Build script uses `node` to run `.ts` file directly",
2518 "severity": "Medium",
2519 "has_false_positive_signal": false,
2520 "canonical_id": "C007",
2521 "phase0_status": "actionable_canonical",
2522 "phase0_reason": "Canonical actionable item."
2523 },
2524 {
2525 "index": 107,
2526 "thread_id": "PRRT_kwDORLtfbc5wiLhQ",
2527 "path": "apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts",
2528 "line": 71,
2529 "is_outdated": false,
2530 "is_resolved": false,
2531 "author": "macroscopeapp",
2532 "title": "`REQUIRED_SNAPSHOT_PROJECTORS` includes `pending-approvals` and `thread-turns`, but `getSnapshot` doesn't query their data. If these projectors lag behind, the returned `snapshotSequence` will be lower than what the included data actually reflects, causing clients to replay already-applied events. Consider filtering `REQUIRED_SNAPSHOT_PROJECTORS` to only include projectors whose data is actually fetched in the snapshot. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
2533 "severity": "Medium",
2534 "has_false_positive_signal": false,
2535 "canonical_id": "C017",
2536 "phase0_status": "actionable_canonical",
2537 "phase0_reason": "Canonical actionable item."
2538 },
2539 {
2540 "index": 108,
2541 "thread_id": "PRRT_kwDORLtfbc5wiLhY",
2542 "path": "apps/server/src/provider/Layers/ProviderSessionDirectory.ts",
2543 "line": 94,
2544 "is_outdated": false,
2545 "is_resolved": false,
2546 "author": "macroscopeapp",
2547 "title": "The read-modify-write pattern (`getBySessionId` \u2192 merge \u2192 `upsert`) is susceptible to lost updates under concurrent writes. Consider wrapping in a transaction or adding optimistic concurrency control (e.g., version field) if concurrent session updates are expected. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
2548 "severity": "Low",
2549 "has_false_positive_signal": false,
2550 "canonical_id": "C050",
2551 "phase0_status": "actionable_canonical",
2552 "phase0_reason": "Canonical actionable item."
2553 },
2554 {
2555 "index": 109,
2556 "thread_id": "PRRT_kwDORLtfbc5wiQi9",
2557 "path": "apps/server/src/checkpointing/Layers/CheckpointStore.ts",
2558 "line": 87,
2559 "is_outdated": false,
2560 "is_resolved": true,
2561 "author": "cursor",
2562 "title": "Incorrect `Effect.catch` API usage in `isGitRepository`",
2563 "severity": "High",
2564 "has_false_positive_signal": true,
2565 "phase0_status": "filtered_resolved",
2566 "phase0_reason": "Thread is already resolved on GitHub."
2567 },
2568 {
2569 "index": 110,
2570 "thread_id": "PRRT_kwDORLtfbc5wiaR-",
2571 "path": "apps/server/src/persistence/Layers/OrchestrationCommandReceipts.ts",
2572 "line": 75,
2573 "is_outdated": false,
2574 "is_resolved": false,
2575 "author": "macroscopeapp",
2576 "title": "`SqlSchema.findOneOption` can produce both SQL errors and decode errors, but `mapError` wraps all as `PersistenceSqlError`. Consider distinguishing `ParseError` from SQL errors and mapping decode failures to `PersistenceDecodeError` instead. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
2577 "severity": "Low",
2578 "has_false_positive_signal": false,
2579 "canonical_id": "C047",
2580 "phase0_status": "actionable_canonical",
2581 "phase0_reason": "Canonical actionable item."
2582 },
2583 {
2584 "index": 111,
2585 "thread_id": "PRRT_kwDORLtfbc5wiaSC",
2586 "path": "apps/web/src/wsTransport.ts",
2587 "line": null,
2588 "is_outdated": true,
2589 "is_resolved": true,
2590 "author": "macroscopeapp",
2591 "title": "If `message.error` exists but lacks a `message` string, the promise resolves with `undefined` instead of rejecting. Consider rejecting with a fallback error message when `message.error` is truthy but doesn't have a valid `message` property. \u2705 Resolved in ef341394699edcf9ab21af4a7bbf0186a87ebce5",
2592 "severity": "Low",
2593 "has_false_positive_signal": false,
2594 "phase0_status": "filtered_outdated",
2595 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2596 },
2597 {
2598 "index": 112,
2599 "thread_id": "PRRT_kwDORLtfbc5widJw",
2600 "path": "apps/server/src/checkpointing/Layers/CheckpointStore.ts",
2601 "line": 94,
2602 "is_outdated": false,
2603 "is_resolved": false,
2604 "author": "cursor",
2605 "title": "Inconsistent input trimming across CheckpointStore methods",
2606 "severity": "Low",
2607 "has_false_positive_signal": false,
2608 "canonical_id": "C008",
2609 "phase0_status": "actionable_canonical",
2610 "phase0_reason": "Canonical actionable item."
2611 },
2612 {
2613 "index": 113,
2614 "thread_id": "PRRT_kwDORLtfbc5widJx",
2615 "path": "apps/server/package.json",
2616 "line": 14,
2617 "is_outdated": false,
2618 "is_resolved": false,
2619 "author": "cursor",
2620 "title": "Build script requires implicit Node.js TypeScript support",
2621 "severity": "Medium",
2622 "has_false_positive_signal": true,
2623 "phase0_status": "filtered_invalid",
2624 "phase0_reason": "Thread contains explicit false-positive determination in replies."
2625 },
2626 {
2627 "index": 114,
2628 "thread_id": "PRRT_kwDORLtfbc5wio-r",
2629 "path": "apps/server/src/persistence/NodeSqliteClient.ts",
2630 "line": 99,
2631 "is_outdated": false,
2632 "is_resolved": false,
2633 "author": "macroscopeapp",
2634 "title": "`statement.run()` returns a `RunResult` object, not an array. Casting it to `ReadonlyArray<any>` will cause runtime errors when consumers call array methods. Consider wrapping the result (e.g., `[result]`) or returning a properly typed object.",
2635 "severity": "Low",
2636 "has_false_positive_signal": false,
2637 "canonical_id": "C048",
2638 "phase0_status": "actionable_canonical",
2639 "phase0_reason": "Canonical actionable item."
2640 },
2641 {
2642 "index": 115,
2643 "thread_id": "PRRT_kwDORLtfbc5wiqFX",
2644 "path": "apps/server/src/checkpointing/Layers/CheckpointStore.ts",
2645 "line": 284,
2646 "is_outdated": false,
2647 "is_resolved": false,
2648 "author": "cursor",
2649 "title": "Duplicate `checkpointRefForThreadTurn` function in two production files",
2650 "severity": "Low",
2651 "has_false_positive_signal": false,
2652 "phase0_status": "filtered_invalid",
2653 "phase0_reason": "Invalid after post-rewrite audit: No longer duplicated; single production source via Refs.ts."
2654 },
2655 {
2656 "index": 116,
2657 "thread_id": "PRRT_kwDORLtfbc5wjahy",
2658 "path": "apps/server/src/main.ts",
2659 "line": null,
2660 "is_outdated": true,
2661 "is_resolved": true,
2662 "author": "macroscopeapp",
2663 "title": "`Effect.callback` lacks a cleanup function. If the fiber is interrupted while `server` or `fallback` is listening, the socket leaks and the port stays bound. Consider returning a cleanup function that closes the active server. No longer relevant as of 69d5caa0f94522b5c90285aa752d9410f756a86e",
2664 "severity": "Low",
2665 "has_false_positive_signal": false,
2666 "phase0_status": "filtered_outdated",
2667 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2668 },
2669 {
2670 "index": 117,
2671 "thread_id": "PRRT_kwDORLtfbc5wj4b_",
2672 "path": "apps/server/src/open.ts",
2673 "line": 21,
2674 "is_outdated": false,
2675 "is_resolved": true,
2676 "author": "macroscopeapp",
2677 "title": "Suggestion: avoid synchronous `throw` in `resolveEditorLaunch` when used from `Effect.gen`\u2014it becomes a `Defect` and bypasses `OpenError` handling. Return `Effect<EditorLaunch, OpenError>` or wrap with `Effect.try(...)` so failures are typed and caught. \u2705 Resolved in cbc92519e040e963b3257a3192be47bffad5b8ce",
2678 "severity": "Low",
2679 "has_false_positive_signal": false,
2680 "phase0_status": "filtered_resolved",
2681 "phase0_reason": "Thread is already resolved on GitHub."
2682 },
2683 {
2684 "index": 118,
2685 "thread_id": "PRRT_kwDORLtfbc5wj4cE",
2686 "path": "apps/server/src/wsServer.ts",
2687 "line": 545,
2688 "is_outdated": false,
2689 "is_resolved": false,
2690 "author": "macroscopeapp",
2691 "title": "Consider adding `.catch(() => {})` after `Effect.runPromise(handleMessage(ws, raw))` to prevent unhandled rejections from crashing the server if `encodeResponse` or setup logic fails.",
2692 "severity": "Medium",
2693 "has_false_positive_signal": false,
2694 "canonical_id": "C026",
2695 "phase0_status": "actionable_canonical",
2696 "phase0_reason": "Canonical actionable item."
2697 },
2698 {
2699 "index": 119,
2700 "thread_id": "PRRT_kwDORLtfbc5wj4cH",
2701 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
2702 "line": 333,
2703 "is_outdated": false,
2704 "is_resolved": false,
2705 "author": "macroscopeapp",
2706 "title": "A dispatch error in `processEvent` will terminate the `Effect.forever` loop, permanently halting event ingestion. Consider adding error recovery (e.g., `Effect.catchAll` with logging) around `processEvent` so failures don't kill the fiber.",
2707 "severity": "High",
2708 "has_false_positive_signal": false,
2709 "canonical_id": "C002",
2710 "phase0_status": "actionable_canonical",
2711 "phase0_reason": "Canonical actionable item."
2712 },
2713 {
2714 "index": 120,
2715 "thread_id": "PRRT_kwDORLtfbc5wj4cI",
2716 "path": "apps/server/src/open.ts",
2717 "line": null,
2718 "is_outdated": true,
2719 "is_resolved": true,
2720 "author": "macroscopeapp",
2721 "title": "`Effect.scoped` closes immediately after the generator completes, triggering the `ChildProcess` finalizer and terminating the editor. Consider removing `Effect.scoped` or using a different approach to keep the process alive. \u2705 Resolved in cbc92519e040e963b3257a3192be47bffad5b8ce",
2722 "severity": "Medium",
2723 "has_false_positive_signal": false,
2724 "phase0_status": "filtered_outdated",
2725 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2726 },
2727 {
2728 "index": 121,
2729 "thread_id": "PRRT_kwDORLtfbc5wj4cM",
2730 "path": "apps/server/src/os-jank.ts",
2731 "line": 10,
2732 "is_outdated": false,
2733 "is_resolved": false,
2734 "author": "macroscopeapp",
2735 "title": "Using `-il` flags causes the shell to source profile scripts that may print banners or other text, polluting the captured `PATH`. Consider using `-lc` (login only, non-interactive) to reduce unwanted output.",
2736 "severity": "Medium",
2737 "has_false_positive_signal": false,
2738 "canonical_id": "C023",
2739 "phase0_status": "actionable_canonical",
2740 "phase0_reason": "Canonical actionable item."
2741 },
2742 {
2743 "index": 122,
2744 "thread_id": "PRRT_kwDORLtfbc5wj4cO",
2745 "path": "apps/server/src/config.ts",
2746 "line": 41,
2747 "is_outdated": false,
2748 "is_resolved": false,
2749 "author": "macroscopeapp",
2750 "title": "`Effect.callback` should return a cleanup function to close the server(s) on fiber interruption. Without it, the `Net.Server` handles keep the process alive and leak the port if the effect is cancelled. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
2751 "severity": "Low",
2752 "has_false_positive_signal": false,
2753 "canonical_id": "C037",
2754 "phase0_status": "actionable_canonical",
2755 "phase0_reason": "Canonical actionable item."
2756 },
2757 {
2758 "index": 123,
2759 "thread_id": "PRRT_kwDORLtfbc5wj5fl",
2760 "path": "apps/server/package.json",
2761 "line": 14,
2762 "is_outdated": false,
2763 "is_resolved": false,
2764 "author": "cursor",
2765 "title": "Build script uses `node` to run `.ts` file directly",
2766 "severity": "Medium",
2767 "has_false_positive_signal": false,
2768 "canonical_id": "C007",
2769 "phase0_status": "actionable_duplicate",
2770 "phase0_reason": "Duplicate of C007."
2771 },
2772 {
2773 "index": 124,
2774 "thread_id": "PRRT_kwDORLtfbc5wj5fn",
2775 "path": "apps/server/src/checkpointing/Errors.ts",
2776 "line": 79,
2777 "is_outdated": false,
2778 "is_resolved": false,
2779 "author": "cursor",
2780 "title": "Redundant `CheckpointInvariantError` in `CheckpointServiceError` union type",
2781 "severity": "Low",
2782 "has_false_positive_signal": false,
2783 "canonical_id": "C034",
2784 "phase0_status": "actionable_canonical",
2785 "phase0_reason": "Canonical actionable item."
2786 },
2787 {
2788 "index": 125,
2789 "thread_id": "PRRT_kwDORLtfbc5wkGlg",
2790 "path": "apps/server/src/ptyAdapter.ts",
2791 "line": null,
2792 "is_outdated": true,
2793 "is_resolved": true,
2794 "author": "macroscopeapp",
2795 "title": "On Windows with Bun, `layer()` returns `BunPtyAdapterLive` which crashes via `Effect.die`. Consider checking `process.platform !== \"win32\"` before choosing `BunPtyAdapterLive`, falling back to `NodePtyAdapterLive` on Windows. No longer relevant as of 3ff826d7cb50031120321f116c1d40f14b030fb8",
2796 "severity": "High",
2797 "has_false_positive_signal": false,
2798 "phase0_status": "filtered_outdated",
2799 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2800 },
2801 {
2802 "index": 126,
2803 "thread_id": "PRRT_kwDORLtfbc5wkGll",
2804 "path": "apps/server/src/ptyAdapter.ts",
2805 "line": null,
2806 "is_outdated": true,
2807 "is_resolved": true,
2808 "author": "macroscopeapp",
2809 "title": "Race condition: `emitData` returns early if `didExit` is true, but the terminal data callback may fire after `emitExit` sets `didExit = true`, dropping buffered output. Consider removing the `didExit` guard in `emitData`, or deferring `didExit = true` until after processing any pending data callbacks. No longer relevant as of 3ff826d7cb50031120321f116c1d40f14b030fb8",
2810 "severity": "Medium",
2811 "has_false_positive_signal": false,
2812 "phase0_status": "filtered_outdated",
2813 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2814 },
2815 {
2816 "index": 127,
2817 "thread_id": "PRRT_kwDORLtfbc5wkGlt",
2818 "path": "apps/server/src/ptyAdapter.ts",
2819 "line": null,
2820 "is_outdated": true,
2821 "is_resolved": true,
2822 "author": "macroscopeapp",
2823 "title": "The `didEnsureSpawnHelperExecutable` flag is set before `chmodSync` succeeds. If chmod fails transiently, subsequent calls skip the fix permanently. Consider moving the flag assignment after the chmod operation. No longer relevant as of 3ff826d7cb50031120321f116c1d40f14b030fb8",
2824 "severity": "Low",
2825 "has_false_positive_signal": false,
2826 "phase0_status": "filtered_outdated",
2827 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2828 },
2829 {
2830 "index": 128,
2831 "thread_id": "PRRT_kwDORLtfbc5wkPaA",
2832 "path": "apps/server/src/orchestration/decider.ts",
2833 "line": 27,
2834 "is_outdated": false,
2835 "is_resolved": false,
2836 "author": "cursor",
2837 "title": "Shared mutable default metadata object causes stale eventId",
2838 "severity": "Medium",
2839 "has_false_positive_signal": false,
2840 "phase0_status": "filtered_invalid",
2841 "phase0_reason": "Invalid after post-rewrite audit: Stale-eventId claim no longer applies; eventId is regenerated per event."
2842 },
2843 {
2844 "index": 129,
2845 "thread_id": "PRRT_kwDORLtfbc5wkPaG",
2846 "path": "apps/server/src/wsServer.ts",
2847 "line": 128,
2848 "is_outdated": false,
2849 "is_resolved": false,
2850 "author": "cursor",
2851 "title": "Duplicate `checkpointRefForThreadTurn` function across files",
2852 "severity": "Low",
2853 "has_false_positive_signal": false,
2854 "phase0_status": "filtered_invalid",
2855 "phase0_reason": "Invalid after post-rewrite audit: No longer duplicated; helper is centralized."
2856 },
2857 {
2858 "index": 130,
2859 "thread_id": "PRRT_kwDORLtfbc5wkPaJ",
2860 "path": "apps/server/src/orchestration/Layers/OrchestrationEngine.ts",
2861 "line": 144,
2862 "is_outdated": false,
2863 "is_resolved": false,
2864 "author": "cursor",
2865 "title": "Engine error handler catches all errors including non-invariant ones",
2866 "severity": "Medium",
2867 "has_false_positive_signal": false,
2868 "canonical_id": "C014",
2869 "phase0_status": "actionable_canonical",
2870 "phase0_reason": "Canonical actionable item."
2871 },
2872 {
2873 "index": 131,
2874 "thread_id": "PRRT_kwDORLtfbc5wkPaL",
2875 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
2876 "line": 178,
2877 "is_outdated": false,
2878 "is_resolved": false,
2879 "author": "cursor",
2880 "title": "ProviderRuntimeIngestion processes events for wrong thread on race",
2881 "severity": "Medium",
2882 "has_false_positive_signal": false,
2883 "canonical_id": "C019",
2884 "phase0_status": "actionable_canonical",
2885 "phase0_reason": "Canonical actionable item."
2886 },
2887 {
2888 "index": 132,
2889 "thread_id": "PRRT_kwDORLtfbc5wkPaO",
2890 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
2891 "line": 274,
2892 "is_outdated": false,
2893 "is_resolved": false,
2894 "author": "cursor",
2895 "title": "Checkpoint reactor swallows diff errors silently for `turn.completed`",
2896 "severity": "Low",
2897 "has_false_positive_signal": false,
2898 "canonical_id": "C044",
2899 "phase0_status": "actionable_canonical",
2900 "phase0_reason": "Canonical actionable item."
2901 },
2902 {
2903 "index": 133,
2904 "thread_id": "PRRT_kwDORLtfbc5wkRZM",
2905 "path": "apps/server/src/os-jank.ts",
2906 "line": 10,
2907 "is_outdated": false,
2908 "is_resolved": false,
2909 "author": "macroscopeapp",
2910 "title": "Fish shell outputs `$PATH` as space-separated, not colon-separated. Consider checking if the shell is fish and using `string join : $PATH` instead, or validating the result contains colons before assigning.",
2911 "severity": "Medium",
2912 "has_false_positive_signal": false,
2913 "canonical_id": "C022",
2914 "phase0_status": "actionable_canonical",
2915 "phase0_reason": "Canonical actionable item."
2916 },
2917 {
2918 "index": 134,
2919 "thread_id": "PRRT_kwDORLtfbc5wlXJp",
2920 "path": "apps/web/src/components/ui/sidebar.tsx",
2921 "line": null,
2922 "is_outdated": true,
2923 "is_resolved": true,
2924 "author": "macroscopeapp",
2925 "title": "Consider spreading `...props` onto the outer wrapper `div` (line 207) instead of the inner container (line 240). Currently, props like `className=\"hidden\"` only affect the inner fixed div, leaving the outer layout placeholder visible and causing \"ghost\" space. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
2926 "severity": "Low",
2927 "has_false_positive_signal": false,
2928 "phase0_status": "filtered_outdated",
2929 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2930 },
2931 {
2932 "index": 135,
2933 "thread_id": "PRRT_kwDORLtfbc5wlXJr",
2934 "path": "apps/web/src/components/ui/sidebar.tsx",
2935 "line": 122,
2936 "is_outdated": false,
2937 "is_resolved": true,
2938 "author": "macroscopeapp",
2939 "title": "`cookieStore` is only available in Chromium browsers. Consider adding a guard like `if ('cookieStore' in window)` before accessing it to prevent `ReferenceError` in Firefox/Safari.",
2940 "severity": "Medium",
2941 "has_false_positive_signal": false,
2942 "phase0_status": "filtered_resolved",
2943 "phase0_reason": "Thread is already resolved on GitHub."
2944 },
2945 {
2946 "index": 136,
2947 "thread_id": "PRRT_kwDORLtfbc5wlXJz",
2948 "path": "apps/server/src/orchestration/Layers/ProviderCommandReactor.ts",
2949 "line": null,
2950 "is_outdated": true,
2951 "is_resolved": false,
2952 "author": "macroscopeapp",
2953 "title": "The `processDomainEvent` consumer loop lacks error handling. A single `providerService` failure will crash the fiber and stop all subsequent event processing. Consider wrapping with `Effect.catchAll` or `Effect.ignoreLogged` to let the loop continue after errors. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
2954 "severity": "High",
2955 "has_false_positive_signal": false,
2956 "phase0_status": "filtered_outdated",
2957 "phase0_reason": "Thread is flagged outdated by GitHub diff context."
2958 },
2959 {
2960 "index": 137,
2961 "thread_id": "PRRT_kwDORLtfbc5wlYgj",
2962 "path": "AGENTS.md",
2963 "line": 45,
2964 "is_outdated": false,
2965 "is_resolved": false,
2966 "author": "cursor",
2967 "title": "Dummy workflow marker accidentally committed to docs",
2968 "severity": "Low",
2969 "has_false_positive_signal": false,
2970 "canonical_id": "C032",
2971 "phase0_status": "actionable_canonical",
2972 "phase0_reason": "Canonical actionable item."
2973 },
2974 {
2975 "index": 138,
2976 "thread_id": "PRRT_kwDORLtfbc5wlYgo",
2977 "path": "apps/server/src/checkpointing/Errors.ts",
2978 "line": 51,
2979 "is_outdated": false,
2980 "is_resolved": false,
2981 "author": "cursor",
2982 "title": "Three error classes defined but never instantiated anywhere",
2983 "severity": "Low",
2984 "has_false_positive_signal": false,
2985 "canonical_id": "C033",
2986 "phase0_status": "actionable_canonical",
2987 "phase0_reason": "Canonical actionable item."
2988 },
2989 {
2990 "index": 139,
2991 "thread_id": "PRRT_kwDORLtfbc5wlYgs",
2992 "path": "apps/server/src/checkpointing/Errors.ts",
2993 "line": 79,
2994 "is_outdated": false,
2995 "is_resolved": false,
2996 "author": "cursor",
2997 "title": "Redundant error type in CheckpointServiceError union definition",
2998 "severity": "Low",
2999 "has_false_positive_signal": false,
3000 "canonical_id": "C035",
3001 "phase0_status": "actionable_canonical",
3002 "phase0_reason": "Canonical actionable item."
3003 },
3004 {
3005 "index": 140,
3006 "thread_id": "PRRT_kwDORLtfbc5wl2A8",
3007 "path": "apps/server/src/orchestration/Layers/ProviderCommandReactor.ts",
3008 "line": 84,
3009 "is_outdated": false,
3010 "is_resolved": false,
3011 "author": "macroscopeapp",
3012 "title": "`handledTurnStartKeys` grows indefinitely since keys are never removed. Consider adding TTL-based cleanup or clearing entries after processing completes. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3013 "severity": "Low",
3014 "has_false_positive_signal": false,
3015 "canonical_id": "C018",
3016 "phase0_status": "actionable_duplicate",
3017 "phase0_reason": "Duplicate of C018."
3018 },
3019 {
3020 "index": 141,
3021 "thread_id": "PRRT_kwDORLtfbc5wnVsI",
3022 "path": "apps/server/src/provider/Layers/ProviderService.ts",
3023 "line": 59,
3024 "is_outdated": false,
3025 "is_resolved": false,
3026 "author": "macroscopeapp",
3027 "title": "`JSON.stringify(cause)` returns `undefined` for `undefined`, functions, or symbols, violating the `string` return type. Consider coercing the result to a string (e.g., `String(JSON.stringify(cause))`) or adding a fallback.",
3028 "severity": "Low",
3029 "has_false_positive_signal": false,
3030 "canonical_id": "C049",
3031 "phase0_status": "actionable_canonical",
3032 "phase0_reason": "Canonical actionable item."
3033 },
3034 {
3035 "index": 142,
3036 "thread_id": "PRRT_kwDORLtfbc5wnVsK",
3037 "path": "apps/web/src/markdown-links.ts",
3038 "line": 111,
3039 "is_outdated": false,
3040 "is_resolved": false,
3041 "author": "macroscopeapp",
3042 "title": "`EXTERNAL_SCHEME_PATTERN` matches `script.ts:10` as a scheme because `.ts:` looks like `scheme:`. Consider requiring `://` after the colon, or checking that what follows the colon is not just digits. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3043 "severity": "Medium",
3044 "has_false_positive_signal": false,
3045 "canonical_id": "C030",
3046 "phase0_status": "actionable_canonical",
3047 "phase0_reason": "Canonical actionable item."
3048 },
3049 {
3050 "index": 143,
3051 "thread_id": "PRRT_kwDORLtfbc5wnVsU",
3052 "path": "apps/web/src/markdown-links.ts",
3053 "line": 105,
3054 "is_outdated": false,
3055 "is_resolved": false,
3056 "author": "macroscopeapp",
3057 "title": "`parseFileUrlHref` already decodes the path (line 46), but `safeDecode` is called again here, corrupting filenames containing `%` sequences. Consider skipping the decode when `fileUrlTarget` is non-null.",
3058 "severity": "Medium",
3059 "has_false_positive_signal": false,
3060 "canonical_id": "C029",
3061 "phase0_status": "actionable_canonical",
3062 "phase0_reason": "Canonical actionable item."
3063 },
3064 {
3065 "index": 144,
3066 "thread_id": "PRRT_kwDORLtfbc5wnVsX",
3067 "path": "apps/web/src/routes/_chat.$threadId.tsx",
3068 "line": 122,
3069 "is_outdated": false,
3070 "is_resolved": false,
3071 "author": "macroscopeapp",
3072 "title": "When `localStorage.getItem()` returns `null`, `Number(null)` evaluates to `0`, which passes `Number.isFinite(0)`. This causes the sidebar to clamp to `minWidth` on first load, overriding the `DIFF_INLINE_DEFAULT_WIDTH` CSS clamp. Consider checking for `null` or empty string before parsing, e.g. guard with `storedWidth === null || storedWidth === ''`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3073 "severity": "Low",
3074 "has_false_positive_signal": false,
3075 "canonical_id": "C058",
3076 "phase0_status": "actionable_canonical",
3077 "phase0_reason": "Canonical actionable item."
3078 },
3079 {
3080 "index": 145,
3081 "thread_id": "PRRT_kwDORLtfbc5wnWv_",
3082 "path": "apps/server/src/checkpointing/Layers/CheckpointStore.ts",
3083 "line": 94,
3084 "is_outdated": false,
3085 "is_resolved": false,
3086 "author": "cursor",
3087 "title": "Inconsistent input normalization across CheckpointStore methods",
3088 "severity": "Medium",
3089 "has_false_positive_signal": false,
3090 "canonical_id": "C008",
3091 "phase0_status": "actionable_duplicate",
3092 "phase0_reason": "Duplicate of C008."
3093 },
3094 {
3095 "index": 146,
3096 "thread_id": "PRRT_kwDORLtfbc5wnWwF",
3097 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
3098 "line": 584,
3099 "is_outdated": false,
3100 "is_resolved": false,
3101 "author": "cursor",
3102 "title": "Unhandled errors in checkpoint reactor worker crash processing permanently",
3103 "severity": "High",
3104 "has_false_positive_signal": false,
3105 "canonical_id": "C002",
3106 "phase0_status": "actionable_duplicate",
3107 "phase0_reason": "Duplicate of C002."
3108 },
3109 {
3110 "index": 147,
3111 "thread_id": "PRRT_kwDORLtfbc5wnWwM",
3112 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
3113 "line": 62,
3114 "is_outdated": false,
3115 "is_resolved": false,
3116 "author": "cursor",
3117 "title": "Duplicated workspace CWD resolution logic across reactor modules",
3118 "severity": "Low",
3119 "has_false_positive_signal": false,
3120 "canonical_id": "C043",
3121 "phase0_status": "actionable_canonical",
3122 "phase0_reason": "Canonical actionable item."
3123 },
3124 {
3125 "index": 148,
3126 "thread_id": "PRRT_kwDORLtfbc5wvwag",
3127 "path": "apps/server/src/wsServer.ts",
3128 "line": 128,
3129 "is_outdated": false,
3130 "is_resolved": false,
3131 "author": "cursor",
3132 "title": "Duplicated checkpoint ref computation across two files",
3133 "severity": "Medium",
3134 "has_false_positive_signal": false,
3135 "phase0_status": "filtered_invalid",
3136 "phase0_reason": "Invalid after post-rewrite audit: No longer duplicated; checkpoint ref helper now centralized."
3137 },
3138 {
3139 "index": 149,
3140 "thread_id": "PRRT_kwDORLtfbc5wxsO6",
3141 "path": "apps/server/src/checkpointing/Errors.ts",
3142 "line": 79,
3143 "is_outdated": false,
3144 "is_resolved": false,
3145 "author": "cursor",
3146 "title": "Redundant error type in CheckpointServiceError union",
3147 "severity": "Low",
3148 "has_false_positive_signal": false,
3149 "canonical_id": "C035",
3150 "phase0_status": "actionable_duplicate",
3151 "phase0_reason": "Duplicate of C035."
3152 },
3153 {
3154 "index": 150,
3155 "thread_id": "PRRT_kwDORLtfbc5wxsO-",
3156 "path": "apps/server/package.json",
3157 "line": 14,
3158 "is_outdated": false,
3159 "is_resolved": false,
3160 "author": "cursor",
3161 "title": "Build script runs TypeScript file with bare node",
3162 "severity": "Medium",
3163 "has_false_positive_signal": false,
3164 "canonical_id": "C006",
3165 "phase0_status": "actionable_canonical",
3166 "phase0_reason": "Canonical actionable item."
3167 },
3168 {
3169 "index": 151,
3170 "thread_id": "PRRT_kwDORLtfbc5wxvH8",
3171 "path": "apps/server/src/orchestration/Layers/ProjectionPipeline.ts",
3172 "line": 490,
3173 "is_outdated": false,
3174 "is_resolved": false,
3175 "author": "macroscopeapp",
3176 "title": "The in-memory `pendingTurnStartByThreadId` map isn't restored during bootstrap. If the service restarts after processing `thread.turn-start-requested` but before `thread.session-set`, the `userMessageId` and `startedAt` will be lost since bootstrap resumes *after* the committed sequence. Consider persisting this pending state or processing these two events atomically. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3177 "severity": "Medium",
3178 "has_false_positive_signal": false,
3179 "canonical_id": "C016",
3180 "phase0_status": "actionable_canonical",
3181 "phase0_reason": "Canonical actionable item."
3182 },
3183 {
3184 "index": 152,
3185 "thread_id": "PRRT_kwDORLtfbc5wxvH9",
3186 "path": "apps/server/src/provider/Layers/ProviderSessionDirectory.ts",
3187 "line": 119,
3188 "is_outdated": false,
3189 "is_resolved": false,
3190 "author": "macroscopeapp",
3191 "title": "Using `??` for `providerThreadId` and `adapterKey` makes it impossible to clear these fields by passing `null`, since `null ?? existing` evaluates to `existing`. Consider using explicit `undefined` checks (like `resumeCursor` does) if clearing should be supported. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3192 "severity": "Low",
3193 "has_false_positive_signal": false,
3194 "canonical_id": "C051",
3195 "phase0_status": "actionable_canonical",
3196 "phase0_reason": "Canonical actionable item."
3197 },
3198 {
3199 "index": 153,
3200 "thread_id": "PRRT_kwDORLtfbc5wxvIA",
3201 "path": "apps/server/src/keybindings.ts",
3202 "line": 488,
3203 "is_outdated": false,
3204 "is_resolved": false,
3205 "author": "macroscopeapp",
3206 "title": "`upsertKeybindingRule` has a race condition: concurrent calls read the same file state, then the last write overwrites earlier changes. Consider wrapping the read-modify-write sequence with `Effect.Semaphore` to serialize access. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3207 "severity": "Low",
3208 "has_false_positive_signal": false,
3209 "canonical_id": "C040",
3210 "phase0_status": "actionable_canonical",
3211 "phase0_reason": "Canonical actionable item."
3212 },
3213 {
3214 "index": 154,
3215 "thread_id": "PRRT_kwDORLtfbc5wxvIB",
3216 "path": "apps/server/src/keybindings.ts",
3217 "line": 352,
3218 "is_outdated": false,
3219 "is_resolved": false,
3220 "author": "macroscopeapp",
3221 "title": "The `+` key can be parsed (via trailing `+` handling) but cannot be encoded because `shortcut.key.includes(\"+\")` returns true for the literal `+` key. Consider checking `shortcut.key === \"+\"` separately and encoding it as `\"space\"` style (e.g., a special token), or adjusting the condition to allow the single `+` character. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3222 "severity": "Low",
3223 "has_false_positive_signal": false,
3224 "canonical_id": "C039",
3225 "phase0_status": "actionable_canonical",
3226 "phase0_reason": "Canonical actionable item."
3227 },
3228 {
3229 "index": 155,
3230 "thread_id": "PRRT_kwDORLtfbc5wxvIJ",
3231 "path": "apps/server/src/keybindings.ts",
3232 "line": 418,
3233 "is_outdated": false,
3234 "is_resolved": false,
3235 "author": "macroscopeapp",
3236 "title": "`loadCustomKeybindingsConfig` fails when the config file doesn't exist, which is expected for new users. Consider catching `ENOENT` and returning an empty array instead. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3237 "severity": "Medium",
3238 "has_false_positive_signal": false,
3239 "canonical_id": "C010",
3240 "phase0_status": "actionable_canonical",
3241 "phase0_reason": "Canonical actionable item."
3242 },
3243 {
3244 "index": 156,
3245 "thread_id": "PRRT_kwDORLtfbc5wxvIQ",
3246 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
3247 "line": 133,
3248 "is_outdated": false,
3249 "is_resolved": false,
3250 "author": "macroscopeapp",
3251 "title": "`latestMessageIdByTurnKey` is written to but never read, and `clearAssistantMessageIdsForTurn` doesn't clear its entries\u2014only `clearTurnStateForSession` does. Consider removing this map entirely if unused, or clearing it alongside `turnMessageIdsByTurnKey` in `clearAssistantMessageIdsForTurn`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3252 "severity": "Low",
3253 "has_false_positive_signal": false,
3254 "canonical_id": "C046",
3255 "phase0_status": "actionable_canonical",
3256 "phase0_reason": "Canonical actionable item."
3257 },
3258 {
3259 "index": 157,
3260 "thread_id": "PRRT_kwDORLtfbc5wxvIq",
3261 "path": "apps/web/src/components/ui/sidebar.tsx",
3262 "line": 114,
3263 "is_outdated": false,
3264 "is_resolved": false,
3265 "author": "macroscopeapp",
3266 "title": "When `onOpenChange` is provided without `open`, the internal `_open` state never updates because `setOpenProp` takes precedence. Consider calling `_setOpen` when `openProp === undefined`, regardless of whether `setOpenProp` exists.",
3267 "severity": "Low",
3268 "has_false_positive_signal": false,
3269 "canonical_id": "C056",
3270 "phase0_status": "actionable_canonical",
3271 "phase0_reason": "Canonical actionable item."
3272 },
3273 {
3274 "index": 158,
3275 "thread_id": "PRRT_kwDORLtfbc5wyTaP",
3276 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
3277 "line": 334,
3278 "is_outdated": false,
3279 "is_resolved": false,
3280 "author": "cursor",
3281 "title": "Worker fibers die permanently on single processing error",
3282 "severity": "High",
3283 "has_false_positive_signal": false,
3284 "canonical_id": "C002",
3285 "phase0_status": "actionable_duplicate",
3286 "phase0_reason": "Duplicate of C002."
3287 },
3288 {
3289 "index": 159,
3290 "thread_id": "PRRT_kwDORLtfbc5wyTaS",
3291 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
3292 "line": 542,
3293 "is_outdated": false,
3294 "is_resolved": false,
3295 "author": "cursor",
3296 "title": "Revert completion dispatched as fire-and-forget fork",
3297 "severity": "Medium",
3298 "has_false_positive_signal": false,
3299 "canonical_id": "C012",
3300 "phase0_status": "actionable_duplicate",
3301 "phase0_reason": "Duplicate of C012."
3302 },
3303 {
3304 "index": 160,
3305 "thread_id": "PRRT_kwDORLtfbc5wyTaT",
3306 "path": "apps/server/src/orchestration/Layers/ProviderCommandReactor.ts",
3307 "line": 84,
3308 "is_outdated": false,
3309 "is_resolved": false,
3310 "author": "cursor",
3311 "title": "Unbounded memory growth in turn start deduplication set",
3312 "severity": "Medium",
3313 "has_false_positive_signal": false,
3314 "canonical_id": "C018",
3315 "phase0_status": "actionable_duplicate",
3316 "phase0_reason": "Duplicate of C018."
3317 },
3318 {
3319 "index": 161,
3320 "thread_id": "PRRT_kwDORLtfbc5wyTaW",
3321 "path": "apps/server/src/wsServer.ts",
3322 "line": 545,
3323 "is_outdated": false,
3324 "is_resolved": false,
3325 "author": "cursor",
3326 "title": "WS message handler can cause unhandled promise rejection",
3327 "severity": "Medium",
3328 "has_false_positive_signal": false,
3329 "canonical_id": "C027",
3330 "phase0_status": "actionable_canonical",
3331 "phase0_reason": "Canonical actionable item."
3332 },
3333 {
3334 "index": 162,
3335 "thread_id": "PRRT_kwDORLtfbc5wyWz4",
3336 "path": "apps/web/src/routes/_chat.$threadId.tsx",
3337 "line": 105,
3338 "is_outdated": false,
3339 "is_resolved": false,
3340 "author": "macroscopeapp",
3341 "title": "The `resizable` object is recreated on every render, causing `SidebarRail`'s `useEffect` to repeatedly read localStorage and update the DOM. Consider memoizing the object with `useMemo`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3342 "severity": "Low",
3343 "has_false_positive_signal": false,
3344 "canonical_id": "C057",
3345 "phase0_status": "actionable_canonical",
3346 "phase0_reason": "Canonical actionable item."
3347 },
3348 {
3349 "index": 163,
3350 "thread_id": "PRRT_kwDORLtfbc5wzliw",
3351 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
3352 "line": 334,
3353 "is_outdated": false,
3354 "is_resolved": false,
3355 "author": "cursor",
3356 "title": "Reactor worker loops die permanently on single error",
3357 "severity": "High",
3358 "has_false_positive_signal": false,
3359 "canonical_id": "C002",
3360 "phase0_status": "actionable_duplicate",
3361 "phase0_reason": "Duplicate of C002."
3362 },
3363 {
3364 "index": 164,
3365 "thread_id": "PRRT_kwDORLtfbc5wzliz",
3366 "path": "apps/server/src/orchestration/Layers/ProviderCommandReactor.ts",
3367 "line": 84,
3368 "is_outdated": false,
3369 "is_resolved": false,
3370 "author": "cursor",
3371 "title": "Unbounded `handledTurnStartKeys` set causes memory leak",
3372 "severity": "Medium",
3373 "has_false_positive_signal": false,
3374 "canonical_id": "C018",
3375 "phase0_status": "actionable_duplicate",
3376 "phase0_reason": "Duplicate of C018."
3377 },
3378 {
3379 "index": 165,
3380 "thread_id": "PRRT_kwDORLtfbc5wzli0",
3381 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
3382 "line": 542,
3383 "is_outdated": false,
3384 "is_resolved": false,
3385 "author": "cursor",
3386 "title": "Revert completion dispatch is fire-and-forget via fork",
3387 "severity": "Medium",
3388 "has_false_positive_signal": false,
3389 "canonical_id": "C012",
3390 "phase0_status": "actionable_duplicate",
3391 "phase0_reason": "Duplicate of C012."
3392 },
3393 {
3394 "index": 166,
3395 "thread_id": "PRRT_kwDORLtfbc5wzli2",
3396 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
3397 "line": 62,
3398 "is_outdated": false,
3399 "is_resolved": false,
3400 "author": "cursor",
3401 "title": "Duplicated `resolveThreadWorkspaceCwd` across three files",
3402 "severity": "Low",
3403 "has_false_positive_signal": false,
3404 "canonical_id": "C042",
3405 "phase0_status": "actionable_canonical",
3406 "phase0_reason": "Canonical actionable item."
3407 },
3408 {
3409 "index": 167,
3410 "thread_id": "PRRT_kwDORLtfbc5wzli3",
3411 "path": "apps/server/src/wsServer.ts",
3412 "line": 545,
3413 "is_outdated": false,
3414 "is_resolved": false,
3415 "author": "cursor",
3416 "title": "WS message handler can cause unhandled promise rejection",
3417 "severity": "Medium",
3418 "has_false_positive_signal": false,
3419 "canonical_id": "C027",
3420 "phase0_status": "actionable_duplicate",
3421 "phase0_reason": "Duplicate of C027."
3422 },
3423 {
3424 "index": 168,
3425 "thread_id": "PRRT_kwDORLtfbc5wzp4R",
3426 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
3427 "line": 29,
3428 "is_outdated": false,
3429 "is_resolved": false,
3430 "author": "macroscopeapp",
3431 "title": "`truncateDetail` slices to `limit - 1` then appends `\"...\"` (3 chars), producing strings of length `limit + 2`. Consider slicing to `limit - 3` instead. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3432 "severity": "Low",
3433 "has_false_positive_signal": false,
3434 "canonical_id": "C045",
3435 "phase0_status": "actionable_canonical",
3436 "phase0_reason": "Canonical actionable item."
3437 },
3438 {
3439 "index": 169,
3440 "thread_id": "PRRT_kwDORLtfbc5wzp4T",
3441 "path": "apps/server/src/orchestration/Layers/OrchestrationEngine.ts",
3442 "line": 96,
3443 "is_outdated": false,
3444 "is_resolved": false,
3445 "author": "macroscopeapp",
3446 "title": "Non-atomic event appending can corrupt state on retry. If an error occurs mid-loop (lines 96-102) after some events are persisted but before the receipt is written, the command appears to fail. A retry generates new UUIDs via `crypto.randomUUID()` in the decider, appending duplicate events. Consider wrapping the loop in a transaction or using deterministic event IDs derived from `commandId`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3447 "severity": "High",
3448 "has_false_positive_signal": false,
3449 "canonical_id": "C001",
3450 "phase0_status": "actionable_canonical",
3451 "phase0_reason": "Canonical actionable item."
3452 },
3453 {
3454 "index": 170,
3455 "thread_id": "PRRT_kwDORLtfbc5w0_g3",
3456 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
3457 "line": 334,
3458 "is_outdated": false,
3459 "is_resolved": false,
3460 "author": "cursor",
3461 "title": "Reactor worker fibers die permanently on single error",
3462 "severity": "High",
3463 "has_false_positive_signal": false,
3464 "canonical_id": "C002",
3465 "phase0_status": "actionable_duplicate",
3466 "phase0_reason": "Duplicate of C002."
3467 },
3468 {
3469 "index": 171,
3470 "thread_id": "PRRT_kwDORLtfbc5w0_g4",
3471 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
3472 "line": 542,
3473 "is_outdated": false,
3474 "is_resolved": false,
3475 "author": "cursor",
3476 "title": "Forked revert completion creates state inconsistency window",
3477 "severity": "Medium",
3478 "has_false_positive_signal": false,
3479 "canonical_id": "C012",
3480 "phase0_status": "actionable_duplicate",
3481 "phase0_reason": "Duplicate of C012."
3482 },
3483 {
3484 "index": 172,
3485 "thread_id": "PRRT_kwDORLtfbc5w0_g7",
3486 "path": "apps/server/src/checkpointing/Layers/CheckpointStore.ts",
3487 "line": 95,
3488 "is_outdated": false,
3489 "is_resolved": false,
3490 "author": "cursor",
3491 "title": "Inconsistent input trimming across CheckpointStore methods",
3492 "severity": "Medium",
3493 "has_false_positive_signal": false,
3494 "canonical_id": "C008",
3495 "phase0_status": "actionable_duplicate",
3496 "phase0_reason": "Duplicate of C008."
3497 },
3498 {
3499 "index": 173,
3500 "thread_id": "PRRT_kwDORLtfbc5w0_g-",
3501 "path": "apps/server/src/orchestration/Layers/ProviderCommandReactor.ts",
3502 "line": 84,
3503 "is_outdated": false,
3504 "is_resolved": false,
3505 "author": "cursor",
3506 "title": "Unbounded `handledTurnStartKeys` Set causes memory leak",
3507 "severity": "Low",
3508 "has_false_positive_signal": false,
3509 "canonical_id": "C018",
3510 "phase0_status": "actionable_duplicate",
3511 "phase0_reason": "Duplicate of C018."
3512 },
3513 {
3514 "index": 174,
3515 "thread_id": "PRRT_kwDORLtfbc5w1CxE",
3516 "path": "apps/server/src/terminal/Layers/BunPTY.ts",
3517 "line": 97,
3518 "is_outdated": false,
3519 "is_resolved": false,
3520 "author": "macroscopeapp",
3521 "title": "Race condition: `processHandle` may be `null` when `data` callback fires, since it's assigned after `Bun.spawn` returns. Consider initializing `BunPtyProcess` first, then passing it to the callback to avoid losing initial output. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3522 "severity": "Low",
3523 "has_false_positive_signal": false,
3524 "canonical_id": "C052",
3525 "phase0_status": "actionable_canonical",
3526 "phase0_reason": "Canonical actionable item."
3527 },
3528 {
3529 "index": 175,
3530 "thread_id": "PRRT_kwDORLtfbc5w1CxT",
3531 "path": "apps/server/src/git/Layers/GitCore.ts",
3532 "line": 41,
3533 "is_outdated": false,
3534 "is_resolved": false,
3535 "author": "macroscopeapp",
3536 "title": "Git's braced rename syntax (e.g., `src/{old => new}/file.ts`) isn't handled correctly. The current slice after ` => ` produces invalid paths like `new}/file.ts`. Consider expanding the braces to construct the full destination path. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3537 "severity": "Medium",
3538 "has_false_positive_signal": false,
3539 "canonical_id": "C009",
3540 "phase0_status": "actionable_canonical",
3541 "phase0_reason": "Canonical actionable item."
3542 },
3543 {
3544 "index": 176,
3545 "thread_id": "PRRT_kwDORLtfbc5w1C36",
3546 "path": "apps/server/src/checkpointing/Layers/CheckpointStore.ts",
3547 "line": 94,
3548 "is_outdated": false,
3549 "is_resolved": false,
3550 "author": "cursor",
3551 "title": "Inconsistent input trimming across CheckpointStore methods",
3552 "severity": "Medium",
3553 "has_false_positive_signal": false,
3554 "canonical_id": "C008",
3555 "phase0_status": "actionable_duplicate",
3556 "phase0_reason": "Duplicate of C008."
3557 },
3558 {
3559 "index": 177,
3560 "thread_id": "PRRT_kwDORLtfbc5w1C3-",
3561 "path": "apps/server/src/checkpointing/Layers/CheckpointDiffQuery.ts",
3562 "line": 40,
3563 "is_outdated": false,
3564 "is_resolved": false,
3565 "author": "cursor",
3566 "title": "Duplicated resolveThreadWorkspaceCwd utility across files",
3567 "severity": "Low",
3568 "has_false_positive_signal": false,
3569 "canonical_id": "C043",
3570 "phase0_status": "actionable_duplicate",
3571 "phase0_reason": "Duplicate of C043."
3572 },
3573 {
3574 "index": 178,
3575 "thread_id": "PRRT_kwDORLtfbc5w1C4B",
3576 "path": "apps/server/src/checkpointing/Errors.ts",
3577 "line": 79,
3578 "is_outdated": false,
3579 "is_resolved": false,
3580 "author": "cursor",
3581 "title": "Redundant CheckpointInvariantError in service error union type",
3582 "severity": "Low",
3583 "has_false_positive_signal": false,
3584 "canonical_id": "C035",
3585 "phase0_status": "actionable_duplicate",
3586 "phase0_reason": "Duplicate of C035."
3587 },
3588 {
3589 "index": 179,
3590 "thread_id": "PRRT_kwDORLtfbc5w1C4H",
3591 "path": "AGENTS.md",
3592 "line": 45,
3593 "is_outdated": false,
3594 "is_resolved": false,
3595 "author": "cursor",
3596 "title": "Dummy workflow testing marker committed to AGENTS.md",
3597 "severity": "Low",
3598 "has_false_positive_signal": false,
3599 "canonical_id": "C032",
3600 "phase0_status": "actionable_duplicate",
3601 "phase0_reason": "Duplicate of C032."
3602 },
3603 {
3604 "index": 180,
3605 "thread_id": "PRRT_kwDORLtfbc5w1GPo",
3606 "path": "apps/server/src/git/Layers/CodexTextGeneration.ts",
3607 "line": 136,
3608 "is_outdated": false,
3609 "is_resolved": false,
3610 "author": "macroscopeapp",
3611 "title": "Multi-byte UTF-8 characters split across chunks will be corrupted when decoding each chunk separately. Consider accumulating all chunks first, then decoding once, or use `TextDecoder` with `stream: true`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
3612 "severity": "Low",
3613 "has_false_positive_signal": false,
3614 "canonical_id": "C038",
3615 "phase0_status": "actionable_canonical",
3616 "phase0_reason": "Canonical actionable item."
3617 },
3618 {
3619 "index": 181,
3620 "thread_id": "PRRT_kwDORLtfbc5w1GPr",
3621 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
3622 "line": 266,
3623 "is_outdated": false,
3624 "is_resolved": false,
3625 "author": "macroscopeapp",
3626 "title": "On `message.completed`, the message ID is added to the set and `thread.message.assistant.complete` is dispatched. On `turn.completed`, the same set is iterated and `thread.message.assistant.complete` is dispatched again for each ID\u2014including already-completed ones. Consider removing message IDs from the set after dispatching on `message.completed`, or filtering out already-completed IDs before the `turn.completed` loop.",
3627 "severity": "Medium",
3628 "has_false_positive_signal": false,
3629 "canonical_id": "C020",
3630 "phase0_status": "actionable_canonical",
3631 "phase0_reason": "Canonical actionable item."
3632 },
3633 {
3634 "index": 182,
3635 "thread_id": "PRRT_kwDORLtfbc5w1HGT",
3636 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
3637 "line": 334,
3638 "is_outdated": false,
3639 "is_resolved": false,
3640 "author": "cursor",
3641 "title": "Reactor worker fibers die permanently on single error",
3642 "severity": "High",
3643 "has_false_positive_signal": false,
3644 "canonical_id": "C002",
3645 "phase0_status": "actionable_duplicate",
3646 "phase0_reason": "Duplicate of C002."
3647 },
3648 {
3649 "index": 183,
3650 "thread_id": "PRRT_kwDORLtfbc5w1HGW",
3651 "path": "apps/server/src/orchestration/Layers/ProviderCommandReactor.ts",
3652 "line": 84,
3653 "is_outdated": false,
3654 "is_resolved": false,
3655 "author": "cursor",
3656 "title": "Unbounded memory growth in turn start deduplication set",
3657 "severity": "Medium",
3658 "has_false_positive_signal": false,
3659 "canonical_id": "C018",
3660 "phase0_status": "actionable_duplicate",
3661 "phase0_reason": "Duplicate of C018."
3662 },
3663 {
3664 "index": 184,
3665 "thread_id": "PRRT_kwDORLtfbc5w1HGX",
3666 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
3667 "line": 542,
3668 "is_outdated": false,
3669 "is_resolved": false,
3670 "author": "cursor",
3671 "title": "Revert completion dispatch is fire-and-forget, causing stale state",
3672 "severity": "Medium",
3673 "has_false_positive_signal": false,
3674 "canonical_id": "C012",
3675 "phase0_status": "actionable_duplicate",
3676 "phase0_reason": "Duplicate of C012."
3677 },
3678 {
3679 "index": 185,
3680 "thread_id": "PRRT_kwDORLtfbc5w1HGZ",
3681 "path": "apps/server/src/checkpointing/Layers/CheckpointDiffQuery.ts",
3682 "line": 40,
3683 "is_outdated": false,
3684 "is_resolved": false,
3685 "author": "cursor",
3686 "title": "Duplicated utility functions across checkpoint modules",
3687 "severity": "Low",
3688 "has_false_positive_signal": false,
3689 "canonical_id": "C043",
3690 "phase0_status": "actionable_duplicate",
3691 "phase0_reason": "Duplicate of C043."
3692 }
3693 ]
3694}
.plans/16a-pr89-phase0-canonical-triage.md deleted-201
...@@ -1,201 +0,0 @@
1# Phase 0: PR #89 Canonical Triage Lock
2
3- PR: https://github.com/pingdotgg/t3-code/pull/89
4- Title: Add server-side orchestration engine with event sourcing
5- Generated from latest GitHub data and deduplicated into canonical fix items.
6
7## Summary
8
9- Total threads: 185
10- Actionable canonical: 53
11- Actionable duplicates: 24
12- Filtered outdated: 94
13- Filtered resolved: 6
14- Filtered invalid: 8
15
16## Canonical Actionable Checklist
17
18### Other (16)
19
20- [ ] C011 [Medium] Template literal `${cause}` throws if `cause` is a `Symbol`. Consider using `String(cause)` instead. (apps/server/src/orchestration/Errors.ts:117)
21 - Threads: PRRT_kwDORLtfbc5wBFv4
22- [ ] C024 [Medium] In `executeUnprepared`, preparing outside an effect and not closing the statement can cause uncaught errors and leaks. Suggest wrapping with `Effect.acquireUseRelease`: prepare in acquire (error-safe) and `finalize` in release. (apps/server/src/persistence/NodeSqliteClient.ts:145)
23 - Threads: PRRT_kwDORLtfbc5whxX9
24- [ ] C028 [Medium] Branch sync dispatches both server and stale local update (apps/web/src/components/BranchToolbar.tsx:102)
25 - Threads: PRRT_kwDORLtfbc5v-XCu
26- [ ] C032 [Low] Dummy workflow marker accidentally committed to docs (AGENTS.md:45) (+1 dup)
27 - Threads: PRRT_kwDORLtfbc5wlYgj, PRRT_kwDORLtfbc5w1C4H
28- [ ] C037 [Low] `Effect.callback` should return a cleanup function to close the server(s) on fiber interruption. Without it, the `Net.Server` handles keep the process alive and leak the port if the effect is cancelled. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/config.ts:41)
29 - Threads: PRRT_kwDORLtfbc5wj4cO
30- [ ] C041 [Low] Template literal interpolation throws `TypeError` if `cause` is a `Symbol`. Consider using `String(cause)` for safe coercion. (apps/server/src/orchestration/Errors.ts:126)
31 - Threads: PRRT_kwDORLtfbc5wBFWs
32- [ ] C047 [Low] `SqlSchema.findOneOption` can produce both SQL errors and decode errors, but `mapError` wraps all as `PersistenceSqlError`. Consider distinguishing `ParseError` from SQL errors and mapping decode failures to `PersistenceDecodeError` instead. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/persistence/Layers/OrchestrationCommandReceipts.ts:75)
33 - Threads: PRRT_kwDORLtfbc5wiaR-
34- [ ] C048 [Low] `statement.run()` returns a `RunResult` object, not an array. Casting it to `ReadonlyArray<any>` will cause runtime errors when consumers call array methods. Consider wrapping the result (e.g., `[result]`) or returning a properly typed object. (apps/server/src/persistence/NodeSqliteClient.ts:99)
35 - Threads: PRRT_kwDORLtfbc5wio-r
36- [ ] C049 [Low] `JSON.stringify(cause)` returns `undefined` for `undefined`, functions, or symbols, violating the `string` return type. Consider coercing the result to a string (e.g., `String(JSON.stringify(cause))`) or adding a fallback. (apps/server/src/provider/Layers/ProviderService.ts:59)
37 - Threads: PRRT_kwDORLtfbc5wnVsI
38- [ ] C050 [Low] The read-modify-write pattern (`getBySessionId` → merge → `upsert`) is susceptible to lost updates under concurrent writes. Consider wrapping in a transaction or adding optimistic concurrency control (e.g., version field) if concurrent session updates are expected. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/provider/Layers/ProviderSessionDirectory.ts:94)
39 - Threads: PRRT_kwDORLtfbc5wiLhY
40- [ ] C051 [Low] Using `??` for `providerThreadId` and `adapterKey` makes it impossible to clear these fields by passing `null`, since `null ?? existing` evaluates to `existing`. Consider using explicit `undefined` checks (like `resumeCursor` does) if clearing should be supported. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/provider/Layers/ProviderSessionDirectory.ts:119)
41 - Threads: PRRT_kwDORLtfbc5wxvH9
42- [ ] C052 [Low] Race condition: `processHandle` may be `null` when `data` callback fires, since it's assigned after `Bun.spawn` returns. Consider initializing `BunPtyProcess` first, then passing it to the callback to avoid losing initial output. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/terminal/Layers/BunPTY.ts:97)
43 - Threads: PRRT_kwDORLtfbc5w1CxE
44- [ ] C056 [Low] When `onOpenChange` is provided without `open`, the internal `_open` state never updates because `setOpenProp` takes precedence. Consider calling `_setOpen` when `openProp === undefined`, regardless of whether `setOpenProp` exists. (apps/web/src/components/ui/sidebar.tsx:114)
45 - Threads: PRRT_kwDORLtfbc5wxvIq
46- [ ] C057 [Low] The `resizable` object is recreated on every render, causing `SidebarRail`'s `useEffect` to repeatedly read localStorage and update the DOM. Consider memoizing the object with `useMemo`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/web/src/routes/_chat.$threadId.tsx:105)
47 - Threads: PRRT_kwDORLtfbc5wyWz4
48- [ ] C058 [Low] When `localStorage.getItem()` returns `null`, `Number(null)` evaluates to `0`, which passes `Number.isFinite(0)`. This causes the sidebar to clamp to `minWidth` on first load, overriding the `DIFF_INLINE_DEFAULT_WIDTH` CSS clamp. Consider checking for `null` or empty string before parsing, e.g. guard with `storedWidth === null || storedWidth === ''`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/web/src/routes/_chat.$threadId.tsx:122)
49 - Threads: PRRT_kwDORLtfbc5wnVsX
50- [ ] C060 [Low] `defaultModel` should be `Schema.optional(Schema.NullOr(Schema.String))` to allow clearing the value. Currently there's no way to reset it to `null` since omitting means "no change" in patch semantics. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (packages/contracts/src/orchestration.ts:253)
51 - Threads: PRRT_kwDORLtfbc5whxJC
52
53### Runtime resilience and failure handling (10)
54
55- [x] C002 [High] A dispatch error in `processEvent` will terminate the `Effect.forever` loop, permanently halting event ingestion. Consider adding error recovery (e.g., `Effect.catchAll` with logging) around `processEvent` so failures don't kill the fiber. (apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:333) (+5 dup)
56 - Threads: PRRT_kwDORLtfbc5wj4cH, PRRT_kwDORLtfbc5wnWwF, PRRT_kwDORLtfbc5wyTaP, PRRT_kwDORLtfbc5wzliw, PRRT_kwDORLtfbc5w0_g3, PRRT_kwDORLtfbc5w1HGT
57- [x] C012 [Medium] Forked revert dispatch risks read model inconsistency (apps/server/src/orchestration/Layers/CheckpointReactor.ts:542) (+4 dup)
58 - Threads: PRRT_kwDORLtfbc5whszW, PRRT_kwDORLtfbc5wyTaS, PRRT_kwDORLtfbc5wzli0, PRRT_kwDORLtfbc5w0_g4, PRRT_kwDORLtfbc5w1HGX
59- [ ] C019 [Medium] ProviderRuntimeIngestion processes events for wrong thread on race (apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:178)
60 - Threads: PRRT_kwDORLtfbc5wkPaL
61- [ ] C020 [Medium] On `message.completed`, the message ID is added to the set and `thread.message.assistant.complete` is dispatched. On `turn.completed`, the same set is iterated and `thread.message.assistant.complete` is dispatched again for each ID—including already-completed ones. Consider removing message IDs from the set after dispatching on `message.completed`, or filtering out already-completed IDs before the `turn.completed` loop. (apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:266)
62 - Threads: PRRT_kwDORLtfbc5w1GPr
63- [x] C027 [Medium] WS message handler can cause unhandled promise rejection (apps/server/src/wsServer.ts:545) (+1 dup)
64 - Threads: PRRT_kwDORLtfbc5wyTaW, PRRT_kwDORLtfbc5wzli3
65- [ ] C042 [Low] Duplicated `resolveThreadWorkspaceCwd` across three files (apps/server/src/orchestration/Layers/CheckpointReactor.ts:62)
66 - Threads: PRRT_kwDORLtfbc5wzli2
67- [ ] C043 [Low] Duplicated workspace CWD resolution logic across reactor modules (apps/server/src/orchestration/Layers/CheckpointReactor.ts:62) (+2 dup)
68 - Threads: PRRT_kwDORLtfbc5wnWwM, PRRT_kwDORLtfbc5w1C3-, PRRT_kwDORLtfbc5w1HGZ
69- [ ] C044 [Low] Checkpoint reactor swallows diff errors silently for `turn.completed` (apps/server/src/orchestration/Layers/CheckpointReactor.ts:274)
70 - Threads: PRRT_kwDORLtfbc5wkPaO
71- [ ] C045 [Low] `truncateDetail` slices to `limit - 1` then appends `"..."` (3 chars), producing strings of length `limit + 2`. Consider slicing to `limit - 3` instead. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:29)
72 - Threads: PRRT_kwDORLtfbc5wzp4R
73- [ ] C046 [Low] `latestMessageIdByTurnKey` is written to but never read, and `clearAssistantMessageIdsForTurn` doesn't clear its entries—only `clearTurnStateForSession` does. Consider removing this map entirely if unused, or clearing it alongside `turnMessageIdsByTurnKey` in `clearAssistantMessageIdsForTurn`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:133)
74 - Threads: PRRT_kwDORLtfbc5wxvIQ
75
76### Edge-case parsing/platform behavior (9)
77
78- [ ] C009 [Medium] Git's braced rename syntax (e.g., `src/{old => new}/file.ts`) isn't handled correctly. The current slice after ` => ` produces invalid paths like `new}/file.ts`. Consider expanding the braces to construct the full destination path. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/git/Layers/GitCore.ts:41)
79 - Threads: PRRT_kwDORLtfbc5w1CxT
80- [ ] C010 [Medium] `loadCustomKeybindingsConfig` fails when the config file doesn't exist, which is expected for new users. Consider catching `ENOENT` and returning an empty array instead. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/keybindings.ts:418)
81 - Threads: PRRT_kwDORLtfbc5wxvIJ
82- [ ] C022 [Medium] Fish shell outputs `$PATH` as space-separated, not colon-separated. Consider checking if the shell is fish and using `string join : $PATH` instead, or validating the result contains colons before assigning. (apps/server/src/os-jank.ts:10)
83 - Threads: PRRT_kwDORLtfbc5wkRZM
84- [ ] C023 [Medium] Using `-il` flags causes the shell to source profile scripts that may print banners or other text, polluting the captured `PATH`. Consider using `-lc` (login only, non-interactive) to reduce unwanted output. (apps/server/src/os-jank.ts:10)
85 - Threads: PRRT_kwDORLtfbc5wj4cM
86- [ ] C029 [Medium] `parseFileUrlHref` already decodes the path (line 46), but `safeDecode` is called again here, corrupting filenames containing `%` sequences. Consider skipping the decode when `fileUrlTarget` is non-null. (apps/web/src/markdown-links.ts:105)
87 - Threads: PRRT_kwDORLtfbc5wnVsU
88- [ ] C030 [Medium] `EXTERNAL_SCHEME_PATTERN` matches `script.ts:10` as a scheme because `.ts:` looks like `scheme:`. Consider requiring `://` after the colon, or checking that what follows the colon is not just digits. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/web/src/markdown-links.ts:111)
89 - Threads: PRRT_kwDORLtfbc5wnVsK
90- [ ] C038 [Low] Multi-byte UTF-8 characters split across chunks will be corrupted when decoding each chunk separately. Consider accumulating all chunks first, then decoding once, or use `TextDecoder` with `stream: true`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/git/Layers/CodexTextGeneration.ts:136)
91 - Threads: PRRT_kwDORLtfbc5w1GPo
92- [ ] C039 [Low] The `+` key can be parsed (via trailing `+` handling) but cannot be encoded because `shortcut.key.includes("+")` returns true for the literal `+` key. Consider checking `shortcut.key === "+"` separately and encoding it as `"space"` style (e.g., a special token), or adjusting the condition to allow the single `+` character. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/keybindings.ts:352)
93 - Threads: PRRT_kwDORLtfbc5wxvIB
94- [ ] C040 [Low] `upsertKeybindingRule` has a race condition: concurrent calls read the same file state, then the last write overwrites earlier changes. Consider wrapping the read-modify-write sequence with `Effect.Semaphore` to serialize access. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/keybindings.ts:488)
95 - Threads: PRRT_kwDORLtfbc5wxvIA
96
97### Event ordering and state consistency (5)
98
99- [ ] C001 [High] Non-atomic event appending can corrupt state on retry. If an error occurs mid-loop (lines 96-102) after some events are persisted but before the receipt is written, the command appears to fail. A retry generates new UUIDs via `crypto.randomUUID()` in the decider, appending duplicate events. Consider wrapping the loop in a transaction or using deterministic event IDs derived from `commandId`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/orchestration/Layers/OrchestrationEngine.ts:96)
100 - Threads: PRRT_kwDORLtfbc5wzp4T
101- [ ] C013 [Medium] If `projectionPipeline.projectEvent` fails after `eventStore.append` succeeds, the event is persisted but `readModel` isn't updated, causing desync. Consider updating the in-memory `readModel` immediately after append (before the external projection), so local state stays consistent regardless of downstream failures. (apps/server/src/orchestration/Layers/OrchestrationEngine.ts:99)
102 - Threads: PRRT_kwDORLtfbc5whtrM
103- [ ] C014 [Medium] Engine error handler catches all errors including non-invariant ones (apps/server/src/orchestration/Layers/OrchestrationEngine.ts:144)
104 - Threads: PRRT_kwDORLtfbc5wkPaJ
105- [ ] C015 [Medium] The gap-filling fallback logic can retain messages from turns that are about to be deleted, causing foreign key violations. Consider removing the fallback logic entirely, or filtering `fallbackUserMessages` and `fallbackAssistantMessages` to only include messages whose `turnId` is in `retainedTurnIds`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/orchestration/Layers/ProjectionPipeline.ts:99)
106 - Threads: PRRT_kwDORLtfbc5whxJO
107- [ ] C016 [Medium] The in-memory `pendingTurnStartByThreadId` map isn't restored during bootstrap. If the service restarts after processing `thread.turn-start-requested` but before `thread.session-set`, the `userMessageId` and `startedAt` will be lost since bootstrap resumes *after* the committed sequence. Consider persisting this pending state or processing these two events atomically. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/orchestration/Layers/ProjectionPipeline.ts:490)
108 - Threads: PRRT_kwDORLtfbc5wxvH8
109
110### WebSocket robustness (5)
111
112- [ ] C003 [High] Consider attaching a no-op error listener before `socket.write` (e.g., `socket.on('error', () => {})`) to prevent an unhandled `EPIPE`/`ECONNRESET` from crashing the process if the client disconnects mid-handshake. (apps/server/src/wsServer.ts:75)
113 - Threads: PRRT_kwDORLtfbc5v-cf4
114- [x] C026 [Medium] Consider adding `.catch(() => {})` after `Effect.runPromise(handleMessage(ws, raw))` to prevent unhandled rejections from crashing the server if `encodeResponse` or setup logic fails. (apps/server/src/wsServer.ts:545)
115 - Threads: PRRT_kwDORLtfbc5wj4cE
116- [ ] C053 [Low] Consider using `socket.end(response)` instead of `socket.write(response)` + `socket.destroy()` to ensure the HTTP error response is fully flushed before closing the connection. (apps/server/src/wsServer.ts:83)
117 - Threads: PRRT_kwDORLtfbc5v-WPD
118- [ ] C054 [Low] When array chunks contain a multi-byte UTF-8 character split across boundaries, decoding each chunk separately produces replacement characters. Consider using `Buffer.concat()` on all chunks before calling `.toString("utf8")`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/wsServer.ts:104)
119 - Threads: PRRT_kwDORLtfbc5whtrR
120- [ ] C059 [Low] Suggestion: don’t spread `params` into `body`; it can override `_tag` and mishandle non-object values. Keep `_tag` separate and nest `params` under a single key (e.g., `data`), or validate `params` is a plain object. (apps/web/src/wsTransport.ts:59)
121 - Threads: PRRT_kwDORLtfbc5whtrN
122
123### Checkpointing correctness (5)
124
125- [ ] C008 [Medium] Inconsistent input normalization across CheckpointStore methods (apps/server/src/checkpointing/Layers/CheckpointStore.ts:94) (+3 dup)
126 - Threads: PRRT_kwDORLtfbc5widJw, PRRT_kwDORLtfbc5wnWv_, PRRT_kwDORLtfbc5w0_g7, PRRT_kwDORLtfbc5w1C36
127- [ ] C017 [Medium] `REQUIRED_SNAPSHOT_PROJECTORS` includes `pending-approvals` and `thread-turns`, but `getSnapshot` doesn't query their data. If these projectors lag behind, the returned `snapshotSequence` will be lower than what the included data actually reflects, causing clients to replay already-applied events. Consider filtering `REQUIRED_SNAPSHOT_PROJECTORS` to only include projectors whose data is actually fetched in the snapshot. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts:71)
128 - Threads: PRRT_kwDORLtfbc5wiLhQ
129- [ ] C033 [Low] Three error classes defined but never instantiated anywhere (apps/server/src/checkpointing/Errors.ts:51)
130 - Threads: PRRT_kwDORLtfbc5wlYgo
131- [ ] C034 [Low] Redundant `CheckpointInvariantError` in `CheckpointServiceError` union type (apps/server/src/checkpointing/Errors.ts:79)
132 - Threads: PRRT_kwDORLtfbc5wj5fn
133- [ ] C035 [Low] Redundant error type in CheckpointServiceError union definition (apps/server/src/checkpointing/Errors.ts:79) (+2 dup)
134 - Threads: PRRT_kwDORLtfbc5wlYgs, PRRT_kwDORLtfbc5wxsO6, PRRT_kwDORLtfbc5w1C4B
135
136### Build/runtime portability (2)
137
138- [ ] C006 [Medium] Build script runs TypeScript file with bare node (apps/server/package.json:14)
139 - Threads: PRRT_kwDORLtfbc5wxsO-
140- [ ] C007 [Medium] Build script uses `node` to run `.ts` file directly (apps/server/package.json:14) (+1 dup)
141 - Threads: PRRT_kwDORLtfbc5wiLNS, PRRT_kwDORLtfbc5wj5fl
142
143### Memory/resource growth (1)
144
145- [ ] C018 [Medium] Unbounded memory growth in turn start deduplication set (apps/server/src/orchestration/Layers/ProviderCommandReactor.ts:84) (+5 dup)
146 - Threads: PRRT_kwDORLtfbc5whszQ, PRRT_kwDORLtfbc5wl2A8, PRRT_kwDORLtfbc5wyTaT, PRRT_kwDORLtfbc5wzliz, PRRT_kwDORLtfbc5w0_g-, PRRT_kwDORLtfbc5w1HGW
147
148## Invalid / False-Positive Threads
149
150- PRRT_kwDORLtfbc5v9An7 | apps/web/src/routes/__root.tsx:140 | channel.** The web app currently does not consume provider event streams via the `providers.event` channel. While the orchestration API has `onReadModel()` and `onDomainEvent()` subscriptions, the providers event stream subscription is missing. Add a subscription to `ORCHESTRATION_WS_CHANNELS.event` (or the appropriate providers event channel from contracts) in `wsNativeApi.ts` and expose it via the `api.providers` object to comply with the guideline requirement. <details> <summary>🤖 Prompt for AI Agents</summary>
151 - Rationale: Invalid by design: provider runtime events are intentionally surfaced through orchestration domain events (`orchestration.domainEvent`), not a separate `providers.event` WebSocket channel in the current architecture.
152- PRRT_kwDORLtfbc5v9An- | apps/web/src/wsNativeApi.ts:165 | push stream, and the callbacks rely on unchecked casts. Please reintroduce a `providers.onEvent` (or equivalent) that subscribes to the `providers.event` channel and decode read-model/domain-event payloads using the shared contracts schemas before invoking callbacks. As per coding guidelines: "Web app must consume provider event streams via WebSocket push on channel `providers.event`" and "Use Zod schemas from `packages/contracts` for shared type contracts covering provider events, WebSocket protocol, and model/session types". <details> <summary>🤖 Prompt for AI Agents</summary>
153 - Rationale: Invalid by design: provider runtime events are intentionally surfaced through orchestration domain events (`orchestration.domainEvent`), not a separate `providers.event` WebSocket channel in the current architecture.
154- PRRT_kwDORLtfbc5v9SCp | apps/web/src/session-logic.ts:127 | Revert uses wrong turn count from positional inference
155 - Rationale: Invalid after post-rewrite audit: Revert now uses explicit checkpointTurnCount first; positional fallback is non-primary.
156- PRRT_kwDORLtfbc5widJx | apps/server/package.json:14 | Build script requires implicit Node.js TypeScript support
157 - Rationale: Thread contains explicit false-positive determination in replies.
158- PRRT_kwDORLtfbc5wiqFX | apps/server/src/checkpointing/Layers/CheckpointStore.ts:284 | Duplicate `checkpointRefForThreadTurn` function in two production files
159 - Rationale: Invalid after post-rewrite audit: No longer duplicated; single production source via Refs.ts.
160- PRRT_kwDORLtfbc5wkPaA | apps/server/src/orchestration/decider.ts:27 | Shared mutable default metadata object causes stale eventId
161 - Rationale: Invalid after post-rewrite audit: Stale-eventId claim no longer applies; eventId is regenerated per event.
162- PRRT_kwDORLtfbc5wkPaG | apps/server/src/wsServer.ts:128 | Duplicate `checkpointRefForThreadTurn` function across files
163 - Rationale: Invalid after post-rewrite audit: No longer duplicated; helper is centralized.
164- PRRT_kwDORLtfbc5wvwag | apps/server/src/wsServer.ts:128 | Duplicated checkpoint ref computation across two files
165 - Rationale: Invalid after post-rewrite audit: No longer duplicated; checkpoint ref helper now centralized.
166
167## Duplicate Mapping
168
169- Each duplicate thread is mapped to one canonical fix item.
170
171- PRRT_kwDORLtfbc5wj5fl | apps/server/package.json:14 -> C007
172- PRRT_kwDORLtfbc5wl2A8 | apps/server/src/orchestration/Layers/ProviderCommandReactor.ts:84 -> C018
173- PRRT_kwDORLtfbc5wnWv_ | apps/server/src/checkpointing/Layers/CheckpointStore.ts:94 -> C008
174- PRRT_kwDORLtfbc5wnWwF | apps/server/src/orchestration/Layers/CheckpointReactor.ts:584 -> C002
175- PRRT_kwDORLtfbc5wxsO6 | apps/server/src/checkpointing/Errors.ts:79 -> C035
176- PRRT_kwDORLtfbc5wyTaP | apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:334 -> C002
177- PRRT_kwDORLtfbc5wyTaS | apps/server/src/orchestration/Layers/CheckpointReactor.ts:542 -> C012
178- PRRT_kwDORLtfbc5wyTaT | apps/server/src/orchestration/Layers/ProviderCommandReactor.ts:84 -> C018
179- PRRT_kwDORLtfbc5wzliw | apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:334 -> C002
180- PRRT_kwDORLtfbc5wzliz | apps/server/src/orchestration/Layers/ProviderCommandReactor.ts:84 -> C018
181- PRRT_kwDORLtfbc5wzli0 | apps/server/src/orchestration/Layers/CheckpointReactor.ts:542 -> C012
182- PRRT_kwDORLtfbc5wzli3 | apps/server/src/wsServer.ts:545 -> C027
183- PRRT_kwDORLtfbc5w0_g3 | apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:334 -> C002
184- PRRT_kwDORLtfbc5w0_g4 | apps/server/src/orchestration/Layers/CheckpointReactor.ts:542 -> C012
185- PRRT_kwDORLtfbc5w0_g7 | apps/server/src/checkpointing/Layers/CheckpointStore.ts:95 -> C008
186- PRRT_kwDORLtfbc5w0_g- | apps/server/src/orchestration/Layers/ProviderCommandReactor.ts:84 -> C018
187- PRRT_kwDORLtfbc5w1C36 | apps/server/src/checkpointing/Layers/CheckpointStore.ts:94 -> C008
188- PRRT_kwDORLtfbc5w1C3- | apps/server/src/checkpointing/Layers/CheckpointDiffQuery.ts:40 -> C043
189- PRRT_kwDORLtfbc5w1C4B | apps/server/src/checkpointing/Errors.ts:79 -> C035
190- PRRT_kwDORLtfbc5w1C4H | AGENTS.md:45 -> C032
191- PRRT_kwDORLtfbc5w1HGT | apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:334 -> C002
192- PRRT_kwDORLtfbc5w1HGW | apps/server/src/orchestration/Layers/ProviderCommandReactor.ts:84 -> C018
193- PRRT_kwDORLtfbc5w1HGX | apps/server/src/orchestration/Layers/CheckpointReactor.ts:542 -> C012
194- PRRT_kwDORLtfbc5w1HGZ | apps/server/src/checkpointing/Layers/CheckpointDiffQuery.ts:40 -> C043
195
196## Outdated / Resolved Buckets
197
198- Outdated threads: 94
199- Resolved threads: 6
200
201Per-thread mapping source of truth: `.plans/16a-pr89-phase0-canonical-triage.json`.
.plans/16b-pr89-architecture-validity-audit.json deleted-790
...@@ -1,790 +0,0 @@
1{
2 "source": ".plans/16a-pr89-phase0-canonical-triage.json",
3 "architecture_assumption": "Provider runtime activity is projected and consumed via orchestration.domainEvent; no separate providers.event push channel is expected.",
4 "summary": {
5 "total_canonical_reviewed": 58,
6 "valid": 34,
7 "partially_valid": 19,
8 "invalid": 5
9 },
10 "records": [
11 {
12 "canonical_id": "C001",
13 "verdict": "valid",
14 "rationale": "Append/project/receipt are non-atomic; retry can duplicate events.",
15 "severity": "High",
16 "area": "Event ordering and state consistency",
17 "path": "apps/server/src/orchestration/Layers/OrchestrationEngine.ts",
18 "line": 96,
19 "title": "Non-atomic event appending can corrupt state on retry. If an error occurs mid-loop (lines 96-102) after some events are persisted but before the receipt is written, the command appears to fail. A retry generates new UUIDs via `crypto.randomUUID()` in the decider, appending duplicate events. Consider wrapping the loop in a transaction or using deterministic event IDs derived from `commandId`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
20 "thread_ids": [
21 "PRRT_kwDORLtfbc5wzp4T"
22 ]
23 },
24 {
25 "canonical_id": "C002",
26 "verdict": "valid",
27 "rationale": "Ingestion worker loop can terminate on unhandled processEvent failure.",
28 "severity": "High",
29 "area": "Runtime resilience and failure handling",
30 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
31 "line": 333,
32 "title": "A dispatch error in `processEvent` will terminate the `Effect.forever` loop, permanently halting event ingestion. Consider adding error recovery (e.g., `Effect.catchAll` with logging) around `processEvent` so failures don't kill the fiber.",
33 "thread_ids": [
34 "PRRT_kwDORLtfbc5wj4cH",
35 "PRRT_kwDORLtfbc5wnWwF",
36 "PRRT_kwDORLtfbc5wyTaP",
37 "PRRT_kwDORLtfbc5wzliw",
38 "PRRT_kwDORLtfbc5w0_g3",
39 "PRRT_kwDORLtfbc5w1HGT"
40 ]
41 },
42 {
43 "canonical_id": "C003",
44 "verdict": "valid",
45 "rationale": "Upgrade reject writes then destroys socket without defensive error listener.",
46 "severity": "High",
47 "area": "WebSocket robustness",
48 "path": "apps/server/src/wsServer.ts",
49 "line": 75,
50 "title": "Consider attaching a no-op error listener before `socket.write` (e.g., `socket.on('error', () => {})`) to prevent an unhandled `EPIPE`/`ECONNRESET` from crashing the process if the client disconnects mid-handshake.",
51 "thread_ids": [
52 "PRRT_kwDORLtfbc5v-cf4"
53 ]
54 },
55 {
56 "canonical_id": "C006",
57 "verdict": "partially-valid",
58 "rationale": "Node 24 makes this work, but script still relies on runtime-specific TS execution.",
59 "severity": "Medium",
60 "area": "Build/runtime portability",
61 "path": "apps/server/package.json",
62 "line": 14,
63 "title": "Build script runs TypeScript file with bare node",
64 "thread_ids": [
65 "PRRT_kwDORLtfbc5wxsO-"
66 ]
67 },
68 {
69 "canonical_id": "C007",
70 "verdict": "partially-valid",
71 "rationale": "Same as C006 (duplicate concern).",
72 "severity": "Medium",
73 "area": "Build/runtime portability",
74 "path": "apps/server/package.json",
75 "line": 14,
76 "title": "Build script uses `node` to run `.ts` file directly",
77 "thread_ids": [
78 "PRRT_kwDORLtfbc5wiLNS",
79 "PRRT_kwDORLtfbc5wj5fl"
80 ]
81 },
82 {
83 "canonical_id": "C008",
84 "verdict": "partially-valid",
85 "rationale": "Normalization inconsistency remains, but upstream callsites now mostly canonicalize inputs.",
86 "severity": "Medium",
87 "area": "Checkpointing correctness",
88 "path": "apps/server/src/checkpointing/Layers/CheckpointStore.ts",
89 "line": 94,
90 "title": "Inconsistent input normalization across CheckpointStore methods",
91 "thread_ids": [
92 "PRRT_kwDORLtfbc5widJw",
93 "PRRT_kwDORLtfbc5wnWv_",
94 "PRRT_kwDORLtfbc5w0_g7",
95 "PRRT_kwDORLtfbc5w1C36"
96 ]
97 },
98 {
99 "canonical_id": "C009",
100 "verdict": "valid",
101 "rationale": "Braced rename parsing still breaks paths like src/{old => new}/file.ts.",
102 "severity": "Medium",
103 "area": "Edge-case parsing/platform behavior",
104 "path": "apps/server/src/git/Layers/GitCore.ts",
105 "line": 41,
106 "title": "Git's braced rename syntax (e.g., `src/{old => new}/file.ts`) isn't handled correctly. The current slice after ` => ` produces invalid paths like `new}/file.ts`. Consider expanding the braces to construct the full destination path. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
107 "thread_ids": [
108 "PRRT_kwDORLtfbc5w1CxT"
109 ]
110 },
111 {
112 "canonical_id": "C010",
113 "verdict": "valid",
114 "rationale": "ENOENT for missing keybindings config still not handled as empty/default.",
115 "severity": "Medium",
116 "area": "Edge-case parsing/platform behavior",
117 "path": "apps/server/src/keybindings.ts",
118 "line": 418,
119 "title": "`loadCustomKeybindingsConfig` fails when the config file doesn't exist, which is expected for new users. Consider catching `ENOENT` and returning an empty array instead. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
120 "thread_ids": [
121 "PRRT_kwDORLtfbc5wxvIJ"
122 ]
123 },
124 {
125 "canonical_id": "C011",
126 "verdict": "valid",
127 "rationale": "Template-literal interpolation of Symbol still throws.",
128 "severity": "Medium",
129 "area": "Other",
130 "path": "apps/server/src/orchestration/Errors.ts",
131 "line": 117,
132 "title": "Template literal `${cause}` throws if `cause` is a `Symbol`. Consider using `String(cause)` instead.",
133 "thread_ids": [
134 "PRRT_kwDORLtfbc5wBFv4"
135 ]
136 },
137 {
138 "canonical_id": "C012",
139 "verdict": "valid",
140 "rationale": "Revert completion dispatch remains forked; state consistency window remains.",
141 "severity": "Medium",
142 "area": "Runtime resilience and failure handling",
143 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
144 "line": 542,
145 "title": "Forked revert dispatch risks read model inconsistency",
146 "thread_ids": [
147 "PRRT_kwDORLtfbc5whszW",
148 "PRRT_kwDORLtfbc5wyTaS",
149 "PRRT_kwDORLtfbc5wzli0",
150 "PRRT_kwDORLtfbc5w0_g4",
151 "PRRT_kwDORLtfbc5w1HGX"
152 ]
153 },
154 {
155 "canonical_id": "C013",
156 "verdict": "valid",
157 "rationale": "Persisted event can outpace in-memory projection on mid-flight failure.",
158 "severity": "Medium",
159 "area": "Event ordering and state consistency",
160 "path": "apps/server/src/orchestration/Layers/OrchestrationEngine.ts",
161 "line": 99,
162 "title": "If `projectionPipeline.projectEvent` fails after `eventStore.append` succeeds, the event is persisted but `readModel` isn't updated, causing desync. Consider updating the in-memory `readModel` immediately after append (before the external projection), so local state stays consistent regardless of downstream failures.",
163 "thread_ids": [
164 "PRRT_kwDORLtfbc5whtrM"
165 ]
166 },
167 {
168 "canonical_id": "C014",
169 "verdict": "partially-valid",
170 "rationale": "Broad catch may be intentional for liveness; correctness concern is contextual.",
171 "severity": "Medium",
172 "area": "Event ordering and state consistency",
173 "path": "apps/server/src/orchestration/Layers/OrchestrationEngine.ts",
174 "line": 144,
175 "title": "Engine error handler catches all errors including non-invariant ones",
176 "thread_ids": [
177 "PRRT_kwDORLtfbc5wkPaJ"
178 ]
179 },
180 {
181 "canonical_id": "C015",
182 "verdict": "partially-valid",
183 "rationale": "Message fallback retention issue is real, but prior FK-violation claim is overstated.",
184 "severity": "Medium",
185 "area": "Event ordering and state consistency",
186 "path": "apps/server/src/orchestration/Layers/ProjectionPipeline.ts",
187 "line": 99,
188 "title": "The gap-filling fallback logic can retain messages from turns that are about to be deleted, causing foreign key violations. Consider removing the fallback logic entirely, or filtering `fallbackUserMessages` and `fallbackAssistantMessages` to only include messages whose `turnId` is in `retainedTurnIds`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
189 "thread_ids": [
190 "PRRT_kwDORLtfbc5whxJO"
191 ]
192 },
193 {
194 "canonical_id": "C016",
195 "verdict": "valid",
196 "rationale": "Pending turn-start map is in-memory only and not rebuilt on bootstrap.",
197 "severity": "Medium",
198 "area": "Event ordering and state consistency",
199 "path": "apps/server/src/orchestration/Layers/ProjectionPipeline.ts",
200 "line": 490,
201 "title": "The in-memory `pendingTurnStartByThreadId` map isn't restored during bootstrap. If the service restarts after processing `thread.turn-start-requested` but before `thread.session-set`, the `userMessageId` and `startedAt` will be lost since bootstrap resumes *after* the committed sequence. Consider persisting this pending state or processing these two events atomically. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
202 "thread_ids": [
203 "PRRT_kwDORLtfbc5wxvH8"
204 ]
205 },
206 {
207 "canonical_id": "C017",
208 "verdict": "partially-valid",
209 "rationale": "Snapshot sequence can under-report due to extra projectors, but replay impact is lower now.",
210 "severity": "Medium",
211 "area": "Checkpointing correctness",
212 "path": "apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts",
213 "line": 71,
214 "title": "`REQUIRED_SNAPSHOT_PROJECTORS` includes `pending-approvals` and `thread-turns`, but `getSnapshot` doesn't query their data. If these projectors lag behind, the returned `snapshotSequence` will be lower than what the included data actually reflects, causing clients to replay already-applied events. Consider filtering `REQUIRED_SNAPSHOT_PROJECTORS` to only include projectors whose data is actually fetched in the snapshot. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
215 "thread_ids": [
216 "PRRT_kwDORLtfbc5wiLhQ"
217 ]
218 },
219 {
220 "canonical_id": "C018",
221 "verdict": "valid",
222 "rationale": "handledTurnStartKeys still grows without pruning.",
223 "severity": "Medium",
224 "area": "Memory/resource growth",
225 "path": "apps/server/src/orchestration/Layers/ProviderCommandReactor.ts",
226 "line": 84,
227 "title": "Unbounded memory growth in turn start deduplication set",
228 "thread_ids": [
229 "PRRT_kwDORLtfbc5whszQ",
230 "PRRT_kwDORLtfbc5wl2A8",
231 "PRRT_kwDORLtfbc5wyTaT",
232 "PRRT_kwDORLtfbc5wzliz",
233 "PRRT_kwDORLtfbc5w0_g-",
234 "PRRT_kwDORLtfbc5w1HGW"
235 ]
236 },
237 {
238 "canonical_id": "C019",
239 "verdict": "partially-valid",
240 "rationale": "SessionId-only routing can misassociate events under races/rebinds.",
241 "severity": "Medium",
242 "area": "Runtime resilience and failure handling",
243 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
244 "line": 178,
245 "title": "ProviderRuntimeIngestion processes events for wrong thread on race",
246 "thread_ids": [
247 "PRRT_kwDORLtfbc5wkPaL"
248 ]
249 },
250 {
251 "canonical_id": "C020",
252 "verdict": "partially-valid",
253 "rationale": "Duplicate complete dispatch exists; downstream impact often idempotent.",
254 "severity": "Medium",
255 "area": "Runtime resilience and failure handling",
256 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
257 "line": 266,
258 "title": "On `message.completed`, the message ID is added to the set and `thread.message.assistant.complete` is dispatched. On `turn.completed`, the same set is iterated and `thread.message.assistant.complete` is dispatched again for each ID\u2014including already-completed ones. Consider removing message IDs from the set after dispatching on `message.completed`, or filtering out already-completed IDs before the `turn.completed` loop.",
259 "thread_ids": [
260 "PRRT_kwDORLtfbc5w1GPr"
261 ]
262 },
263 {
264 "canonical_id": "C021",
265 "verdict": "invalid",
266 "rationale": "Stale-eventId claim no longer applies; eventId is regenerated per event.",
267 "severity": "Medium",
268 "area": "Other",
269 "path": "apps/server/src/orchestration/decider.ts",
270 "line": 27,
271 "title": "Shared mutable default metadata object causes stale eventId",
272 "thread_ids": [
273 "PRRT_kwDORLtfbc5wkPaA"
274 ]
275 },
276 {
277 "canonical_id": "C022",
278 "verdict": "valid",
279 "rationale": "fish PATH formatting risk still exists in os-jank path recovery.",
280 "severity": "Medium",
281 "area": "Edge-case parsing/platform behavior",
282 "path": "apps/server/src/os-jank.ts",
283 "line": 10,
284 "title": "Fish shell outputs `$PATH` as space-separated, not colon-separated. Consider checking if the shell is fish and using `string join : $PATH` instead, or validating the result contains colons before assigning.",
285 "thread_ids": [
286 "PRRT_kwDORLtfbc5wkRZM"
287 ]
288 },
289 {
290 "canonical_id": "C023",
291 "verdict": "valid",
292 "rationale": "-ilc shell invocation can pollute captured PATH output.",
293 "severity": "Medium",
294 "area": "Edge-case parsing/platform behavior",
295 "path": "apps/server/src/os-jank.ts",
296 "line": 10,
297 "title": "Using `-il` flags causes the shell to source profile scripts that may print banners or other text, polluting the captured `PATH`. Consider using `-lc` (login only, non-interactive) to reduce unwanted output.",
298 "thread_ids": [
299 "PRRT_kwDORLtfbc5wj4cM"
300 ]
301 },
302 {
303 "canonical_id": "C024",
304 "verdict": "partially-valid",
305 "rationale": "prepare() still outside managed effect boundary; failure handling risk remains.",
306 "severity": "Medium",
307 "area": "Other",
308 "path": "apps/server/src/persistence/NodeSqliteClient.ts",
309 "line": 145,
310 "title": "In `executeUnprepared`, preparing outside an effect and not closing the statement can cause uncaught errors and leaks. Suggest wrapping with `Effect.acquireUseRelease`: prepare in acquire (error-safe) and `finalize` in release.",
311 "thread_ids": [
312 "PRRT_kwDORLtfbc5whxX9"
313 ]
314 },
315 {
316 "canonical_id": "C025",
317 "verdict": "invalid",
318 "rationale": "No longer duplicated; checkpoint ref helper now centralized.",
319 "severity": "Medium",
320 "area": "Checkpointing correctness",
321 "path": "apps/server/src/wsServer.ts",
322 "line": 128,
323 "title": "Duplicated checkpoint ref computation across two files",
324 "thread_ids": [
325 "PRRT_kwDORLtfbc5wvwag"
326 ]
327 },
328 {
329 "canonical_id": "C026",
330 "verdict": "valid",
331 "rationale": "runPromise result still not caught; rejection can surface unhandled.",
332 "severity": "Medium",
333 "area": "WebSocket robustness",
334 "path": "apps/server/src/wsServer.ts",
335 "line": 545,
336 "title": "Consider adding `.catch(() => {})` after `Effect.runPromise(handleMessage(ws, raw))` to prevent unhandled rejections from crashing the server if `encodeResponse` or setup logic fails.",
337 "thread_ids": [
338 "PRRT_kwDORLtfbc5wj4cE"
339 ]
340 },
341 {
342 "canonical_id": "C027",
343 "verdict": "valid",
344 "rationale": "Same unhandled rejection path remains in WS message handler.",
345 "severity": "Medium",
346 "area": "Runtime resilience and failure handling",
347 "path": "apps/server/src/wsServer.ts",
348 "line": 545,
349 "title": "WS message handler can cause unhandled promise rejection",
350 "thread_ids": [
351 "PRRT_kwDORLtfbc5wyTaW",
352 "PRRT_kwDORLtfbc5wzli3"
353 ]
354 },
355 {
356 "canonical_id": "C028",
357 "verdict": "partially-valid",
358 "rationale": "Optimistic local+server dual update is intentional but can temporarily diverge.",
359 "severity": "Medium",
360 "area": "Other",
361 "path": "apps/web/src/components/BranchToolbar.tsx",
362 "line": 102,
363 "title": "Branch sync dispatches both server and stale local update",
364 "thread_ids": [
365 "PRRT_kwDORLtfbc5v-XCu"
366 ]
367 },
368 {
369 "canonical_id": "C029",
370 "verdict": "valid",
371 "rationale": "file URL decoding still double-decodes in one path.",
372 "severity": "Medium",
373 "area": "Edge-case parsing/platform behavior",
374 "path": "apps/web/src/markdown-links.ts",
375 "line": 105,
376 "title": "`parseFileUrlHref` already decodes the path (line 46), but `safeDecode` is called again here, corrupting filenames containing `%` sequences. Consider skipping the decode when `fileUrlTarget` is non-null.",
377 "thread_ids": [
378 "PRRT_kwDORLtfbc5wnVsU"
379 ]
380 },
381 {
382 "canonical_id": "C030",
383 "verdict": "valid",
384 "rationale": "Scheme regex still misclassifies script.ts:10 as external scheme.",
385 "severity": "Medium",
386 "area": "Edge-case parsing/platform behavior",
387 "path": "apps/web/src/markdown-links.ts",
388 "line": 111,
389 "title": "`EXTERNAL_SCHEME_PATTERN` matches `script.ts:10` as a scheme because `.ts:` looks like `scheme:`. Consider requiring `://` after the colon, or checking that what follows the colon is not just digits. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
390 "thread_ids": [
391 "PRRT_kwDORLtfbc5wnVsK"
392 ]
393 },
394 {
395 "canonical_id": "C031",
396 "verdict": "invalid",
397 "rationale": "Revert now uses explicit checkpointTurnCount first; positional fallback is non-primary.",
398 "severity": "Medium",
399 "area": "Event ordering and state consistency",
400 "path": "apps/web/src/session-logic.ts",
401 "line": 127,
402 "title": "Revert uses wrong turn count from positional inference",
403 "thread_ids": [
404 "PRRT_kwDORLtfbc5v9SCp"
405 ]
406 },
407 {
408 "canonical_id": "C032",
409 "verdict": "valid",
410 "rationale": "Dummy workflow marker comment is still present in docs.",
411 "severity": "Low",
412 "area": "Other",
413 "path": "AGENTS.md",
414 "line": 45,
415 "title": "Dummy workflow marker accidentally committed to docs",
416 "thread_ids": [
417 "PRRT_kwDORLtfbc5wlYgj",
418 "PRRT_kwDORLtfbc5w1C4H"
419 ]
420 },
421 {
422 "canonical_id": "C033",
423 "verdict": "partially-valid",
424 "rationale": "Original claim overstated; some errors used, others appear unused.",
425 "severity": "Low",
426 "area": "Checkpointing correctness",
427 "path": "apps/server/src/checkpointing/Errors.ts",
428 "line": 51,
429 "title": "Three error classes defined but never instantiated anywhere",
430 "thread_ids": [
431 "PRRT_kwDORLtfbc5wlYgo"
432 ]
433 },
434 {
435 "canonical_id": "C034",
436 "verdict": "valid",
437 "rationale": "CheckpointInvariantError remains redundantly included in service union.",
438 "severity": "Low",
439 "area": "Checkpointing correctness",
440 "path": "apps/server/src/checkpointing/Errors.ts",
441 "line": 79,
442 "title": "Redundant `CheckpointInvariantError` in `CheckpointServiceError` union type",
443 "thread_ids": [
444 "PRRT_kwDORLtfbc5wj5fn"
445 ]
446 },
447 {
448 "canonical_id": "C035",
449 "verdict": "valid",
450 "rationale": "Same as C034.",
451 "severity": "Low",
452 "area": "Checkpointing correctness",
453 "path": "apps/server/src/checkpointing/Errors.ts",
454 "line": 79,
455 "title": "Redundant error type in CheckpointServiceError union definition",
456 "thread_ids": [
457 "PRRT_kwDORLtfbc5wlYgs",
458 "PRRT_kwDORLtfbc5wxsO6",
459 "PRRT_kwDORLtfbc5w1C4B"
460 ]
461 },
462 {
463 "canonical_id": "C036",
464 "verdict": "invalid",
465 "rationale": "No longer duplicated; single production source via Refs.ts.",
466 "severity": "Low",
467 "area": "Checkpointing correctness",
468 "path": "apps/server/src/checkpointing/Layers/CheckpointStore.ts",
469 "line": 284,
470 "title": "Duplicate `checkpointRefForThreadTurn` function in two production files",
471 "thread_ids": [
472 "PRRT_kwDORLtfbc5wiqFX"
473 ]
474 },
475 {
476 "canonical_id": "C037",
477 "verdict": "partially-valid",
478 "rationale": "Callback cleanup missing, but practical exposure is low in one-shot startup path.",
479 "severity": "Low",
480 "area": "Other",
481 "path": "apps/server/src/config.ts",
482 "line": 41,
483 "title": "`Effect.callback` should return a cleanup function to close the server(s) on fiber interruption. Without it, the `Net.Server` handles keep the process alive and leak the port if the effect is cancelled. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
484 "thread_ids": [
485 "PRRT_kwDORLtfbc5wj4cO"
486 ]
487 },
488 {
489 "canonical_id": "C038",
490 "verdict": "valid",
491 "rationale": "Chunk-by-chunk UTF-8 decode can still corrupt split multibyte characters.",
492 "severity": "Low",
493 "area": "Edge-case parsing/platform behavior",
494 "path": "apps/server/src/git/Layers/CodexTextGeneration.ts",
495 "line": 136,
496 "title": "Multi-byte UTF-8 characters split across chunks will be corrupted when decoding each chunk separately. Consider accumulating all chunks first, then decoding once, or use `TextDecoder` with `stream: true`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
497 "thread_ids": [
498 "PRRT_kwDORLtfbc5w1GPo"
499 ]
500 },
501 {
502 "canonical_id": "C039",
503 "verdict": "partially-valid",
504 "rationale": "Parser/encoder mismatch remains, but encoder path currently low-use.",
505 "severity": "Low",
506 "area": "Edge-case parsing/platform behavior",
507 "path": "apps/server/src/keybindings.ts",
508 "line": 352,
509 "title": "The `+` key can be parsed (via trailing `+` handling) but cannot be encoded because `shortcut.key.includes(\"+\")` returns true for the literal `+` key. Consider checking `shortcut.key === \"+\"` separately and encoding it as `\"space\"` style (e.g., a special token), or adjusting the condition to allow the single `+` character. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
510 "thread_ids": [
511 "PRRT_kwDORLtfbc5wxvIB"
512 ]
513 },
514 {
515 "canonical_id": "C040",
516 "verdict": "valid",
517 "rationale": "upsertKeybindingRule read-modify-write remains race-prone.",
518 "severity": "Low",
519 "area": "Edge-case parsing/platform behavior",
520 "path": "apps/server/src/keybindings.ts",
521 "line": 488,
522 "title": "`upsertKeybindingRule` has a race condition: concurrent calls read the same file state, then the last write overwrites earlier changes. Consider wrapping the read-modify-write sequence with `Effect.Semaphore` to serialize access. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
523 "thread_ids": [
524 "PRRT_kwDORLtfbc5wxvIA"
525 ]
526 },
527 {
528 "canonical_id": "C041",
529 "verdict": "valid",
530 "rationale": "Same Symbol interpolation hazard as C011 remains.",
531 "severity": "Low",
532 "area": "Other",
533 "path": "apps/server/src/orchestration/Errors.ts",
534 "line": 126,
535 "title": "Template literal interpolation throws `TypeError` if `cause` is a `Symbol`. Consider using `String(cause)` for safe coercion.",
536 "thread_ids": [
537 "PRRT_kwDORLtfbc5wBFWs"
538 ]
539 },
540 {
541 "canonical_id": "C042",
542 "verdict": "partially-valid",
543 "rationale": "Duplication exists but one instance is variant logic, so impact is moderate.",
544 "severity": "Low",
545 "area": "Runtime resilience and failure handling",
546 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
547 "line": 62,
548 "title": "Duplicated `resolveThreadWorkspaceCwd` across three files",
549 "thread_ids": [
550 "PRRT_kwDORLtfbc5wzli2"
551 ]
552 },
553 {
554 "canonical_id": "C043",
555 "verdict": "valid",
556 "rationale": "Workspace CWD resolution duplication still present across modules.",
557 "severity": "Low",
558 "area": "Runtime resilience and failure handling",
559 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
560 "line": 62,
561 "title": "Duplicated workspace CWD resolution logic across reactor modules",
562 "thread_ids": [
563 "PRRT_kwDORLtfbc5wnWwM",
564 "PRRT_kwDORLtfbc5w1C3-",
565 "PRRT_kwDORLtfbc5w1HGZ"
566 ]
567 },
568 {
569 "canonical_id": "C044",
570 "verdict": "partially-valid",
571 "rationale": "Errors are swallowed to empty diff with warning; not fully silent but still lossy.",
572 "severity": "Low",
573 "area": "Runtime resilience and failure handling",
574 "path": "apps/server/src/orchestration/Layers/CheckpointReactor.ts",
575 "line": 274,
576 "title": "Checkpoint reactor swallows diff errors silently for `turn.completed`",
577 "thread_ids": [
578 "PRRT_kwDORLtfbc5wkPaO"
579 ]
580 },
581 {
582 "canonical_id": "C045",
583 "verdict": "valid",
584 "rationale": "truncateDetail still overshoots limit.",
585 "severity": "Low",
586 "area": "Runtime resilience and failure handling",
587 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
588 "line": 29,
589 "title": "`truncateDetail` slices to `limit - 1` then appends `\"...\"` (3 chars), producing strings of length `limit + 2`. Consider slicing to `limit - 3` instead. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
590 "thread_ids": [
591 "PRRT_kwDORLtfbc5wzp4R"
592 ]
593 },
594 {
595 "canonical_id": "C046",
596 "verdict": "valid",
597 "rationale": "latestMessageIdByTurnKey still unused/unpruned in per-turn clear path.",
598 "severity": "Low",
599 "area": "Runtime resilience and failure handling",
600 "path": "apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts",
601 "line": 133,
602 "title": "`latestMessageIdByTurnKey` is written to but never read, and `clearAssistantMessageIdsForTurn` doesn't clear its entries\u2014only `clearTurnStateForSession` does. Consider removing this map entirely if unused, or clearing it alongside `turnMessageIdsByTurnKey` in `clearAssistantMessageIdsForTurn`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
603 "thread_ids": [
604 "PRRT_kwDORLtfbc5wxvIQ"
605 ]
606 },
607 {
608 "canonical_id": "C047",
609 "verdict": "valid",
610 "rationale": "Decode and SQL errors still collapsed into one persistence error kind.",
611 "severity": "Low",
612 "area": "Other",
613 "path": "apps/server/src/persistence/Layers/OrchestrationCommandReceipts.ts",
614 "line": 75,
615 "title": "`SqlSchema.findOneOption` can produce both SQL errors and decode errors, but `mapError` wraps all as `PersistenceSqlError`. Consider distinguishing `ParseError` from SQL errors and mapping decode failures to `PersistenceDecodeError` instead. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
616 "thread_ids": [
617 "PRRT_kwDORLtfbc5wiaR-"
618 ]
619 },
620 {
621 "canonical_id": "C048",
622 "verdict": "partially-valid",
623 "rationale": "Unsafe run() result cast remains; runtime risk depends on raw-result consumers.",
624 "severity": "Low",
625 "area": "Other",
626 "path": "apps/server/src/persistence/NodeSqliteClient.ts",
627 "line": 99,
628 "title": "`statement.run()` returns a `RunResult` object, not an array. Casting it to `ReadonlyArray<any>` will cause runtime errors when consumers call array methods. Consider wrapping the result (e.g., `[result]`) or returning a properly typed object.",
629 "thread_ids": [
630 "PRRT_kwDORLtfbc5wio-r"
631 ]
632 },
633 {
634 "canonical_id": "C049",
635 "verdict": "valid",
636 "rationale": "JSON.stringify(cause) may return undefined despite string expectations.",
637 "severity": "Low",
638 "area": "Other",
639 "path": "apps/server/src/provider/Layers/ProviderService.ts",
640 "line": 59,
641 "title": "`JSON.stringify(cause)` returns `undefined` for `undefined`, functions, or symbols, violating the `string` return type. Consider coercing the result to a string (e.g., `String(JSON.stringify(cause))`) or adding a fallback.",
642 "thread_ids": [
643 "PRRT_kwDORLtfbc5wnVsI"
644 ]
645 },
646 {
647 "canonical_id": "C050",
648 "verdict": "valid",
649 "rationale": "ProviderSessionDirectory upsert remains read-merge-write without concurrency control.",
650 "severity": "Low",
651 "area": "Other",
652 "path": "apps/server/src/provider/Layers/ProviderSessionDirectory.ts",
653 "line": 94,
654 "title": "The read-modify-write pattern (`getBySessionId` \u2192 merge \u2192 `upsert`) is susceptible to lost updates under concurrent writes. Consider wrapping in a transaction or adding optimistic concurrency control (e.g., version field) if concurrent session updates are expected. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
655 "thread_ids": [
656 "PRRT_kwDORLtfbc5wiLhY"
657 ]
658 },
659 {
660 "canonical_id": "C051",
661 "verdict": "partially-valid",
662 "rationale": "Null-clearing issue is real for providerThreadId; adapterKey part overstated.",
663 "severity": "Low",
664 "area": "Other",
665 "path": "apps/server/src/provider/Layers/ProviderSessionDirectory.ts",
666 "line": 119,
667 "title": "Using `??` for `providerThreadId` and `adapterKey` makes it impossible to clear these fields by passing `null`, since `null ?? existing` evaluates to `existing`. Consider using explicit `undefined` checks (like `resumeCursor` does) if clearing should be supported. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
668 "thread_ids": [
669 "PRRT_kwDORLtfbc5wxvH9"
670 ]
671 },
672 {
673 "canonical_id": "C052",
674 "verdict": "valid",
675 "rationale": "Data callback may race before processHandle assignment.",
676 "severity": "Low",
677 "area": "Other",
678 "path": "apps/server/src/terminal/Layers/BunPTY.ts",
679 "line": 97,
680 "title": "Race condition: `processHandle` may be `null` when `data` callback fires, since it's assigned after `Bun.spawn` returns. Consider initializing `BunPtyProcess` first, then passing it to the callback to avoid losing initial output. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
681 "thread_ids": [
682 "PRRT_kwDORLtfbc5w1CxE"
683 ]
684 },
685 {
686 "canonical_id": "C053",
687 "verdict": "valid",
688 "rationale": "Still uses write+destroy rather than end() for rejection response.",
689 "severity": "Low",
690 "area": "WebSocket robustness",
691 "path": "apps/server/src/wsServer.ts",
692 "line": 83,
693 "title": "Consider using `socket.end(response)` instead of `socket.write(response)` + `socket.destroy()` to ensure the HTTP error response is fully flushed before closing the connection.",
694 "thread_ids": [
695 "PRRT_kwDORLtfbc5v-WPD"
696 ]
697 },
698 {
699 "canonical_id": "C054",
700 "verdict": "valid",
701 "rationale": "Array chunk UTF-8 decode remains vulnerable to split multibyte corruption.",
702 "severity": "Low",
703 "area": "WebSocket robustness",
704 "path": "apps/server/src/wsServer.ts",
705 "line": 104,
706 "title": "When array chunks contain a multi-byte UTF-8 character split across boundaries, decoding each chunk separately produces replacement characters. Consider using `Buffer.concat()` on all chunks before calling `.toString(\"utf8\")`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
707 "thread_ids": [
708 "PRRT_kwDORLtfbc5whtrR"
709 ]
710 },
711 {
712 "canonical_id": "C055",
713 "verdict": "invalid",
714 "rationale": "No longer duplicated; helper is centralized.",
715 "severity": "Low",
716 "area": "Checkpointing correctness",
717 "path": "apps/server/src/wsServer.ts",
718 "line": 128,
719 "title": "Duplicate `checkpointRefForThreadTurn` function across files",
720 "thread_ids": [
721 "PRRT_kwDORLtfbc5wkPaG"
722 ]
723 },
724 {
725 "canonical_id": "C056",
726 "verdict": "partially-valid",
727 "rationale": "Bug pattern exists, but current callsites mostly avoid triggering it.",
728 "severity": "Low",
729 "area": "Other",
730 "path": "apps/web/src/components/ui/sidebar.tsx",
731 "line": 114,
732 "title": "When `onOpenChange` is provided without `open`, the internal `_open` state never updates because `setOpenProp` takes precedence. Consider calling `_setOpen` when `openProp === undefined`, regardless of whether `setOpenProp` exists.",
733 "thread_ids": [
734 "PRRT_kwDORLtfbc5wxvIq"
735 ]
736 },
737 {
738 "canonical_id": "C057",
739 "verdict": "valid",
740 "rationale": "Resizable object recreation still retriggers effect/storage reads.",
741 "severity": "Low",
742 "area": "Other",
743 "path": "apps/web/src/routes/_chat.$threadId.tsx",
744 "line": 105,
745 "title": "The `resizable` object is recreated on every render, causing `SidebarRail`'s `useEffect` to repeatedly read localStorage and update the DOM. Consider memoizing the object with `useMemo`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
746 "thread_ids": [
747 "PRRT_kwDORLtfbc5wyWz4"
748 ]
749 },
750 {
751 "canonical_id": "C058",
752 "verdict": "valid",
753 "rationale": "Number(null) -> 0 path still forces min width on initial load.",
754 "severity": "Low",
755 "area": "Other",
756 "path": "apps/web/src/routes/_chat.$threadId.tsx",
757 "line": 122,
758 "title": "When `localStorage.getItem()` returns `null`, `Number(null)` evaluates to `0`, which passes `Number.isFinite(0)`. This causes the sidebar to clamp to `minWidth` on first load, overriding the `DIFF_INLINE_DEFAULT_WIDTH` CSS clamp. Consider checking for `null` or empty string before parsing, e.g. guard with `storedWidth === null || storedWidth === ''`. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
759 "thread_ids": [
760 "PRRT_kwDORLtfbc5wnVsX"
761 ]
762 },
763 {
764 "canonical_id": "C059",
765 "verdict": "partially-valid",
766 "rationale": "Transport _tag override risk exists but current callsites are constrained.",
767 "severity": "Low",
768 "area": "WebSocket robustness",
769 "path": "apps/web/src/wsTransport.ts",
770 "line": 59,
771 "title": "Suggestion: don\u2019t spread `params` into `body`; it can override `_tag` and mishandle non-object values. Keep `_tag` separate and nest `params` under a single key (e.g., `data`), or validate `params` is a plain object.",
772 "thread_ids": [
773 "PRRT_kwDORLtfbc5whtrN"
774 ]
775 },
776 {
777 "canonical_id": "C060",
778 "verdict": "valid",
779 "rationale": "Schema still cannot express null clear for defaultModel patch.",
780 "severity": "Low",
781 "area": "Other",
782 "path": "packages/contracts/src/orchestration.ts",
783 "line": 253,
784 "title": "`defaultModel` should be `Schema.optional(Schema.NullOr(Schema.String))` to allow clearing the value. Currently there's no way to reset it to `null` since omitting means \"no change\" in patch semantics. <details> <summary>\ud83d\ude80 Reply \"<strong>fix it for me</strong>\" or copy this <strong>AI Prompt</strong> for your agent:</summary>",
785 "thread_ids": [
786 "PRRT_kwDORLtfbc5whxJC"
787 ]
788 }
789 ]
790}
.plans/16b-pr89-architecture-validity-audit.md deleted-125
...@@ -1,125 +0,0 @@
1# PR #89 Post-Rewrite Validity Audit
2
3Architecture baseline used for this audit:
4- Provider runtime activity is projected into orchestration domain events.
5- Web client consumes `orchestration.domainEvent`; no separate `providers.event` channel is expected.
6
7## Summary
8
9- Canonical items reviewed: 58
10- Valid: 34
11- Partially valid: 19
12- Invalid (stale/no longer applicable): 5
13
14## Invalid Items To Close
15
16- C021 [Medium] Shared mutable default metadata object causes stale eventId (apps/server/src/orchestration/decider.ts:27)
17 - Reason: Stale-eventId claim no longer applies; eventId is regenerated per event.
18 - Threads: PRRT_kwDORLtfbc5wkPaA
19- C025 [Medium] Duplicated checkpoint ref computation across two files (apps/server/src/wsServer.ts:128)
20 - Reason: No longer duplicated; checkpoint ref helper now centralized.
21 - Threads: PRRT_kwDORLtfbc5wvwag
22- C031 [Medium] Revert uses wrong turn count from positional inference (apps/web/src/session-logic.ts:127)
23 - Reason: Revert now uses explicit checkpointTurnCount first; positional fallback is non-primary.
24 - Threads: PRRT_kwDORLtfbc5v9SCp
25- C036 [Low] Duplicate `checkpointRefForThreadTurn` function in two production files (apps/server/src/checkpointing/Layers/CheckpointStore.ts:284)
26 - Reason: No longer duplicated; single production source via Refs.ts.
27 - Threads: PRRT_kwDORLtfbc5wiqFX
28- C055 [Low] Duplicate `checkpointRefForThreadTurn` function across files (apps/server/src/wsServer.ts:128)
29 - Reason: No longer duplicated; helper is centralized.
30 - Threads: PRRT_kwDORLtfbc5wkPaG
31
32## Partially Valid Items (de-scope or lower priority)
33
34- C006 [Medium] Build script runs TypeScript file with bare node (apps/server/package.json:14)
35 - Note: Node 24 makes this work, but script still relies on runtime-specific TS execution.
36- C007 [Medium] Build script uses `node` to run `.ts` file directly (apps/server/package.json:14)
37 - Note: Same as C006 (duplicate concern).
38- C008 [Medium] Inconsistent input normalization across CheckpointStore methods (apps/server/src/checkpointing/Layers/CheckpointStore.ts:94)
39 - Note: Normalization inconsistency remains, but upstream callsites now mostly canonicalize inputs.
40- C014 [Medium] Engine error handler catches all errors including non-invariant ones (apps/server/src/orchestration/Layers/OrchestrationEngine.ts:144)
41 - Note: Broad catch may be intentional for liveness; correctness concern is contextual.
42- C015 [Medium] The gap-filling fallback logic can retain messages from turns that are about to be deleted, causing foreign key violations. Consider removing the fallback logic entirely, or filtering `fallbackUserMessages` and `fallbackAssistantMessages` to only include messages whose `turnId` is in `retainedTurnIds`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/orchestration/Layers/ProjectionPipeline.ts:99)
43 - Note: Message fallback retention issue is real, but prior FK-violation claim is overstated.
44- C017 [Medium] `REQUIRED_SNAPSHOT_PROJECTORS` includes `pending-approvals` and `thread-turns`, but `getSnapshot` doesn't query their data. If these projectors lag behind, the returned `snapshotSequence` will be lower than what the included data actually reflects, causing clients to replay already-applied events. Consider filtering `REQUIRED_SNAPSHOT_PROJECTORS` to only include projectors whose data is actually fetched in the snapshot. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts:71)
45 - Note: Snapshot sequence can under-report due to extra projectors, but replay impact is lower now.
46- C019 [Medium] ProviderRuntimeIngestion processes events for wrong thread on race (apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:178)
47 - Note: SessionId-only routing can misassociate events under races/rebinds.
48- C020 [Medium] On `message.completed`, the message ID is added to the set and `thread.message.assistant.complete` is dispatched. On `turn.completed`, the same set is iterated and `thread.message.assistant.complete` is dispatched again for each ID—including already-completed ones. Consider removing message IDs from the set after dispatching on `message.completed`, or filtering out already-completed IDs before the `turn.completed` loop. (apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:266)
49 - Note: Duplicate complete dispatch exists; downstream impact often idempotent.
50- C024 [Medium] In `executeUnprepared`, preparing outside an effect and not closing the statement can cause uncaught errors and leaks. Suggest wrapping with `Effect.acquireUseRelease`: prepare in acquire (error-safe) and `finalize` in release. (apps/server/src/persistence/NodeSqliteClient.ts:145)
51 - Note: prepare() still outside managed effect boundary; failure handling risk remains.
52- C028 [Medium] Branch sync dispatches both server and stale local update (apps/web/src/components/BranchToolbar.tsx:102)
53 - Note: Optimistic local+server dual update is intentional but can temporarily diverge.
54- C033 [Low] Three error classes defined but never instantiated anywhere (apps/server/src/checkpointing/Errors.ts:51)
55 - Note: Original claim overstated; some errors used, others appear unused.
56- C037 [Low] `Effect.callback` should return a cleanup function to close the server(s) on fiber interruption. Without it, the `Net.Server` handles keep the process alive and leak the port if the effect is cancelled. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/config.ts:41)
57 - Note: Callback cleanup missing, but practical exposure is low in one-shot startup path.
58- C039 [Low] The `+` key can be parsed (via trailing `+` handling) but cannot be encoded because `shortcut.key.includes("+")` returns true for the literal `+` key. Consider checking `shortcut.key === "+"` separately and encoding it as `"space"` style (e.g., a special token), or adjusting the condition to allow the single `+` character. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/keybindings.ts:352)
59 - Note: Parser/encoder mismatch remains, but encoder path currently low-use.
60- C042 [Low] Duplicated `resolveThreadWorkspaceCwd` across three files (apps/server/src/orchestration/Layers/CheckpointReactor.ts:62)
61 - Note: Duplication exists but one instance is variant logic, so impact is moderate.
62- C044 [Low] Checkpoint reactor swallows diff errors silently for `turn.completed` (apps/server/src/orchestration/Layers/CheckpointReactor.ts:274)
63 - Note: Errors are swallowed to empty diff with warning; not fully silent but still lossy.
64- C048 [Low] `statement.run()` returns a `RunResult` object, not an array. Casting it to `ReadonlyArray<any>` will cause runtime errors when consumers call array methods. Consider wrapping the result (e.g., `[result]`) or returning a properly typed object. (apps/server/src/persistence/NodeSqliteClient.ts:99)
65 - Note: Unsafe run() result cast remains; runtime risk depends on raw-result consumers.
66- C051 [Low] Using `??` for `providerThreadId` and `adapterKey` makes it impossible to clear these fields by passing `null`, since `null ?? existing` evaluates to `existing`. Consider using explicit `undefined` checks (like `resumeCursor` does) if clearing should be supported. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/provider/Layers/ProviderSessionDirectory.ts:119)
67 - Note: Null-clearing issue is real for providerThreadId; adapterKey part overstated.
68- C056 [Low] When `onOpenChange` is provided without `open`, the internal `_open` state never updates because `setOpenProp` takes precedence. Consider calling `_setOpen` when `openProp === undefined`, regardless of whether `setOpenProp` exists. (apps/web/src/components/ui/sidebar.tsx:114)
69 - Note: Bug pattern exists, but current callsites mostly avoid triggering it.
70- C059 [Low] Suggestion: don’t spread `params` into `body`; it can override `_tag` and mishandle non-object values. Keep `_tag` separate and nest `params` under a single key (e.g., `data`), or validate `params` is a plain object. (apps/web/src/wsTransport.ts:59)
71 - Note: Transport _tag override risk exists but current callsites are constrained.
72
73## Valid Items (actionable unless marked completed)
74
75- C001 [High] Non-atomic event appending can corrupt state on retry. If an error occurs mid-loop (lines 96-102) after some events are persisted but before the receipt is written, the command appears to fail. A retry generates new UUIDs via `crypto.randomUUID()` in the decider, appending duplicate events. Consider wrapping the loop in a transaction or using deterministic event IDs derived from `commandId`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/orchestration/Layers/OrchestrationEngine.ts:96)
76- C002 [High] A dispatch error in `processEvent` will terminate the `Effect.forever` loop, permanently halting event ingestion. Consider adding error recovery (e.g., `Effect.catchAll` with logging) around `processEvent` so failures don't kill the fiber. (apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:333)
77 - Status: Completed in Phase 1.
78- C003 [High] Consider attaching a no-op error listener before `socket.write` (e.g., `socket.on('error', () => {})`) to prevent an unhandled `EPIPE`/`ECONNRESET` from crashing the process if the client disconnects mid-handshake. (apps/server/src/wsServer.ts:75)
79- C009 [Medium] Git's braced rename syntax (e.g., `src/{old => new}/file.ts`) isn't handled correctly. The current slice after ` => ` produces invalid paths like `new}/file.ts`. Consider expanding the braces to construct the full destination path. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/git/Layers/GitCore.ts:41)
80- C010 [Medium] `loadCustomKeybindingsConfig` fails when the config file doesn't exist, which is expected for new users. Consider catching `ENOENT` and returning an empty array instead. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/keybindings.ts:418)
81- C011 [Medium] Template literal `${cause}` throws if `cause` is a `Symbol`. Consider using `String(cause)` instead. (apps/server/src/orchestration/Errors.ts:117)
82- C012 [Medium] Forked revert dispatch risks read model inconsistency (apps/server/src/orchestration/Layers/CheckpointReactor.ts:542)
83 - Status: Completed in Phase 1.
84- C013 [Medium] If `projectionPipeline.projectEvent` fails after `eventStore.append` succeeds, the event is persisted but `readModel` isn't updated, causing desync. Consider updating the in-memory `readModel` immediately after append (before the external projection), so local state stays consistent regardless of downstream failures. (apps/server/src/orchestration/Layers/OrchestrationEngine.ts:99)
85- C016 [Medium] The in-memory `pendingTurnStartByThreadId` map isn't restored during bootstrap. If the service restarts after processing `thread.turn-start-requested` but before `thread.session-set`, the `userMessageId` and `startedAt` will be lost since bootstrap resumes *after* the committed sequence. Consider persisting this pending state or processing these two events atomically. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/orchestration/Layers/ProjectionPipeline.ts:490)
86- C018 [Medium] Unbounded memory growth in turn start deduplication set (apps/server/src/orchestration/Layers/ProviderCommandReactor.ts:84)
87- C022 [Medium] Fish shell outputs `$PATH` as space-separated, not colon-separated. Consider checking if the shell is fish and using `string join : $PATH` instead, or validating the result contains colons before assigning. (apps/server/src/os-jank.ts:10)
88- C023 [Medium] Using `-il` flags causes the shell to source profile scripts that may print banners or other text, polluting the captured `PATH`. Consider using `-lc` (login only, non-interactive) to reduce unwanted output. (apps/server/src/os-jank.ts:10)
89- C026 [Medium] Consider adding `.catch(() => {})` after `Effect.runPromise(handleMessage(ws, raw))` to prevent unhandled rejections from crashing the server if `encodeResponse` or setup logic fails. (apps/server/src/wsServer.ts:545)
90 - Status: Completed in Phase 1.
91- C027 [Medium] WS message handler can cause unhandled promise rejection (apps/server/src/wsServer.ts:545)
92 - Status: Completed in Phase 1.
93- C029 [Medium] `parseFileUrlHref` already decodes the path (line 46), but `safeDecode` is called again here, corrupting filenames containing `%` sequences. Consider skipping the decode when `fileUrlTarget` is non-null. (apps/web/src/markdown-links.ts:105)
94- C030 [Medium] `EXTERNAL_SCHEME_PATTERN` matches `script.ts:10` as a scheme because `.ts:` looks like `scheme:`. Consider requiring `://` after the colon, or checking that what follows the colon is not just digits. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/web/src/markdown-links.ts:111)
95- C032 [Low] Dummy workflow marker accidentally committed to docs (AGENTS.md:45)
96- C034 [Low] Redundant `CheckpointInvariantError` in `CheckpointServiceError` union type (apps/server/src/checkpointing/Errors.ts:79)
97- C035 [Low] Redundant error type in CheckpointServiceError union definition (apps/server/src/checkpointing/Errors.ts:79)
98- C038 [Low] Multi-byte UTF-8 characters split across chunks will be corrupted when decoding each chunk separately. Consider accumulating all chunks first, then decoding once, or use `TextDecoder` with `stream: true`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/git/Layers/CodexTextGeneration.ts:136)
99- C040 [Low] `upsertKeybindingRule` has a race condition: concurrent calls read the same file state, then the last write overwrites earlier changes. Consider wrapping the read-modify-write sequence with `Effect.Semaphore` to serialize access. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/keybindings.ts:488)
100- C041 [Low] Template literal interpolation throws `TypeError` if `cause` is a `Symbol`. Consider using `String(cause)` for safe coercion. (apps/server/src/orchestration/Errors.ts:126)
101- C043 [Low] Duplicated workspace CWD resolution logic across reactor modules (apps/server/src/orchestration/Layers/CheckpointReactor.ts:62)
102- C045 [Low] `truncateDetail` slices to `limit - 1` then appends `"..."` (3 chars), producing strings of length `limit + 2`. Consider slicing to `limit - 3` instead. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:29)
103- C046 [Low] `latestMessageIdByTurnKey` is written to but never read, and `clearAssistantMessageIdsForTurn` doesn't clear its entries—only `clearTurnStateForSession` does. Consider removing this map entirely if unused, or clearing it alongside `turnMessageIdsByTurnKey` in `clearAssistantMessageIdsForTurn`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:133)
104- C047 [Low] `SqlSchema.findOneOption` can produce both SQL errors and decode errors, but `mapError` wraps all as `PersistenceSqlError`. Consider distinguishing `ParseError` from SQL errors and mapping decode failures to `PersistenceDecodeError` instead. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/persistence/Layers/OrchestrationCommandReceipts.ts:75)
105- C049 [Low] `JSON.stringify(cause)` returns `undefined` for `undefined`, functions, or symbols, violating the `string` return type. Consider coercing the result to a string (e.g., `String(JSON.stringify(cause))`) or adding a fallback. (apps/server/src/provider/Layers/ProviderService.ts:59)
106- C050 [Low] The read-modify-write pattern (`getBySessionId` → merge → `upsert`) is susceptible to lost updates under concurrent writes. Consider wrapping in a transaction or adding optimistic concurrency control (e.g., version field) if concurrent session updates are expected. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/provider/Layers/ProviderSessionDirectory.ts:94)
107- C052 [Low] Race condition: `processHandle` may be `null` when `data` callback fires, since it's assigned after `Bun.spawn` returns. Consider initializing `BunPtyProcess` first, then passing it to the callback to avoid losing initial output. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/terminal/Layers/BunPTY.ts:97)
108- C053 [Low] Consider using `socket.end(response)` instead of `socket.write(response)` + `socket.destroy()` to ensure the HTTP error response is fully flushed before closing the connection. (apps/server/src/wsServer.ts:83)
109- C054 [Low] When array chunks contain a multi-byte UTF-8 character split across boundaries, decoding each chunk separately produces replacement characters. Consider using `Buffer.concat()` on all chunks before calling `.toString("utf8")`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/server/src/wsServer.ts:104)
110- C057 [Low] The `resizable` object is recreated on every render, causing `SidebarRail`'s `useEffect` to repeatedly read localStorage and update the DOM. Consider memoizing the object with `useMemo`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/web/src/routes/_chat.$threadId.tsx:105)
111- C058 [Low] When `localStorage.getItem()` returns `null`, `Number(null)` evaluates to `0`, which passes `Number.isFinite(0)`. This causes the sidebar to clamp to `minWidth` on first load, overriding the `DIFF_INLINE_DEFAULT_WIDTH` CSS clamp. Consider checking for `null` or empty string before parsing, e.g. guard with `storedWidth === null || storedWidth === ''`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (apps/web/src/routes/_chat.$threadId.tsx:122)
112- C060 [Low] `defaultModel` should be `Schema.optional(Schema.NullOr(Schema.String))` to allow clearing the value. Currently there's no way to reset it to `null` since omitting means "no change" in patch semantics. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary> (packages/contracts/src/orchestration.ts:253)
113
114## By Area
115
116- Build/runtime portability: valid=0, partially-valid=2, invalid=0
117- Checkpointing correctness: valid=2, partially-valid=3, invalid=3
118- Edge-case parsing/platform behavior: valid=8, partially-valid=1, invalid=0
119- Event ordering and state consistency: valid=3, partially-valid=2, invalid=1
120- Memory/resource growth: valid=1, partially-valid=0, invalid=0
121- Other: valid=10, partially-valid=6, invalid=1
122- Runtime resilience and failure handling: valid=6, partially-valid=4, invalid=0
123- WebSocket robustness: valid=4, partially-valid=1, invalid=0
124
125Machine-readable mapping: `.plans/16b-pr89-architecture-validity-audit.json`.
.plans/16c-pr89-remediation-checklist.md created+542
...@@ -0,0 +1,542 @@
1# PR #89 Remediation Checklist (Consolidated)
2
3_Last updated: 2026-02-26_
4
5This is the working checklist for remediation execution.
6
7Status values:
8- `TODO`: Not started
9- `IN_PROGRESS`: Currently being worked
10- `BLOCKED`: Waiting on decision/dependency
11- `DONE`: Implemented and verified
12- `CLOSED_INVALID`: Stale/invalid review finding
13
14Counts: active `53` (`valid=34`, `partially-valid=19`), closed-invalid `5`
15
16## Active Checklist
17
18### Phase 1
19
20- [x] `C002` A dispatch error in `processEvent` will terminate the `Effect.forever` loop, permanently halting event ingestion. Consider adding error recovery (e.g., `Effect.catchAll` with logging) around `processEvent` so failures don't kill the fiber.
21 - Status: `DONE`
22 - Verdict: `valid`
23 - Severity: `High`
24 - Area: `Runtime resilience and failure handling`
25 - File: `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:333`
26 - Threads: PRRT_kwDORLtfbc5wj4cH, PRRT_kwDORLtfbc5wnWwF, PRRT_kwDORLtfbc5wyTaP, PRRT_kwDORLtfbc5wzliw, PRRT_kwDORLtfbc5w0_g3, PRRT_kwDORLtfbc5w1HGT (+5 duplicate thread(s))
27 - Audit note: Ingestion worker loop can terminate on unhandled processEvent failure.
28
29- [x] `C003` Consider attaching a no-op error listener before `socket.write` (e.g., `socket.on('error', () => {})`) to prevent an unhandled `EPIPE`/`ECONNRESET` from crashing the process if the client disconnects mid-handshake.
30 - Status: `DONE`
31 - Verdict: `valid`
32 - Severity: `High`
33 - Area: `WebSocket robustness`
34 - File: `apps/server/src/wsServer.ts:75`
35 - Threads: PRRT_kwDORLtfbc5v-cf4
36 - Audit note: Upgrade reject writes then destroys socket without defensive error listener.
37
38- [x] `C012` Forked revert dispatch risks read model inconsistency
39 - Status: `DONE`
40 - Verdict: `valid`
41 - Severity: `Medium`
42 - Area: `Runtime resilience and failure handling`
43 - File: `apps/server/src/orchestration/Layers/CheckpointReactor.ts:542`
44 - Threads: PRRT_kwDORLtfbc5whszW, PRRT_kwDORLtfbc5wyTaS, PRRT_kwDORLtfbc5wzli0, PRRT_kwDORLtfbc5w0_g4, PRRT_kwDORLtfbc5w1HGX (+4 duplicate thread(s))
45 - Audit note: Revert completion dispatch remains forked; state consistency window remains.
46
47- [ ] `C019` ProviderRuntimeIngestion processes events for wrong thread on race
48 - Status: `TODO`
49 - Verdict: `partially-valid`
50 - Severity: `Medium`
51 - Area: `Runtime resilience and failure handling`
52 - File: `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:178`
53 - Threads: PRRT_kwDORLtfbc5wkPaL
54 - Audit note: SessionId-only routing can misassociate events under races/rebinds.
55
56- [ ] `C020` On `message.completed`, the message ID is added to the set and `thread.message.assistant.complete` is dispatched. On `turn.completed`, the same set is iterated and `thread.message.assistant.complete` is dispatched again for each ID—including already-completed ones. Consider removing message IDs from the set after dispatching on `message.completed`, or filtering out already-completed IDs before the `turn.completed` loop.
57 - Status: `TODO`
58 - Verdict: `partially-valid`
59 - Severity: `Medium`
60 - Area: `Runtime resilience and failure handling`
61 - File: `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:266`
62 - Threads: PRRT_kwDORLtfbc5w1GPr
63 - Audit note: Duplicate complete dispatch exists; downstream impact often idempotent.
64
65- [x] `C026` Consider adding `.catch(() => {})` after `Effect.runPromise(handleMessage(ws, raw))` to prevent unhandled rejections from crashing the server if `encodeResponse` or setup logic fails.
66 - Status: `DONE`
67 - Verdict: `valid`
68 - Severity: `Medium`
69 - Area: `WebSocket robustness`
70 - File: `apps/server/src/wsServer.ts:545`
71 - Threads: PRRT_kwDORLtfbc5wj4cE
72 - Audit note: runPromise result still not caught; rejection can surface unhandled.
73
74- [x] `C027` WS message handler can cause unhandled promise rejection
75 - Status: `DONE`
76 - Verdict: `valid`
77 - Severity: `Medium`
78 - Area: `Runtime resilience and failure handling`
79 - File: `apps/server/src/wsServer.ts:545`
80 - Threads: PRRT_kwDORLtfbc5wyTaW, PRRT_kwDORLtfbc5wzli3 (+1 duplicate thread(s))
81 - Audit note: Same unhandled rejection path remains in WS message handler.
82
83- [ ] `C042` Duplicated `resolveThreadWorkspaceCwd` across three files
84 - Status: `TODO`
85 - Verdict: `partially-valid`
86 - Severity: `Low`
87 - Area: `Runtime resilience and failure handling`
88 - File: `apps/server/src/orchestration/Layers/CheckpointReactor.ts:62`
89 - Threads: PRRT_kwDORLtfbc5wzli2
90 - Audit note: Duplication exists but one instance is variant logic, so impact is moderate.
91
92- [x] `C043` Duplicated workspace CWD resolution logic across reactor modules
93 - Status: `DONE`
94 - Verdict: `valid`
95 - Severity: `Low`
96 - Area: `Runtime resilience and failure handling`
97 - File: `apps/server/src/orchestration/Layers/CheckpointReactor.ts:62`
98 - Threads: PRRT_kwDORLtfbc5wnWwM, PRRT_kwDORLtfbc5w1C3-, PRRT_kwDORLtfbc5w1HGZ (+2 duplicate thread(s))
99 - Audit note: Workspace CWD resolution duplication still present across modules.
100
101- [ ] `C044` Checkpoint reactor swallows diff errors silently for `turn.completed`
102 - Status: `TODO`
103 - Verdict: `partially-valid`
104 - Severity: `Low`
105 - Area: `Runtime resilience and failure handling`
106 - File: `apps/server/src/orchestration/Layers/CheckpointReactor.ts:274`
107 - Threads: PRRT_kwDORLtfbc5wkPaO
108 - Audit note: Errors are swallowed to empty diff with warning; not fully silent but still lossy.
109
110- [x] `C045` `truncateDetail` slices to `limit - 1` then appends `"..."` (3 chars), producing strings of length `limit + 2`. Consider slicing to `limit - 3` instead. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
111 - Status: `DONE`
112 - Verdict: `valid`
113 - Severity: `Low`
114 - Area: `Runtime resilience and failure handling`
115 - File: `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:29`
116 - Threads: PRRT_kwDORLtfbc5wzp4R
117 - Audit note: truncateDetail still overshoots limit.
118
119- [x] `C046` `latestMessageIdByTurnKey` is written to but never read, and `clearAssistantMessageIdsForTurn` doesn't clear its entries—only `clearTurnStateForSession` does. Consider removing this map entirely if unused, or clearing it alongside `turnMessageIdsByTurnKey` in `clearAssistantMessageIdsForTurn`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
120 - Status: `DONE`
121 - Verdict: `valid`
122 - Severity: `Low`
123 - Area: `Runtime resilience and failure handling`
124 - File: `apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts:133`
125 - Threads: PRRT_kwDORLtfbc5wxvIQ
126 - Audit note: latestMessageIdByTurnKey still unused/unpruned in per-turn clear path.
127
128- [x] `C053` Consider using `socket.end(response)` instead of `socket.write(response)` + `socket.destroy()` to ensure the HTTP error response is fully flushed before closing the connection.
129 - Status: `DONE`
130 - Verdict: `valid`
131 - Severity: `Low`
132 - Area: `WebSocket robustness`
133 - File: `apps/server/src/wsServer.ts:83`
134 - Threads: PRRT_kwDORLtfbc5v-WPD
135 - Audit note: Still uses write+destroy rather than end() for rejection response.
136
137- [ ] `C054` When array chunks contain a multi-byte UTF-8 character split across boundaries, decoding each chunk separately produces replacement characters. Consider using `Buffer.concat()` on all chunks before calling `.toString("utf8")`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
138 - Status: `TODO`
139 - Verdict: `valid`
140 - Severity: `Low`
141 - Area: `WebSocket robustness`
142 - File: `apps/server/src/wsServer.ts:104`
143 - Threads: PRRT_kwDORLtfbc5whtrR
144 - Audit note: Array chunk UTF-8 decode remains vulnerable to split multibyte corruption.
145
146- [x] `C059` Suggestion: don’t spread `params` into `body`; it can override `_tag` and mishandle non-object values. Keep `_tag` separate and nest `params` under a single key (e.g., `data`), or validate `params` is a plain object.
147 - Status: `DONE`
148 - Verdict: `partially-valid`
149 - Severity: `Low`
150 - Area: `WebSocket robustness`
151 - File: `apps/web/src/wsTransport.ts:59`
152 - Threads: PRRT_kwDORLtfbc5whtrN
153 - Audit note: Transport _tag override risk exists but current callsites are constrained.
154
155### Phase 2
156
157- [ ] `C001` Non-atomic event appending can corrupt state on retry. If an error occurs mid-loop (lines 96-102) after some events are persisted but before the receipt is written, the command appears to fail. A retry generates new UUIDs via `crypto.randomUUID()` in the decider, appending duplicate events. Consider wrapping the loop in a transaction or using deterministic event IDs derived from `commandId`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
158 - Status: `TODO`
159 - Verdict: `valid`
160 - Severity: `High`
161 - Area: `Event ordering and state consistency`
162 - File: `apps/server/src/orchestration/Layers/OrchestrationEngine.ts:96`
163 - Threads: PRRT_kwDORLtfbc5wzp4T
164 - Audit note: Append/project/receipt are non-atomic; retry can duplicate events.
165
166- [ ] `C013` If `projectionPipeline.projectEvent` fails after `eventStore.append` succeeds, the event is persisted but `readModel` isn't updated, causing desync. Consider updating the in-memory `readModel` immediately after append (before the external projection), so local state stays consistent regardless of downstream failures.
167 - Status: `TODO`
168 - Verdict: `valid`
169 - Severity: `Medium`
170 - Area: `Event ordering and state consistency`
171 - File: `apps/server/src/orchestration/Layers/OrchestrationEngine.ts:99`
172 - Threads: PRRT_kwDORLtfbc5whtrM
173 - Audit note: Persisted event can outpace in-memory projection on mid-flight failure.
174
175- [ ] `C014` Engine error handler catches all errors including non-invariant ones
176 - Status: `TODO`
177 - Verdict: `partially-valid`
178 - Severity: `Medium`
179 - Area: `Event ordering and state consistency`
180 - File: `apps/server/src/orchestration/Layers/OrchestrationEngine.ts:144`
181 - Threads: PRRT_kwDORLtfbc5wkPaJ
182 - Audit note: Broad catch may be intentional for liveness; correctness concern is contextual.
183
184- [ ] `C015` The gap-filling fallback logic can retain messages from turns that are about to be deleted, causing foreign key violations. Consider removing the fallback logic entirely, or filtering `fallbackUserMessages` and `fallbackAssistantMessages` to only include messages whose `turnId` is in `retainedTurnIds`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
185 - Status: `TODO`
186 - Verdict: `partially-valid`
187 - Severity: `Medium`
188 - Area: `Event ordering and state consistency`
189 - File: `apps/server/src/orchestration/Layers/ProjectionPipeline.ts:99`
190 - Threads: PRRT_kwDORLtfbc5whxJO
191 - Audit note: Message fallback retention issue is real, but prior FK-violation claim is overstated.
192
193- [ ] `C016` The in-memory `pendingTurnStartByThreadId` map isn't restored during bootstrap. If the service restarts after processing `thread.turn-start-requested` but before `thread.session-set`, the `userMessageId` and `startedAt` will be lost since bootstrap resumes *after* the committed sequence. Consider persisting this pending state or processing these two events atomically. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
194 - Status: `TODO`
195 - Verdict: `valid`
196 - Severity: `Medium`
197 - Area: `Event ordering and state consistency`
198 - File: `apps/server/src/orchestration/Layers/ProjectionPipeline.ts:490`
199 - Threads: PRRT_kwDORLtfbc5wxvH8
200 - Audit note: Pending turn-start map is in-memory only and not rebuilt on bootstrap.
201
202### Phase 3
203
204- [ ] `C008` Inconsistent input normalization across CheckpointStore methods
205 - Status: `TODO`
206 - Verdict: `partially-valid`
207 - Severity: `Medium`
208 - Area: `Checkpointing correctness`
209 - File: `apps/server/src/checkpointing/Layers/CheckpointStore.ts:94`
210 - Threads: PRRT_kwDORLtfbc5widJw, PRRT_kwDORLtfbc5wnWv_, PRRT_kwDORLtfbc5w0_g7, PRRT_kwDORLtfbc5w1C36 (+3 duplicate thread(s))
211 - Audit note: Normalization inconsistency remains, but upstream callsites now mostly canonicalize inputs.
212
213- [ ] `C017` `REQUIRED_SNAPSHOT_PROJECTORS` includes `pending-approvals` and `thread-turns`, but `getSnapshot` doesn't query their data. If these projectors lag behind, the returned `snapshotSequence` will be lower than what the included data actually reflects, causing clients to replay already-applied events. Consider filtering `REQUIRED_SNAPSHOT_PROJECTORS` to only include projectors whose data is actually fetched in the snapshot. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
214 - Status: `TODO`
215 - Verdict: `partially-valid`
216 - Severity: `Medium`
217 - Area: `Checkpointing correctness`
218 - File: `apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts:71`
219 - Threads: PRRT_kwDORLtfbc5wiLhQ
220 - Audit note: Snapshot sequence can under-report due to extra projectors, but replay impact is lower now.
221
222- [ ] `C033` Three error classes defined but never instantiated anywhere
223 - Status: `TODO`
224 - Verdict: `partially-valid`
225 - Severity: `Low`
226 - Area: `Checkpointing correctness`
227 - File: `apps/server/src/checkpointing/Errors.ts:51`
228 - Threads: PRRT_kwDORLtfbc5wlYgo
229 - Audit note: Original claim overstated; some errors used, others appear unused.
230
231- [ ] `C034` Redundant `CheckpointInvariantError` in `CheckpointServiceError` union type
232 - Status: `TODO`
233 - Verdict: `valid`
234 - Severity: `Low`
235 - Area: `Checkpointing correctness`
236 - File: `apps/server/src/checkpointing/Errors.ts:79`
237 - Threads: PRRT_kwDORLtfbc5wj5fn
238 - Audit note: CheckpointInvariantError remains redundantly included in service union.
239
240- [ ] `C035` Redundant error type in CheckpointServiceError union definition
241 - Status: `TODO`
242 - Verdict: `valid`
243 - Severity: `Low`
244 - Area: `Checkpointing correctness`
245 - File: `apps/server/src/checkpointing/Errors.ts:79`
246 - Threads: PRRT_kwDORLtfbc5wlYgs, PRRT_kwDORLtfbc5wxsO6, PRRT_kwDORLtfbc5w1C4B (+2 duplicate thread(s))
247 - Audit note: Same as C034.
248
249### Phase 4
250
251- [ ] `C018` Unbounded memory growth in turn start deduplication set
252 - Status: `TODO`
253 - Verdict: `valid`
254 - Severity: `Medium`
255 - Area: `Memory/resource growth`
256 - File: `apps/server/src/orchestration/Layers/ProviderCommandReactor.ts:84`
257 - Threads: PRRT_kwDORLtfbc5whszQ, PRRT_kwDORLtfbc5wl2A8, PRRT_kwDORLtfbc5wyTaT, PRRT_kwDORLtfbc5wzliz, PRRT_kwDORLtfbc5w0_g-, PRRT_kwDORLtfbc5w1HGW (+5 duplicate thread(s))
258 - Audit note: handledTurnStartKeys still grows without pruning.
259
260### Phase 5
261
262- [ ] `C009` Git's braced rename syntax (e.g., `src/{old => new}/file.ts`) isn't handled correctly. The current slice after ` => ` produces invalid paths like `new}/file.ts`. Consider expanding the braces to construct the full destination path. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
263 - Status: `TODO`
264 - Verdict: `valid`
265 - Severity: `Medium`
266 - Area: `Edge-case parsing/platform behavior`
267 - File: `apps/server/src/git/Layers/GitCore.ts:41`
268 - Threads: PRRT_kwDORLtfbc5w1CxT
269 - Audit note: Braced rename parsing still breaks paths like src/{old => new}/file.ts.
270
271- [ ] `C010` `loadCustomKeybindingsConfig` fails when the config file doesn't exist, which is expected for new users. Consider catching `ENOENT` and returning an empty array instead. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
272 - Status: `TODO`
273 - Verdict: `valid`
274 - Severity: `Medium`
275 - Area: `Edge-case parsing/platform behavior`
276 - File: `apps/server/src/keybindings.ts:418`
277 - Threads: PRRT_kwDORLtfbc5wxvIJ
278 - Audit note: ENOENT for missing keybindings config still not handled as empty/default.
279
280- [ ] `C022` Fish shell outputs `$PATH` as space-separated, not colon-separated. Consider checking if the shell is fish and using `string join : $PATH` instead, or validating the result contains colons before assigning.
281 - Status: `TODO`
282 - Verdict: `valid`
283 - Severity: `Medium`
284 - Area: `Edge-case parsing/platform behavior`
285 - File: `apps/server/src/os-jank.ts:10`
286 - Threads: PRRT_kwDORLtfbc5wkRZM
287 - Audit note: fish PATH formatting risk still exists in os-jank path recovery.
288
289- [ ] `C023` Using `-il` flags causes the shell to source profile scripts that may print banners or other text, polluting the captured `PATH`. Consider using `-lc` (login only, non-interactive) to reduce unwanted output.
290 - Status: `TODO`
291 - Verdict: `valid`
292 - Severity: `Medium`
293 - Area: `Edge-case parsing/platform behavior`
294 - File: `apps/server/src/os-jank.ts:10`
295 - Threads: PRRT_kwDORLtfbc5wj4cM
296 - Audit note: -ilc shell invocation can pollute captured PATH output.
297
298- [ ] `C029` `parseFileUrlHref` already decodes the path (line 46), but `safeDecode` is called again here, corrupting filenames containing `%` sequences. Consider skipping the decode when `fileUrlTarget` is non-null.
299 - Status: `TODO`
300 - Verdict: `valid`
301 - Severity: `Medium`
302 - Area: `Edge-case parsing/platform behavior`
303 - File: `apps/web/src/markdown-links.ts:105`
304 - Threads: PRRT_kwDORLtfbc5wnVsU
305 - Audit note: file URL decoding still double-decodes in one path.
306
307- [ ] `C030` `EXTERNAL_SCHEME_PATTERN` matches `script.ts:10` as a scheme because `.ts:` looks like `scheme:`. Consider requiring `://` after the colon, or checking that what follows the colon is not just digits. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
308 - Status: `TODO`
309 - Verdict: `valid`
310 - Severity: `Medium`
311 - Area: `Edge-case parsing/platform behavior`
312 - File: `apps/web/src/markdown-links.ts:111`
313 - Threads: PRRT_kwDORLtfbc5wnVsK
314 - Audit note: Scheme regex still misclassifies script.ts:10 as external scheme.
315
316- [ ] `C038` Multi-byte UTF-8 characters split across chunks will be corrupted when decoding each chunk separately. Consider accumulating all chunks first, then decoding once, or use `TextDecoder` with `stream: true`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
317 - Status: `TODO`
318 - Verdict: `valid`
319 - Severity: `Low`
320 - Area: `Edge-case parsing/platform behavior`
321 - File: `apps/server/src/git/Layers/CodexTextGeneration.ts:136`
322 - Threads: PRRT_kwDORLtfbc5w1GPo
323 - Audit note: Chunk-by-chunk UTF-8 decode can still corrupt split multibyte characters.
324
325- [ ] `C039` The `+` key can be parsed (via trailing `+` handling) but cannot be encoded because `shortcut.key.includes("+")` returns true for the literal `+` key. Consider checking `shortcut.key === "+"` separately and encoding it as `"space"` style (e.g., a special token), or adjusting the condition to allow the single `+` character. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
326 - Status: `TODO`
327 - Verdict: `partially-valid`
328 - Severity: `Low`
329 - Area: `Edge-case parsing/platform behavior`
330 - File: `apps/server/src/keybindings.ts:352`
331 - Threads: PRRT_kwDORLtfbc5wxvIB
332 - Audit note: Parser/encoder mismatch remains, but encoder path currently low-use.
333
334- [ ] `C040` `upsertKeybindingRule` has a race condition: concurrent calls read the same file state, then the last write overwrites earlier changes. Consider wrapping the read-modify-write sequence with `Effect.Semaphore` to serialize access. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
335 - Status: `TODO`
336 - Verdict: `valid`
337 - Severity: `Low`
338 - Area: `Edge-case parsing/platform behavior`
339 - File: `apps/server/src/keybindings.ts:488`
340 - Threads: PRRT_kwDORLtfbc5wxvIA
341 - Audit note: upsertKeybindingRule read-modify-write remains race-prone.
342
343### Phase 6
344
345- [ ] `C006` Build script runs TypeScript file with bare node
346 - Status: `TODO`
347 - Verdict: `partially-valid`
348 - Severity: `Medium`
349 - Area: `Build/runtime portability`
350 - File: `apps/server/package.json:14`
351 - Threads: PRRT_kwDORLtfbc5wxsO-
352 - Audit note: Node 24 makes this work, but script still relies on runtime-specific TS execution.
353
354- [ ] `C007` Build script uses `node` to run `.ts` file directly
355 - Status: `TODO`
356 - Verdict: `partially-valid`
357 - Severity: `Medium`
358 - Area: `Build/runtime portability`
359 - File: `apps/server/package.json:14`
360 - Threads: PRRT_kwDORLtfbc5wiLNS, PRRT_kwDORLtfbc5wj5fl (+1 duplicate thread(s))
361 - Audit note: Same as C006 (duplicate concern).
362
363- [ ] `C011` Template literal `${cause}` throws if `cause` is a `Symbol`. Consider using `String(cause)` instead.
364 - Status: `TODO`
365 - Verdict: `valid`
366 - Severity: `Medium`
367 - Area: `Other`
368 - File: `apps/server/src/orchestration/Errors.ts:117`
369 - Threads: PRRT_kwDORLtfbc5wBFv4
370 - Audit note: Template-literal interpolation of Symbol still throws.
371
372- [ ] `C024` In `executeUnprepared`, preparing outside an effect and not closing the statement can cause uncaught errors and leaks. Suggest wrapping with `Effect.acquireUseRelease`: prepare in acquire (error-safe) and `finalize` in release.
373 - Status: `TODO`
374 - Verdict: `partially-valid`
375 - Severity: `Medium`
376 - Area: `Other`
377 - File: `apps/server/src/persistence/NodeSqliteClient.ts:145`
378 - Threads: PRRT_kwDORLtfbc5whxX9
379 - Audit note: prepare() still outside managed effect boundary; failure handling risk remains.
380
381- [ ] `C028` Branch sync dispatches both server and stale local update
382 - Status: `TODO`
383 - Verdict: `partially-valid`
384 - Severity: `Medium`
385 - Area: `Other`
386 - File: `apps/web/src/components/BranchToolbar.tsx:102`
387 - Threads: PRRT_kwDORLtfbc5v-XCu
388 - Audit note: Optimistic local+server dual update is intentional but can temporarily diverge.
389
390- [ ] `C032` Dummy workflow marker accidentally committed to docs
391 - Status: `TODO`
392 - Verdict: `valid`
393 - Severity: `Low`
394 - Area: `Other`
395 - File: `AGENTS.md:45`
396 - Threads: PRRT_kwDORLtfbc5wlYgj, PRRT_kwDORLtfbc5w1C4H (+1 duplicate thread(s))
397 - Audit note: Dummy workflow marker comment is still present in docs.
398
399- [ ] `C037` `Effect.callback` should return a cleanup function to close the server(s) on fiber interruption. Without it, the `Net.Server` handles keep the process alive and leak the port if the effect is cancelled. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
400 - Status: `TODO`
401 - Verdict: `partially-valid`
402 - Severity: `Low`
403 - Area: `Other`
404 - File: `apps/server/src/config.ts:41`
405 - Threads: PRRT_kwDORLtfbc5wj4cO
406 - Audit note: Callback cleanup missing, but practical exposure is low in one-shot startup path.
407
408- [ ] `C041` Template literal interpolation throws `TypeError` if `cause` is a `Symbol`. Consider using `String(cause)` for safe coercion.
409 - Status: `TODO`
410 - Verdict: `valid`
411 - Severity: `Low`
412 - Area: `Other`
413 - File: `apps/server/src/orchestration/Errors.ts:126`
414 - Threads: PRRT_kwDORLtfbc5wBFWs
415 - Audit note: Same Symbol interpolation hazard as C011 remains.
416
417- [ ] `C047` `SqlSchema.findOneOption` can produce both SQL errors and decode errors, but `mapError` wraps all as `PersistenceSqlError`. Consider distinguishing `ParseError` from SQL errors and mapping decode failures to `PersistenceDecodeError` instead. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
418 - Status: `TODO`
419 - Verdict: `valid`
420 - Severity: `Low`
421 - Area: `Other`
422 - File: `apps/server/src/persistence/Layers/OrchestrationCommandReceipts.ts:75`
423 - Threads: PRRT_kwDORLtfbc5wiaR-
424 - Audit note: Decode and SQL errors still collapsed into one persistence error kind.
425
426- [ ] `C048` `statement.run()` returns a `RunResult` object, not an array. Casting it to `ReadonlyArray<any>` will cause runtime errors when consumers call array methods. Consider wrapping the result (e.g., `[result]`) or returning a properly typed object.
427 - Status: `TODO`
428 - Verdict: `partially-valid`
429 - Severity: `Low`
430 - Area: `Other`
431 - File: `apps/server/src/persistence/NodeSqliteClient.ts:99`
432 - Threads: PRRT_kwDORLtfbc5wio-r
433 - Audit note: Unsafe run() result cast remains; runtime risk depends on raw-result consumers.
434
435- [ ] `C049` `JSON.stringify(cause)` returns `undefined` for `undefined`, functions, or symbols, violating the `string` return type. Consider coercing the result to a string (e.g., `String(JSON.stringify(cause))`) or adding a fallback.
436 - Status: `TODO`
437 - Verdict: `valid`
438 - Severity: `Low`
439 - Area: `Other`
440 - File: `apps/server/src/provider/Layers/ProviderService.ts:59`
441 - Threads: PRRT_kwDORLtfbc5wnVsI
442 - Audit note: JSON.stringify(cause) may return undefined despite string expectations.
443
444- [ ] `C050` The read-modify-write pattern (`getBySessionId` → merge → `upsert`) is susceptible to lost updates under concurrent writes. Consider wrapping in a transaction or adding optimistic concurrency control (e.g., version field) if concurrent session updates are expected. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
445 - Status: `TODO`
446 - Verdict: `valid`
447 - Severity: `Low`
448 - Area: `Other`
449 - File: `apps/server/src/provider/Layers/ProviderSessionDirectory.ts:94`
450 - Threads: PRRT_kwDORLtfbc5wiLhY
451 - Audit note: ProviderSessionDirectory upsert remains read-merge-write without concurrency control.
452
453- [ ] `C051` Using `??` for `providerThreadId` and `adapterKey` makes it impossible to clear these fields by passing `null`, since `null ?? existing` evaluates to `existing`. Consider using explicit `undefined` checks (like `resumeCursor` does) if clearing should be supported. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
454 - Status: `TODO`
455 - Verdict: `partially-valid`
456 - Severity: `Low`
457 - Area: `Other`
458 - File: `apps/server/src/provider/Layers/ProviderSessionDirectory.ts:119`
459 - Threads: PRRT_kwDORLtfbc5wxvH9
460 - Audit note: Null-clearing issue is real for providerThreadId; adapterKey part overstated.
461
462- [ ] `C052` Race condition: `processHandle` may be `null` when `data` callback fires, since it's assigned after `Bun.spawn` returns. Consider initializing `BunPtyProcess` first, then passing it to the callback to avoid losing initial output. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
463 - Status: `TODO`
464 - Verdict: `valid`
465 - Severity: `Low`
466 - Area: `Other`
467 - File: `apps/server/src/terminal/Layers/BunPTY.ts:97`
468 - Threads: PRRT_kwDORLtfbc5w1CxE
469 - Audit note: Data callback may race before processHandle assignment.
470
471- [ ] `C056` When `onOpenChange` is provided without `open`, the internal `_open` state never updates because `setOpenProp` takes precedence. Consider calling `_setOpen` when `openProp === undefined`, regardless of whether `setOpenProp` exists.
472 - Status: `TODO`
473 - Verdict: `partially-valid`
474 - Severity: `Low`
475 - Area: `Other`
476 - File: `apps/web/src/components/ui/sidebar.tsx:114`
477 - Threads: PRRT_kwDORLtfbc5wxvIq
478 - Audit note: Bug pattern exists, but current callsites mostly avoid triggering it.
479
480- [ ] `C057` The `resizable` object is recreated on every render, causing `SidebarRail`'s `useEffect` to repeatedly read localStorage and update the DOM. Consider memoizing the object with `useMemo`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
481 - Status: `TODO`
482 - Verdict: `valid`
483 - Severity: `Low`
484 - Area: `Other`
485 - File: `apps/web/src/routes/_chat.$threadId.tsx:105`
486 - Threads: PRRT_kwDORLtfbc5wyWz4
487 - Audit note: Resizable object recreation still retriggers effect/storage reads.
488
489- [ ] `C058` When `localStorage.getItem()` returns `null`, `Number(null)` evaluates to `0`, which passes `Number.isFinite(0)`. This causes the sidebar to clamp to `minWidth` on first load, overriding the `DIFF_INLINE_DEFAULT_WIDTH` CSS clamp. Consider checking for `null` or empty string before parsing, e.g. guard with `storedWidth === null || storedWidth === ''`. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
490 - Status: `TODO`
491 - Verdict: `valid`
492 - Severity: `Low`
493 - Area: `Other`
494 - File: `apps/web/src/routes/_chat.$threadId.tsx:122`
495 - Threads: PRRT_kwDORLtfbc5wnVsX
496 - Audit note: Number(null) -> 0 path still forces min width on initial load.
497
498- [ ] `C060` `defaultModel` should be `Schema.optional(Schema.NullOr(Schema.String))` to allow clearing the value. Currently there's no way to reset it to `null` since omitting means "no change" in patch semantics. <details> <summary>🚀 Reply "<strong>fix it for me</strong>" or copy this <strong>AI Prompt</strong> for your agent:</summary>
499 - Status: `TODO`
500 - Verdict: `valid`
501 - Severity: `Low`
502 - Area: `Other`
503 - File: `packages/contracts/src/orchestration.ts:253`
504 - Threads: PRRT_kwDORLtfbc5whxJC
505 - Audit note: Schema still cannot express null clear for defaultModel patch.
506
507## Closed Invalid Items
508
509- [x] `C021` Shared mutable default metadata object causes stale eventId
510 - Status: `CLOSED_INVALID`
511 - Severity: `Medium`
512 - File: `apps/server/src/orchestration/decider.ts:27`
513 - Threads: PRRT_kwDORLtfbc5wkPaA
514 - Rationale: Stale-eventId claim no longer applies; eventId is regenerated per event.
515
516- [x] `C025` Duplicated checkpoint ref computation across two files
517 - Status: `CLOSED_INVALID`
518 - Severity: `Medium`
519 - File: `apps/server/src/wsServer.ts:128`
520 - Threads: PRRT_kwDORLtfbc5wvwag
521 - Rationale: No longer duplicated; checkpoint ref helper now centralized.
522
523- [x] `C031` Revert uses wrong turn count from positional inference
524 - Status: `CLOSED_INVALID`
525 - Severity: `Medium`
526 - File: `apps/web/src/session-logic.ts:127`
527 - Threads: PRRT_kwDORLtfbc5v9SCp
528 - Rationale: Revert now uses explicit checkpointTurnCount first; positional fallback is non-primary.
529
530- [x] `C036` Duplicate `checkpointRefForThreadTurn` function in two production files
531 - Status: `CLOSED_INVALID`
532 - Severity: `Low`
533 - File: `apps/server/src/checkpointing/Layers/CheckpointStore.ts:284`
534 - Threads: PRRT_kwDORLtfbc5wiqFX
535 - Rationale: No longer duplicated; single production source via Refs.ts.
536
537- [x] `C055` Duplicate `checkpointRefForThreadTurn` function across files
538 - Status: `CLOSED_INVALID`
539 - Severity: `Low`
540 - File: `apps/server/src/wsServer.ts:128`
541 - Threads: PRRT_kwDORLtfbc5wkPaG
542 - Rationale: No longer duplicated; helper is centralized.
apps/server/integration/orchestrationEngine.integration.test.ts+1-1
...@@ -22,7 +22,7 @@ import {...@@ -22,7 +22,7 @@ import {
22 makeOrchestrationIntegrationHarness,22 makeOrchestrationIntegrationHarness,
23 type OrchestrationIntegrationHarness,23 type OrchestrationIntegrationHarness,
24} from "./OrchestrationEngineHarness.integration.ts";24} from "./OrchestrationEngineHarness.integration.ts";
25import { checkpointRefForThreadTurn } from "../src/checkpointing/Refs.ts";25import { checkpointRefForThreadTurn } from "../src/checkpointing/Utils.ts";
2626
27const asMessageId = (value: string): MessageId => MessageId.makeUnsafe(value);27const asMessageId = (value: string): MessageId => MessageId.makeUnsafe(value);
28const asProjectId = (value: string): ProjectId => ProjectId.makeUnsafe(value);28const asProjectId = (value: string): ProjectId => ProjectId.makeUnsafe(value);
apps/server/src/checkpointing/Layers/CheckpointDiffQuery.test.ts+1-1
...@@ -9,7 +9,7 @@ import { Effect, Layer } from "effect";...@@ -9,7 +9,7 @@ import { Effect, Layer } from "effect";
9import { describe, expect, it } from "vitest";9import { describe, expect, it } from "vitest";
1010
11import { ProjectionSnapshotQuery } from "../../orchestration/Services/ProjectionSnapshotQuery.ts";11import { ProjectionSnapshotQuery } from "../../orchestration/Services/ProjectionSnapshotQuery.ts";
12import { checkpointRefForThreadTurn } from "../Refs.ts";12import { checkpointRefForThreadTurn } from "../Utils.ts";
13import { CheckpointDiffQueryLive } from "./CheckpointDiffQuery.ts";13import { CheckpointDiffQueryLive } from "./CheckpointDiffQuery.ts";
14import { CheckpointStore, type CheckpointStoreShape } from "../Services/CheckpointStore.ts";14import { CheckpointStore, type CheckpointStoreShape } from "../Services/CheckpointStore.ts";
15import { CheckpointDiffQuery } from "../Services/CheckpointDiffQuery.ts";15import { CheckpointDiffQuery } from "../Services/CheckpointDiffQuery.ts";
apps/server/src/checkpointing/Layers/CheckpointDiffQuery.ts+5-28
...@@ -9,35 +9,12 @@ import { Effect, Layer, Schema } from "effect";...@@ -9,35 +9,12 @@ import { Effect, Layer, Schema } from "effect";
99
10import { ProjectionSnapshotQuery } from "../../orchestration/Services/ProjectionSnapshotQuery.ts";10import { ProjectionSnapshotQuery } from "../../orchestration/Services/ProjectionSnapshotQuery.ts";
11import { CheckpointInvariantError, CheckpointUnavailableError } from "../Errors.ts";11import { CheckpointInvariantError, CheckpointUnavailableError } from "../Errors.ts";
12import { checkpointRefForThreadTurn } from "../Refs.ts";12import { checkpointRefForThreadTurn, resolveThreadWorkspaceCwd } from "../Utils.ts";
13import { CheckpointStore } from "../Services/CheckpointStore.ts";13import { CheckpointStore } from "../Services/CheckpointStore.ts";
14import { CheckpointDiffQuery, type CheckpointDiffQueryShape } from "../Services/CheckpointDiffQuery.ts";14import {
1515 CheckpointDiffQuery,
16function trimToNonEmpty(value: string | null | undefined): string | undefined {16 type CheckpointDiffQueryShape,
17 const normalized = value?.trim();17} from "../Services/CheckpointDiffQuery.ts";
18 return normalized && normalized.length > 0 ? normalized : undefined;
19}
20
21function resolveThreadWorkspaceCwd(input: {
22 readonly thread: {
23 readonly projectId: ProjectId;
24 readonly worktreePath: string | null;
25 };
26 readonly projects: ReadonlyArray<{
27 readonly id: ProjectId;
28 readonly workspaceRoot: string;
29 }>;
30}): string | undefined {
31 const worktreeCwd = trimToNonEmpty(input.thread.worktreePath);
32 if (worktreeCwd) {
33 return worktreeCwd;
34 }
35
36 const workspaceRoot = input.projects.find(
37 (project) => project.id === input.thread.projectId,
38 )?.workspaceRoot;
39 return trimToNonEmpty(workspaceRoot);
40}
4118
42const isTurnDiffResult = Schema.is(OrchestrationGetTurnDiffResult);19const isTurnDiffResult = Schema.is(OrchestrationGetTurnDiffResult);
4320
apps/server/src/checkpointing/Refs.ts deleted-10
...@@ -1,10 +0,0 @@
1import { Encoding } from "effect";
2import { CheckpointRef, type ThreadId } from "@t3tools/contracts";
3
4export const CHECKPOINT_REFS_PREFIX = "refs/t3/checkpoints";
5
6export function checkpointRefForThreadTurn(threadId: ThreadId, turnCount: number): CheckpointRef {
7 return CheckpointRef.makeUnsafe(
8 `${CHECKPOINT_REFS_PREFIX}/${Encoding.encodeBase64Url(threadId)}/turn/${turnCount}`,
9 );
10}
apps/server/src/checkpointing/Utils.ts created+36
...@@ -0,0 +1,36 @@
1import { Encoding } from "effect";
2import { CheckpointRef, ProjectId, type ThreadId } from "@t3tools/contracts";
3
4export const CHECKPOINT_REFS_PREFIX = "refs/t3/checkpoints";
5
6export function checkpointRefForThreadTurn(threadId: ThreadId, turnCount: number): CheckpointRef {
7 return CheckpointRef.makeUnsafe(
8 `${CHECKPOINT_REFS_PREFIX}/${Encoding.encodeBase64Url(threadId)}/turn/${turnCount}`,
9 );
10}
11
12function trimToNonEmpty(value: string | null | undefined): string | undefined {
13 const normalized = value?.trim();
14 return normalized && normalized.length > 0 ? normalized : undefined;
15}
16
17export function resolveThreadWorkspaceCwd(input: {
18 readonly thread: {
19 readonly projectId: ProjectId;
20 readonly worktreePath: string | null;
21 };
22 readonly projects: ReadonlyArray<{
23 readonly id: ProjectId;
24 readonly workspaceRoot: string;
25 }>;
26}): string | undefined {
27 const worktreeCwd = trimToNonEmpty(input.thread.worktreePath);
28 if (worktreeCwd) {
29 return worktreeCwd;
30 }
31
32 const workspaceRoot = input.projects.find(
33 (project) => project.id === input.thread.projectId,
34 )?.workspaceRoot;
35 return trimToNonEmpty(workspaceRoot);
36}
apps/server/src/orchestration/Layers/CheckpointReactor.test.ts+25-16
...@@ -35,7 +35,7 @@ import {...@@ -35,7 +35,7 @@ import {
35 ProviderService,35 ProviderService,
36 type ProviderServiceShape,36 type ProviderServiceShape,
37} from "../../provider/Services/ProviderService.ts";37} from "../../provider/Services/ProviderService.ts";
38import { checkpointRefForThreadTurn } from "../../checkpointing/Refs.ts";38import { checkpointRefForThreadTurn } from "../../checkpointing/Utils.ts";
3939
40const asProjectId = (value: string): ProjectId => ProjectId.makeUnsafe(value);40const asProjectId = (value: string): ProjectId => ProjectId.makeUnsafe(value);
41const asSessionId = (value: string): ProviderSessionId => ProviderSessionId.makeUnsafe(value);41const asSessionId = (value: string): ProviderSessionId => ProviderSessionId.makeUnsafe(value);
...@@ -334,7 +334,10 @@ describe("CheckpointReactor", () => {...@@ -334,7 +334,10 @@ describe("CheckpointReactor", () => {
334 threadId: ProviderThreadId.makeUnsafe("provider-thread-1"),334 threadId: ProviderThreadId.makeUnsafe("provider-thread-1"),
335 turnId: asTurnId("turn-1"),335 turnId: asTurnId("turn-1"),
336 });336 });
337 await waitForGitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0));337 await waitForGitRefExists(
338 harness.cwd,
339 checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0),
340 );
338341
339 fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8");342 fs.writeFileSync(path.join(harness.cwd, "README.md"), "v2\n", "utf8");
340 harness.provider.emit({343 harness.provider.emit({
...@@ -354,12 +357,12 @@ describe("CheckpointReactor", () => {...@@ -354,12 +357,12 @@ describe("CheckpointReactor", () => {
354 (entry) => entry.latestTurnId === "turn-1" && entry.checkpoints.length === 1,357 (entry) => entry.latestTurnId === "turn-1" && entry.checkpoints.length === 1,
355 );358 );
356 expect(thread.checkpoints[0]?.checkpointTurnCount).toBe(1);359 expect(thread.checkpoints[0]?.checkpointTurnCount).toBe(1);
357 expect(gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0))).toBe(360 expect(
358 true,361 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0)),
359 );362 ).toBe(true);
360 expect(gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 1))).toBe(363 expect(
361 true,364 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 1)),
362 );365 ).toBe(true);
363 expect(366 expect(
364 gitShowFileAtRef(367 gitShowFileAtRef(
365 harness.cwd,368 harness.cwd,
...@@ -398,7 +401,10 @@ describe("CheckpointReactor", () => {...@@ -398,7 +401,10 @@ describe("CheckpointReactor", () => {
398 }),401 }),
399 );402 );
400403
401 await waitForGitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0));404 await waitForGitRefExists(
405 harness.cwd,
406 checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0),
407 );
402 expect(408 expect(
403 gitShowFileAtRef(409 gitShowFileAtRef(
404 harness.cwd,410 harness.cwd,
...@@ -448,9 +454,9 @@ describe("CheckpointReactor", () => {...@@ -448,9 +454,9 @@ describe("CheckpointReactor", () => {
448 });454 });
449455
450 await waitForEvent(harness.engine, (event) => event.type === "thread.turn-diff-completed");456 await waitForEvent(harness.engine, (event) => event.type === "thread.turn-diff-completed");
451 expect(gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 1))).toBe(457 expect(
452 true,458 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 1)),
453 );459 ).toBe(true);
454 expect(460 expect(
455 gitShowFileAtRef(461 gitShowFileAtRef(
456 harness.cwd,462 harness.cwd,
...@@ -556,7 +562,10 @@ describe("CheckpointReactor", () => {...@@ -556,7 +562,10 @@ describe("CheckpointReactor", () => {
556 turnId: asTurnId("turn-after-runtime-failure"),562 turnId: asTurnId("turn-after-runtime-failure"),
557 });563 });
558564
559 await waitForGitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0));565 await waitForGitRefExists(
566 harness.cwd,
567 checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0),
568 );
560 expect(569 expect(
561 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0)),570 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 0)),
562 ).toBe(true);571 ).toBe(true);
...@@ -636,9 +645,9 @@ describe("CheckpointReactor", () => {...@@ -636,9 +645,9 @@ describe("CheckpointReactor", () => {
636 numTurns: 1,645 numTurns: 1,
637 });646 });
638 expect(fs.readFileSync(path.join(harness.cwd, "README.md"), "utf8")).toBe("v2\n");647 expect(fs.readFileSync(path.join(harness.cwd, "README.md"), "utf8")).toBe("v2\n");
639 expect(gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 2))).toBe(648 expect(
640 false,649 gitRefExists(harness.cwd, checkpointRefForThreadTurn(ThreadId.makeUnsafe("thread-1"), 2)),
641 );650 ).toBe(false);
642 });651 });
643652
644 it("processes consecutive revert requests with deterministic rollback sequencing", async () => {653 it("processes consecutive revert requests with deterministic rollback sequencing", async () => {
apps/server/src/orchestration/Layers/CheckpointReactor.ts+4-23
...@@ -5,14 +5,16 @@ import {...@@ -5,14 +5,16 @@ import {
5 ProviderSessionId,5 ProviderSessionId,
6 ThreadId,6 ThreadId,
7 TurnId,7 TurnId,
8 type ProjectId,
9 type OrchestrationEvent,8 type OrchestrationEvent,
10 type ProviderRuntimeEvent,9 type ProviderRuntimeEvent,
11} from "@t3tools/contracts";10} from "@t3tools/contracts";
12import { Cause, Effect, Layer, Option, Queue, Stream } from "effect";11import { Cause, Effect, Layer, Option, Queue, Stream } from "effect";
1312
14import { parseTurnDiffFilesFromUnifiedDiff } from "../../checkpointing/Diffs.ts";13import { parseTurnDiffFilesFromUnifiedDiff } from "../../checkpointing/Diffs.ts";
15import { checkpointRefForThreadTurn } from "../../checkpointing/Refs.ts";14import {
15 checkpointRefForThreadTurn,
16 resolveThreadWorkspaceCwd,
17} from "../../checkpointing/Utils.ts";
16import { CheckpointStore } from "../../checkpointing/Services/CheckpointStore.ts";18import { CheckpointStore } from "../../checkpointing/Services/CheckpointStore.ts";
17import { ProviderService } from "../../provider/Services/ProviderService.ts";19import { ProviderService } from "../../provider/Services/ProviderService.ts";
18import { CheckpointReactor, type CheckpointReactorShape } from "../Services/CheckpointReactor.ts";20import { CheckpointReactor, type CheckpointReactorShape } from "../Services/CheckpointReactor.ts";
...@@ -40,27 +42,6 @@ function trimToNonEmpty(value: string | null | undefined): string | undefined {...@@ -40,27 +42,6 @@ function trimToNonEmpty(value: string | null | undefined): string | undefined {
40 return normalized && normalized.length > 0 ? normalized : undefined;42 return normalized && normalized.length > 0 ? normalized : undefined;
41}43}
4244
43function resolveThreadWorkspaceCwd(input: {
44 readonly thread: {
45 readonly projectId: ProjectId;
46 readonly worktreePath: string | null;
47 };
48 readonly projects: ReadonlyArray<{
49 readonly id: ProjectId;
50 readonly workspaceRoot: string;
51 }>;
52}): string | undefined {
53 const worktreeCwd = trimToNonEmpty(input.thread.worktreePath);
54 if (worktreeCwd) {
55 return worktreeCwd;
56 }
57
58 const workspaceRoot = input.projects.find(
59 (project) => project.id === input.thread.projectId,
60 )?.workspaceRoot;
61 return trimToNonEmpty(workspaceRoot);
62}
63
64function checkpointStatusFromRuntime(status: string | undefined): "ready" | "missing" | "error" {45function checkpointStatusFromRuntime(status: string | undefined): "ready" | "missing" | "error" {
65 switch (status) {46 switch (status) {
66 case "failed":47 case "failed":
apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts+1-8
...@@ -26,7 +26,7 @@ function toTurnId(value: string | undefined): TurnId | undefined {...@@ -26,7 +26,7 @@ function toTurnId(value: string | undefined): TurnId | undefined {
26}26}
2727
28function truncateDetail(value: string, limit = 180): string {28function truncateDetail(value: string, limit = 180): string {
29 return value.length > limit ? `${value.slice(0, limit - 1)}...` : value;29 return value.length > limit ? `${value.slice(0, limit - 3)}...` : value;
30}30}
3131
32function runtimeEventToActivities(32function runtimeEventToActivities(
...@@ -130,7 +130,6 @@ const make = Effect.gen(function* () {...@@ -130,7 +130,6 @@ const make = Effect.gen(function* () {
130 const providerService = yield* ProviderService;130 const providerService = yield* ProviderService;
131131
132 const turnMessageIdsByTurnKey = new Map<string, Set<MessageId>>();132 const turnMessageIdsByTurnKey = new Map<string, Set<MessageId>>();
133 const latestMessageIdByTurnKey = new Map<string, MessageId>();
134133
135 const rememberAssistantMessageId = (134 const rememberAssistantMessageId = (
136 sessionId: ProviderSessionId,135 sessionId: ProviderSessionId,
...@@ -144,7 +143,6 @@ const make = Effect.gen(function* () {...@@ -144,7 +143,6 @@ const make = Effect.gen(function* () {
144 } else {143 } else {
145 turnMessageIdsByTurnKey.set(key, new Set([messageId]));144 turnMessageIdsByTurnKey.set(key, new Set([messageId]));
146 }145 }
147 latestMessageIdByTurnKey.set(key, messageId);
148 };146 };
149147
150 const getAssistantMessageIdsForTurn = (sessionId: ProviderSessionId, turnId: TurnId) => {148 const getAssistantMessageIdsForTurn = (sessionId: ProviderSessionId, turnId: TurnId) => {
...@@ -162,11 +160,6 @@ const make = Effect.gen(function* () {...@@ -162,11 +160,6 @@ const make = Effect.gen(function* () {
162 turnMessageIdsByTurnKey.delete(key);160 turnMessageIdsByTurnKey.delete(key);
163 }161 }
164 }162 }
165 for (const key of latestMessageIdByTurnKey.keys()) {
166 if (key.startsWith(prefix)) {
167 latestMessageIdByTurnKey.delete(key);
168 }
169 }
170 };163 };
171164
172 const processEvent = (event: ProviderRuntimeEvent) =>165 const processEvent = (event: ProviderRuntimeEvent) =>
apps/server/src/wsServer.ts+3-2
...@@ -72,7 +72,7 @@ const isServerNotRunningError = (error: unknown): boolean => {...@@ -72,7 +72,7 @@ const isServerNotRunningError = (error: unknown): boolean => {
72};72};
7373
74function rejectUpgrade(socket: Duplex, statusCode: number, message: string): void {74function rejectUpgrade(socket: Duplex, statusCode: number, message: string): void {
75 socket.write(75 socket.end(
76 `HTTP/1.1 ${statusCode} ${statusCode === 401 ? "Unauthorized" : "Bad Request"}\r\n` +76 `HTTP/1.1 ${statusCode} ${statusCode === 401 ? "Unauthorized" : "Bad Request"}\r\n` +
77 "Connection: close\r\n" +77 "Connection: close\r\n" +
78 "Content-Type: text/plain\r\n" +78 "Content-Type: text/plain\r\n" +
...@@ -80,7 +80,6 @@ function rejectUpgrade(socket: Duplex, statusCode: number, message: string): voi...@@ -80,7 +80,6 @@ function rejectUpgrade(socket: Duplex, statusCode: number, message: string): voi
80 "\r\n" +80 "\r\n" +
81 message,81 message,
82 );82 );
83 socket.destroy();
84}83}
8584
86function parseBooleanEnv(value: string | undefined): boolean | undefined {85function parseBooleanEnv(value: string | undefined): boolean | undefined {
...@@ -506,6 +505,8 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E...@@ -506,6 +505,8 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E
506 });505 });
507506
508 httpServer.on("upgrade", (request, socket, head) => {507 httpServer.on("upgrade", (request, socket, head) => {
508 socket.on("error", () => {}); // Prevent unhandled `EPIPE`/`ECONNRESET` from crashing the process if the client disconnects mid-handshake
509
509 if (authToken) {510 if (authToken) {
510 let providedToken: string | null = null;511 let providedToken: string | null = null;
511 try {512 try {
apps/web/src/wsTransport.ts+1-4
...@@ -53,10 +53,7 @@ export class WsTransport {...@@ -53,10 +53,7 @@ export class WsTransport {
53 throw new Error("Request method is required");53 throw new Error("Request method is required");
54 }54 }
55 const id = String(this.nextId++);55 const id = String(this.nextId++);
56 const body =56 const body = params != null ? { ...params, _tag: method } : { _tag: method };
57 params !== undefined
58 ? { _tag: method, ...(params as Record<string, unknown>) }
59 : { _tag: method };
60 const message: WsRequestEnvelope = { id, body };57 const message: WsRequestEnvelope = { id, body };
6158
62 return new Promise<T>((resolve, reject) => {59 return new Promise<T>((resolve, reject) => {