[GPT-6.1-Sol][Codex] on behalf of shivam.
R15 and R16 runtime verification for head 27c2e13d491a3c0ad19e18f8f41fc2067771b88d, base 15fe2832727893cbdd2214383f9f971474849695. Both probes ran the actual unmodified source server on physical corex with Node v26.12.1, isolated real SQLite databases, synthetic git workspaces and a free gated ACP replay. No paid or real provider session was used.
R15, real disk full: passed. An unprivileged private user/mount namespace mounted only the disposable test storage as a 16 MiB tmpfs. After the paragraph prefix persisted and before idle delivery, the verifier checkpointed and truncated the WAL, then filled that tmpfs until allocation returned ENOSPC and zero blocks remained. An independent filesystem write failed, and the real server logged two idle-write failures ending in SQLite's database or disk is full. Removing the test filler restored space; an independent write succeeded. Events 29 and 30 and both projections then contained the full text with streaming: true while the same run remained running. The provider had emitted no new assistant text and was gated from finishing until those assertions passed. It then completed normally.
Disk-full milestones, UTC: zero free blocks at 19:10:26.553; SQLite failure observed at 19:10:27.343; space restored at 19:10:27.346; both streaming representations persisted at 19:10:29.404; run completed at 19:10:29.639. The verifier exited 0. The host filesystem was never filled.
R16, SIGKILL with an idle paragraph pending: observed as expected. At 19:11:37.994 UTC, the verifier captured the stored prefix and running run, then sent SIGKILL to its own server PID. After process exit and before restart, the full tail was still absent. A second source server used the same home and database. Startup recovery terminalized one run and stopped one session. At 19:11:43.766, both the real API and database showed the original run cancelled, the stored message still exactly First paragraph.\n\n with streaming: false, and the assistant turn item cancelled with its existing streaming: true flag. The verifier exited 0.
The unpersisted tail was lost on SIGKILL. continueThreadsAfterServerUpdate: false was explicit for this crash probe so provider replay could not stand in for recovery of the original held snapshot. This proves reconciliation at that crash cutpoint, not crash durability or default automatic continuation. This PR adds no durable buffer.
Commands, with temporary paths represented by <isolated-task>:
| 1 | unshare --user --map-root-user --mount sh -c 'mount -t tmpfs -o size=16m tmpfs <isolated-task>/disk-full/storage && exec /home/shivam/.local/share/vite-plus/js_runtime/node/26.12.1/bin/node <isolated-task>/disk-full/disk-full.ts' |
| 2 | /home/shivam/.local/share/vite-plus/js_runtime/node/26.12.1/bin/node <isolated-task>/crash/crash-pending.ts |
A fresh independent GPT-6.1-Sol review read the raw logs, database snapshots and complete verifiers and resolved R15/R16 with no new findings or required source changes. Both servers and verifier processes are stopped. No source changes or new build were needed.
Earlier R13/R14 evidence also passed: a targeted SQLite trigger failure permitted an unrelated provider event to commit before restoration, and a separate BEGIN IMMEDIATE experiment blocked all database writers until ROLLBACK, after which both held representations retried while the run remained open. The first R14 observer attempt hit its own WebSocket heartbeat timeout and was excluded; the successful attempt lengthened only that observer timeout.
Real Antigravity behavior, native desktop/iOS rendering, power loss and default automatic continuation after a crash are outside these probes. Android Replay recordings remain embedded in the PR body.
R15 disk-full log, milestones and database assertions
| 1 | Milestones |
| 2 | {"kind":"server-started","at":"2026-10-31T19:10:25.948Z","head":"27c2e13d491a3c0ad19e18f8f41fc2067771b88d","node":"v26.12.1","pid":2937904,"machine":"physical corex","sqlite":"file-backed statev2.sqlite","provider":"free synthetic ACP replay","sourceDirty":false} |
| 3 | {"kind":"private-tmpfs-filled-while-full-text-held","at":"2026-10-31T19:10:26.553Z","filesystem":"tmpfs","capacityBytes":16777216,"freeBlocks":0,"independentWriteError":"ENOSPC","walCheckpointTruncated":true,"heldFullTextAbsent":true,"runStatus":"running"} |
| 4 | {"kind":"idle-write-failed-with-real-disk-full","at":"2026-10-31T19:10:27.343Z","failureCount":1,"independentFilesystemWriteAlsoFailsENOSPC":true,"fullTextEvents":0,"fullTextMessageProjections":0,"fullTextItemProjections":0,"runStatus":"running"} |
| 5 | {"kind":"disk-space-restored","at":"2026-10-31T19:10:27.346Z","freeBlocks":3538} |
| 6 | {"kind":"independent-filesystem-write-succeeds-after-restoration","at":"2026-10-31T19:10:27.346Z"} |
| 7 | {"kind":"held-text-retried-without-new-text-or-prompt-completion","at":"2026-10-31T19:10:29.404Z","events":[{"sequence":29,"eventType":"message.updated","streaming":true},{"sequence":30,"eventType":"turn-item.updated","streaming":true}],"messageProjection":"First paragraph.\n\nHeld text survives a real disk-full failure.","turnItemProjection":"First paragraph.\n\nHeld text survives a real disk-full failure.","runStatus":"running","providerPromptStillOpen":true} |
| 8 | {"kind":"provider-completed-after-proof","at":"2026-10-31T19:10:29.639Z","runStatus":"completed","failureCount":2} |
| 9 | |
| 10 | First of two actual SQLite failure chains |
| 11 | |
| 12 | [00:40:27.341] WARN (#8827): orchestration V2 idle assistant text flush failed |
| 13 | { |
| 14 | runId: 'run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1', |
| 15 | failures: 1, |
| 16 | cause: 'ProviderEventPublishError: Failed to publish 1 normalized provider event(s).\n' + |
| 17 | ' at mapWriteError (file://<checkout>/apps/server/src/orchestration-v2/ProviderEventIngestor.ts:765:15)\n' + |
| 18 | ' at file://<checkout>/node_modules/.pnpm/effect@4.0.5/node_modules/effect/dist/internal/effect.js:1483:96\n' + |
| 19 | ' at RunExecutionService.startRootRun (file://<checkout>/apps/server/src/orchestration-v2/ProviderTurnStartService.ts:1634:27)\n' + |
| 20 | ' at RunExecutionService.startRootRun (definition) (file://<checkout>/apps/server/src/orchestration-v2/RunExecutionService.ts:878:28)\n' + |
| 21 | ' at ProviderTurnStartService.start (file://<checkout>/apps/server/src/orchestration-v2/EffectWorker.ts:143:16)\n' + |
| 22 | ' at ProviderTurnStartService.start (definition) (file://<checkout>/apps/server/src/orchestration-v2/ProviderTurnStartService.ts:1687:21)\n' + |
| 23 | ' at OrchestrationEffectExecutorV2.execute (file://<checkout>/apps/server/src/orchestration-v2/EffectWorker.ts:785:14)\n' + |
| 24 | ' at OrchestrationEffectExecutorV2.execute (definition) (file://<checkout>/apps/server/src/orchestration-v2/EffectWorker.ts:98:23)\n' + |
| 25 | ' at serverActivation.forkParkedFiber (file://<checkout>/apps/server/src/serverRuntimeStartup.ts:394:30)\n' + |
| 26 | ' at serverActivation.forkParkedFiber (definition) (file://<checkout>/apps/server/src/serverActivation.ts:12:39)\n' + |
| 27 | ' at ServerRuntimeStartup.startEffectWorkerWithRelay (file://<checkout>/apps/server/src/serverRuntimeStartup.ts:551:9)\n' + |
| 28 | ' at ServerRuntimeStartup.startEffectWorkerWithRelay (definition) (file://<checkout>/apps/server/src/serverRuntimeStartup.ts:387:50) {\n' + |
| 29 | ' [cause]: EventSinkWriteError: Failed to write 1 orchestration V2 event(s).\n' + |
| 30 | ' at file://<checkout>/apps/server/src/orchestration-v2/EventSink.ts:885:15\n' + |
| 31 | ' at file://<checkout>/node_modules/.pnpm/effect@4.0.5/node_modules/effect/dist/internal/effect.js:1483:96 {\n' + |
| 32 | ' [cause]: effect/sql/SqlError: Failed to execute statement\n' + |
| 33 | ' at execute (file://<checkout>/packages/shared/src/nodeSqliteClient.ts:1013:15) {\n' + |
| 34 | ' [cause]: effect/sql/SqlError/UnknownError: Failed to execute statement\n' + |
| 35 | ' at classifySqliteError (file://<checkout>/node_modules/.pnpm/effect@4.0.5/node_modules/effect/dist/sql/SqlError.js:486:16)\n' + |
| 36 | ' at classifyError (file://<checkout>/packages/shared/src/nodeSqliteClient.ts:155:10)\n' + |
| 37 | ' at execute (file://<checkout>/packages/shared/src/nodeSqliteClient.ts:1014:25) {\n' + |
| 38 | ' [cause]: Error: database or disk is full\n' + |
| 39 | ' at file://<checkout>/packages/shared/src/nodeSqliteClient.ts:1007:40\n' + |
| 40 | ' at withClassification (file://<checkout>/packages/shared/src/nodeSqliteClient.ts:922:16)\n' + |
| 41 | ' at execute (file://<checkout>/packages/shared/src/nodeSqliteClient.ts:1000:27)\n' + |
| 42 | ' }\n' + |
| 43 | ' }\n' + |
| 44 | ' }\n' + |
| 45 | '}' |
| 46 | } |
| 47 | State during failure |
| 48 | { |
| 49 | "runs": [ |
| 50 | { |
| 51 | "run_id": "run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1", |
| 52 | "status": "running" |
| 53 | } |
| 54 | ], |
| 55 | "messages": [ |
| 56 | { |
| 57 | "id": "message:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1:2cc3bc9b-d78e-4f28-9e15-90d11b5f2b2a", |
| 58 | "runId": "run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1", |
| 59 | "text": "Run the controlled storage recovery replay.", |
| 60 | "streaming": 0 |
| 61 | }, |
| 62 | { |
| 63 | "id": "message:provider:acpRegistry:native-item:provider-instance%3AstorageReplay%3Asqlite-runtime-replay%3Aturn%3A1%3Aassistant%3A0", |
| 64 | "runId": "run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1", |
| 65 | "text": "First paragraph.\n\n", |
| 66 | "streaming": 1 |
| 67 | } |
| 68 | ], |
| 69 | "turnItems": [ |
| 70 | { |
| 71 | "id": "turn-item:message:message%3Athread%3Af775b8b4-1548-48d1-a591-74382be6f8d0%3Aordinal%3A1%3A2cc3bc9b-d78e-4f28-9e15-90d11b5f2b2a", |
| 72 | "runId": "run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1", |
| 73 | "type": "user_message", |
| 74 | "text": "Run the controlled storage recovery replay.", |
| 75 | "status": "completed", |
| 76 | "streaming": null |
| 77 | }, |
| 78 | { |
| 79 | "id": "turn-item:provider:acpRegistry:native-item:workspace-preparation%3Arun%3Athread%3Af775b8b4-1548-48d1-a591-74382be6f8d0%3Aordinal%3A1", |
| 80 | "runId": "run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1", |
| 81 | "type": "command_execution", |
| 82 | "text": null, |
| 83 | "status": "completed", |
| 84 | "streaming": null |
| 85 | }, |
| 86 | { |
| 87 | "id": "turn-item:provider:acpRegistry:native-item:provider-instance%3AstorageReplay%3Asqlite-runtime-replay%3Atool%3Abackground-probe", |
| 88 | "runId": "run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1", |
| 89 | "type": "command_execution", |
| 90 | "text": null, |
| 91 | "status": "running", |
| 92 | "streaming": null |
| 93 | }, |
| 94 | { |
| 95 | "id": "turn-item:provider:acpRegistry:native-item:provider-instance%3AstorageReplay%3Asqlite-runtime-replay%3Aturn%3A1%3Aassistant%3A0", |
| 96 | "runId": "run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1", |
| 97 | "type": "assistant_message", |
| 98 | "text": "First paragraph.\n\n", |
| 99 | "status": "running", |
| 100 | "streaming": true |
| 101 | } |
| 102 | ], |
| 103 | "textEvents": [ |
| 104 | { |
| 105 | "sequence": 8, |
| 106 | "type": "message.updated", |
| 107 | "text": "Run the controlled storage recovery replay.", |
| 108 | "streaming": false |
| 109 | }, |
| 110 | { |
| 111 | "sequence": 9, |
| 112 | "type": "turn-item.updated", |
| 113 | "text": "Run the controlled storage recovery replay.", |
| 114 | "streaming": null |
| 115 | }, |
| 116 | { |
| 117 | "sequence": 26, |
| 118 | "type": "message.updated", |
| 119 | "text": "First paragraph.\n\n", |
| 120 | "streaming": true |
| 121 | }, |
| 122 | { |
| 123 | "sequence": 27, |
| 124 | "type": "turn-item.updated", |
| 125 | "text": "First paragraph.\n\n", |
| 126 | "streaming": true |
| 127 | } |
| 128 | ] |
| 129 | } |
| 130 | State after successful retry, before prompt completion |
| 131 | { |
| 132 | "runs": [ |
| 133 | { |
| 134 | "run_id": "run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1", |
| 135 | "status": "running" |
| 136 | } |
| 137 | ], |
| 138 | "messages": [ |
| 139 | { |
| 140 | "id": "message:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1:2cc3bc9b-d78e-4f28-9e15-90d11b5f2b2a", |
| 141 | "runId": "run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1", |
| 142 | "text": "Run the controlled storage recovery replay.", |
| 143 | "streaming": 0 |
| 144 | }, |
| 145 | { |
| 146 | "id": "message:provider:acpRegistry:native-item:provider-instance%3AstorageReplay%3Asqlite-runtime-replay%3Aturn%3A1%3Aassistant%3A0", |
| 147 | "runId": "run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1", |
| 148 | "text": "First paragraph.\n\nHeld text survives a real disk-full failure.", |
| 149 | "streaming": 1 |
| 150 | } |
| 151 | ], |
| 152 | "turnItems": [ |
| 153 | { |
| 154 | "id": "turn-item:message:message%3Athread%3Af775b8b4-1548-48d1-a591-74382be6f8d0%3Aordinal%3A1%3A2cc3bc9b-d78e-4f28-9e15-90d11b5f2b2a", |
| 155 | "runId": "run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1", |
| 156 | "type": "user_message", |
| 157 | "text": "Run the controlled storage recovery replay.", |
| 158 | "status": "completed", |
| 159 | "streaming": null |
| 160 | }, |
| 161 | { |
| 162 | "id": "turn-item:provider:acpRegistry:native-item:workspace-preparation%3Arun%3Athread%3Af775b8b4-1548-48d1-a591-74382be6f8d0%3Aordinal%3A1", |
| 163 | "runId": "run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1", |
| 164 | "type": "command_execution", |
| 165 | "text": null, |
| 166 | "status": "completed", |
| 167 | "streaming": null |
| 168 | }, |
| 169 | { |
| 170 | "id": "turn-item:provider:acpRegistry:native-item:provider-instance%3AstorageReplay%3Asqlite-runtime-replay%3Atool%3Abackground-probe", |
| 171 | "runId": "run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1", |
| 172 | "type": "command_execution", |
| 173 | "text": null, |
| 174 | "status": "running", |
| 175 | "streaming": null |
| 176 | }, |
| 177 | { |
| 178 | "id": "turn-item:provider:acpRegistry:native-item:provider-instance%3AstorageReplay%3Asqlite-runtime-replay%3Aturn%3A1%3Aassistant%3A0", |
| 179 | "runId": "run:thread:f775b8b4-1548-48d1-a591-74382be6f8d0:ordinal:1", |
| 180 | "type": "assistant_message", |
| 181 | "text": "First paragraph.\n\nHeld text survives a real disk-full failure.", |
| 182 | "status": "running", |
| 183 | "streaming": true |
| 184 | } |
| 185 | ], |
| 186 | "textEvents": [ |
| 187 | { |
| 188 | "sequence": 8, |
| 189 | "type": "message.updated", |
| 190 | "text": "Run the controlled storage recovery replay.", |
| 191 | "streaming": false |
| 192 | }, |
| 193 | { |
| 194 | "sequence": 9, |
| 195 | "type": "turn-item.updated", |
| 196 | "text": "Run the controlled storage recovery replay.", |
| 197 | "streaming": null |
| 198 | }, |
| 199 | { |
| 200 | "sequence": 26, |
| 201 | "type": "message.updated", |
| 202 | "text": "First paragraph.\n\n", |
| 203 | "streaming": true |
| 204 | }, |
| 205 | { |
| 206 | "sequence": 27, |
| 207 | "type": "turn-item.updated", |
| 208 | "text": "First paragraph.\n\n", |
| 209 | "streaming": true |
| 210 | }, |
| 211 | { |
| 212 | "sequence": 29, |
| 213 | "type": "message.updated", |
| 214 | "text": "First paragraph.\n\nHeld text survives a real disk-full failure.", |
| 215 | "streaming": true |
| 216 | }, |
| 217 | { |
| 218 | "sequence": 30, |
| 219 | "type": "turn-item.updated", |
| 220 | "text": "First paragraph.\n\nHeld text survives a real disk-full failure.", |
| 221 | "streaming": true |
| 222 | } |
| 223 | ] |
| 224 | } |
| 225 | Verdict |
| 226 | { |
| 227 | "status": "passed", |
| 228 | "head": "27c2e13d491a3c0ad19e18f8f41fc2067771b88d", |
| 229 | "base": "15fe2832727893cbdd2214383f9f971474849695", |
| 230 | "machine": "physical corex", |
| 231 | "node": "v26.12.1", |
| 232 | "provider": "controlled free ACP replay", |
| 233 | "realSQLiteFailure": true, |
| 234 | "realDiskFull": true, |
| 235 | "independentFilesystemWritesFailENOSPC": true, |
| 236 | "providerUpdatePersistedAfterRestoration": true, |
| 237 | "heldTextRetriedWhilePromptOpen": true, |
| 238 | "failureCount": 2, |
| 239 | "threadId": "f775b8b4-1548-48d1-a591-74382be6f8d0" |
| 240 | } |
R16 crash/restart log, milestones and persisted state
| 1 | Milestones |
| 2 | {"kind":"server-started","at":"2026-10-31T19:11:37.330Z","head":"27c2e13d491a3c0ad19e18f8f41fc2067771b88d","node":"v26.12.1","pid":2943997,"machine":"physical corex","sqlite":"file-backed statev2.sqlite","provider":"free synthetic ACP replay","sourceDirty":false} |
| 3 | {"kind":"sigkill-with-idle-paragraph-pending","at":"2026-10-31T19:11:37.994Z","runId":"run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1","runStatus":"running","persistedPrefix":"First paragraph.\n\n","fullHeldTextAbsent":true,"pid":2943997} |
| 4 | {"kind":"crashed-server-exited","at":"2026-10-31T19:11:38.050Z","code":null,"signal":"SIGKILL"} |
| 5 | {"kind":"after-process-exit-before-restart","at":"2026-10-31T19:11:38.051Z","originalRunStatus":"running","fullHeldTextAbsent":true,"messagePrefixOnly":true} |
| 6 | {"kind":"restart-reconciliation-observed","at":"2026-10-31T19:11:43.766Z","originalRunStatus":"cancelled","runCount":1,"messageText":"First paragraph.\n\n","messageStreaming":0,"turnItemStatus":"cancelled","turnItemStreaming":true,"fullHeldTextAbsent":true,"continueThreadsAfterServerUpdate":false} |
| 7 | |
| 8 | Actual restart recovery log |
| 9 | V2 orchestration recovery completed |
| 10 | { |
| 11 | terminalizedRuns: 1, |
| 12 | stoppedSessions: 1, |
| 13 | closedRequests: 0, |
| 14 | retiredEffects: 0, |
| 15 | requeuedEffects: 0 |
| 16 | } |
| 17 | [00:41:43.579] DEBUG (#7444): |
| 18 | After SIGKILL, before restart |
| 19 | { |
| 20 | "runs": [ |
| 21 | { |
| 22 | "run_id": "run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1", |
| 23 | "status": "running" |
| 24 | } |
| 25 | ], |
| 26 | "messages": [ |
| 27 | { |
| 28 | "id": "message:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1:4854b980-1a95-4c80-82ef-1f40ebe34812", |
| 29 | "runId": "run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1", |
| 30 | "text": "Run the controlled storage recovery replay.", |
| 31 | "streaming": 0 |
| 32 | }, |
| 33 | { |
| 34 | "id": "message:provider:acpRegistry:native-item:provider-instance%3AstorageReplay%3Asqlite-runtime-replay%3Aturn%3A1%3Aassistant%3A0", |
| 35 | "runId": "run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1", |
| 36 | "text": "First paragraph.\n\n", |
| 37 | "streaming": 1 |
| 38 | } |
| 39 | ], |
| 40 | "turnItems": [ |
| 41 | { |
| 42 | "id": "turn-item:message:message%3Athread%3A5e11efa8-331d-4d0b-8873-ad2e00563636%3Aordinal%3A1%3A4854b980-1a95-4c80-82ef-1f40ebe34812", |
| 43 | "runId": "run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1", |
| 44 | "type": "user_message", |
| 45 | "text": "Run the controlled storage recovery replay.", |
| 46 | "status": "completed", |
| 47 | "streaming": null |
| 48 | }, |
| 49 | { |
| 50 | "id": "turn-item:provider:acpRegistry:native-item:workspace-preparation%3Arun%3Athread%3A5e11efa8-331d-4d0b-8873-ad2e00563636%3Aordinal%3A1", |
| 51 | "runId": "run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1", |
| 52 | "type": "command_execution", |
| 53 | "text": null, |
| 54 | "status": "completed", |
| 55 | "streaming": null |
| 56 | }, |
| 57 | { |
| 58 | "id": "turn-item:provider:acpRegistry:native-item:provider-instance%3AstorageReplay%3Asqlite-runtime-replay%3Atool%3Abackground-probe", |
| 59 | "runId": "run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1", |
| 60 | "type": "command_execution", |
| 61 | "text": null, |
| 62 | "status": "running", |
| 63 | "streaming": null |
| 64 | }, |
| 65 | { |
| 66 | "id": "turn-item:provider:acpRegistry:native-item:provider-instance%3AstorageReplay%3Asqlite-runtime-replay%3Aturn%3A1%3Aassistant%3A0", |
| 67 | "runId": "run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1", |
| 68 | "type": "assistant_message", |
| 69 | "text": "First paragraph.\n\n", |
| 70 | "status": "running", |
| 71 | "streaming": true |
| 72 | } |
| 73 | ], |
| 74 | "textEvents": [ |
| 75 | { |
| 76 | "sequence": 8, |
| 77 | "type": "message.updated", |
| 78 | "text": "Run the controlled storage recovery replay.", |
| 79 | "streaming": false |
| 80 | }, |
| 81 | { |
| 82 | "sequence": 9, |
| 83 | "type": "turn-item.updated", |
| 84 | "text": "Run the controlled storage recovery replay.", |
| 85 | "streaming": null |
| 86 | }, |
| 87 | { |
| 88 | "sequence": 26, |
| 89 | "type": "message.updated", |
| 90 | "text": "First paragraph.\n\n", |
| 91 | "streaming": true |
| 92 | }, |
| 93 | { |
| 94 | "sequence": 27, |
| 95 | "type": "turn-item.updated", |
| 96 | "text": "First paragraph.\n\n", |
| 97 | "streaming": true |
| 98 | } |
| 99 | ] |
| 100 | } |
| 101 | After restart |
| 102 | { |
| 103 | "runs": [ |
| 104 | { |
| 105 | "run_id": "run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1", |
| 106 | "status": "cancelled" |
| 107 | } |
| 108 | ], |
| 109 | "messages": [ |
| 110 | { |
| 111 | "id": "message:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1:4854b980-1a95-4c80-82ef-1f40ebe34812", |
| 112 | "runId": "run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1", |
| 113 | "text": "Run the controlled storage recovery replay.", |
| 114 | "streaming": 0 |
| 115 | }, |
| 116 | { |
| 117 | "id": "message:provider:acpRegistry:native-item:provider-instance%3AstorageReplay%3Asqlite-runtime-replay%3Aturn%3A1%3Aassistant%3A0", |
| 118 | "runId": "run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1", |
| 119 | "text": "First paragraph.\n\n", |
| 120 | "streaming": 0 |
| 121 | } |
| 122 | ], |
| 123 | "turnItems": [ |
| 124 | { |
| 125 | "id": "turn-item:message:message%3Athread%3A5e11efa8-331d-4d0b-8873-ad2e00563636%3Aordinal%3A1%3A4854b980-1a95-4c80-82ef-1f40ebe34812", |
| 126 | "runId": "run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1", |
| 127 | "type": "user_message", |
| 128 | "text": "Run the controlled storage recovery replay.", |
| 129 | "status": "completed", |
| 130 | "streaming": null |
| 131 | }, |
| 132 | { |
| 133 | "id": "turn-item:provider:acpRegistry:native-item:workspace-preparation%3Arun%3Athread%3A5e11efa8-331d-4d0b-8873-ad2e00563636%3Aordinal%3A1", |
| 134 | "runId": "run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1", |
| 135 | "type": "command_execution", |
| 136 | "text": null, |
| 137 | "status": "completed", |
| 138 | "streaming": null |
| 139 | }, |
| 140 | { |
| 141 | "id": "turn-item:provider:acpRegistry:native-item:provider-instance%3AstorageReplay%3Asqlite-runtime-replay%3Atool%3Abackground-probe", |
| 142 | "runId": "run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1", |
| 143 | "type": "command_execution", |
| 144 | "text": null, |
| 145 | "status": "cancelled", |
| 146 | "streaming": null |
| 147 | }, |
| 148 | { |
| 149 | "id": "turn-item:provider:acpRegistry:native-item:provider-instance%3AstorageReplay%3Asqlite-runtime-replay%3Aturn%3A1%3Aassistant%3A0", |
| 150 | "runId": "run:thread:5e11efa8-331d-4d0b-8873-ad2e00563636:ordinal:1", |
| 151 | "type": "assistant_message", |
| 152 | "text": "First paragraph.\n\n", |
| 153 | "status": "cancelled", |
| 154 | "streaming": true |
| 155 | } |
| 156 | ], |
| 157 | "textEvents": [ |
| 158 | { |
| 159 | "sequence": 8, |
| 160 | "type": "message.updated", |
| 161 | "text": "Run the controlled storage recovery replay.", |
| 162 | "streaming": false |
| 163 | }, |
| 164 | { |
| 165 | "sequence": 9, |
| 166 | "type": "turn-item.updated", |
| 167 | "text": "Run the controlled storage recovery replay.", |
| 168 | "streaming": null |
| 169 | }, |
| 170 | { |
| 171 | "sequence": 26, |
| 172 | "type": "message.updated", |
| 173 | "text": "First paragraph.\n\n", |
| 174 | "streaming": true |
| 175 | }, |
| 176 | { |
| 177 | "sequence": 27, |
| 178 | "type": "turn-item.updated", |
| 179 | "text": "First paragraph.\n\n", |
| 180 | "streaming": true |
| 181 | }, |
| 182 | { |
| 183 | "sequence": 33, |
| 184 | "type": "message.updated", |
| 185 | "text": "First paragraph.\n\n", |
| 186 | "streaming": false |
| 187 | }, |
| 188 | { |
| 189 | "sequence": 35, |
| 190 | "type": "turn-item.updated", |
| 191 | "text": "First paragraph.\n\n", |
| 192 | "streaming": true |
| 193 | } |
| 194 | ] |
| 195 | } |
| 196 | Verdict |
| 197 | { |
| 198 | "status": "passed", |
| 199 | "head": "27c2e13d491a3c0ad19e18f8f41fc2067771b88d", |
| 200 | "base": "15fe2832727893cbdd2214383f9f971474849695", |
| 201 | "machine": "physical corex", |
| 202 | "node": "v26.12.1", |
| 203 | "provider": "controlled free ACP replay", |
| 204 | "crashSignal": "SIGKILL", |
| 205 | "restartSameHome": true, |
| 206 | "continueThreadsAfterServerUpdate": false, |
| 207 | "originalRunStatus": "cancelled", |
| 208 | "persistedPrefixRetained": true, |
| 209 | "unpersistedTailRecovered": false, |
| 210 | "threadId": "5e11efa8-331d-4d0b-8873-ad2e00563636" |
| 211 | } |
R15 complete disk-full verifier, local paths replaced with placeholders
| 1 | import assert from 'node:assert/strict'; |
| 2 | import { spawn, execFileSync } from 'node:child_process'; |
| 3 | import { randomUUID } from 'node:crypto'; |
| 4 | import * as fs from 'node:fs'; |
| 5 | import * as http from 'node:http'; |
| 6 | import * as path from 'node:path'; |
| 7 | import { DatabaseSync } from 'node:sqlite'; |
| 8 | import * as Effect from 'effect/Effect'; |
| 9 | import * as Layer from 'effect/Layer'; |
| 10 | import * as Stream from 'effect/Stream'; |
| 11 | import { Socket } from 'effect/socket'; |
| 12 | import { RpcClient, RpcSerialization } from 'effect/rpc'; |
| 13 | import { CommandId, ProjectId, ThreadId, ProviderInstanceId, WsRpcGroup, |
| 14 | ORCHESTRATION_PROTOCOL_VERSION, ORCHESTRATION_PROTOCOL_HEADER } from '@t3tools/contracts'; |
| 15 | |
| 16 | const root = '<checkout>'; |
| 17 | const out = '<isolated-task>/disk-full'; |
| 18 | const home = path.join(out, 'storage', 'home'); |
| 19 | const workspace = path.join(out, 'workspace'); |
| 20 | const heldText = 'First paragraph.\n\nHeld text survives a real disk-full failure.'; |
| 21 | const marker = 'PROVIDER_INGESTION_AFTER_DISK_SPACE_RESTORED'; |
| 22 | const afterMarker = 'PROVIDER_INGESTION_AFTER_STORAGE_RECOVERY'; |
| 23 | const head = execFileSync('git', ['rev-parse', 'HEAD'], {cwd: root, encoding: 'utf8'}).trim(); |
| 24 | assert.equal(head, '27c2e13d491a3c0ad19e18f8f41fc2067771b88d'); |
| 25 | assert.equal(execFileSync('git', ['status', '--porcelain'], {cwd: root, encoding: 'utf8'}), ''); |
| 26 | fs.mkdirSync(path.join(home, 'userdata'), {recursive: true}); |
| 27 | fs.mkdirSync(workspace, {recursive: true}); |
| 28 | execFileSync('git', ['init', '-q', workspace]); |
| 29 | execFileSync('git', ['-C', workspace, '-c', 'user.name=Runtime verifier', '-c', 'user.email=verifier@localhost', 'commit', '--allow-empty', '-qm', 'synthetic workspace']); |
| 30 | const milestones: unknown[] = []; |
| 31 | function record(kind: string, data: object = {}) { |
| 32 | const entry = {kind, at: new Date().toISOString(), ...data}; |
| 33 | milestones.push(entry); |
| 34 | fs.appendFileSync(path.join(out, 'milestones.ndjson'), JSON.stringify(entry) + '\n'); |
| 35 | console.log(JSON.stringify(entry)); |
| 36 | } |
| 37 | const failureObserved = Promise.withResolvers<void>(); |
| 38 | const recovered = Promise.withResolvers<void>(); |
| 39 | const gate = http.createServer(async (req, res) => { |
| 40 | if (req.url === '/failure-observed') { |
| 41 | await failureObserved.promise; |
| 42 | res.end('marker may now be emitted'); |
| 43 | } else if (req.url === '/recovered') { |
| 44 | await recovered.promise; |
| 45 | res.end('held text persisted; finish the replay'); |
| 46 | } else res.writeHead(404).end(); |
| 47 | }); |
| 48 | gate.listen(0, '127.0.0.1'); |
| 49 | await new Promise<void>(resolve => gate.once('listening', resolve)); |
| 50 | const gateAddress = gate.address(); |
| 51 | assert.ok(gateAddress && typeof gateAddress !== 'string'); |
| 52 | const gateOrigin = `http://127.0.0.1:${gateAddress.port}`; |
| 53 | fs.writeFileSync(path.join(out, 'agent.mjs'), ` |
| 54 | import readline from 'node:readline'; |
| 55 | const send = value => process.stdout.write(JSON.stringify({jsonrpc:'2.0',...value})+'\\n'); |
| 56 | const sid = 'sqlite-runtime-replay'; |
| 57 | const update = value => send({method:'session/update',params:{sessionId:sid,update:value}}); |
| 58 | async function handle(m) { |
| 59 | if (m.id === undefined) return; |
| 60 | let result = {}; |
| 61 | switch(m.method) { |
| 62 | case 'initialize': result={protocolVersion:1,agentCapabilities:{loadSession:true,promptCapabilities:{image:false,audio:false,embeddedContext:true}},authMethods:[],agentInfo:{name:'sqlite-runtime-replay',version:'1'}}; break; |
| 63 | case 'session/new': result={sessionId:sid,models:{currentModelId:'storage-recovery',availableModels:[{modelId:'storage-recovery',name:'Storage recovery'}]}}; break; |
| 64 | case 'session/load': result={}; break; |
| 65 | case 'session/prompt': |
| 66 | update({sessionUpdate:'tool_call',toolCallId:'background-probe',kind:'execute',title:'Synthetic background command',status:'in_progress',rawInput:{command:'synthetic held-open provider'}}); |
| 67 | update({sessionUpdate:'agent_message_chunk',content:{type:'text',text:${JSON.stringify(heldText)}}}); |
| 68 | await fetch(${JSON.stringify(gateOrigin + '/failure-observed')}); |
| 69 | update({sessionUpdate:'session_info_update',title:${JSON.stringify(marker)}}); |
| 70 | await fetch(${JSON.stringify(gateOrigin + '/recovered')}); |
| 71 | update({sessionUpdate:'session_info_update',title:${JSON.stringify(afterMarker)}}); |
| 72 | update({sessionUpdate:'tool_call_update',toolCallId:'background-probe',status:'completed'}); |
| 73 | result={stopReason:'end_turn'}; break; |
| 74 | } |
| 75 | send({id:m.id,result}); |
| 76 | } |
| 77 | readline.createInterface({input:process.stdin}).on('line',line=>{void handle(JSON.parse(line)).catch(error=>{console.error(error);process.exitCode=1})}); |
| 78 | `); |
| 79 | fs.writeFileSync(path.join(home, 'userdata', 'settings.json'), JSON.stringify({ |
| 80 | responseStreamingMode: 'paragraph', enableProviderUpdateChecks: false, |
| 81 | providerInstances: {storageReplay: {driver:'acpRegistry',displayName:'SQLite runtime replay',enabled:true, |
| 82 | config:{source:'local',commandPath:process.execPath,commandArgs:[path.join(out, 'agent.mjs')]}}}, |
| 83 | })); |
| 84 | const reservation = http.createServer(); |
| 85 | reservation.listen(0, '127.0.0.1'); |
| 86 | await new Promise<void>(resolve => reservation.once('listening', resolve)); |
| 87 | const address = reservation.address(); |
| 88 | assert.ok(address && typeof address !== 'string'); |
| 89 | const port = address.port; |
| 90 | await new Promise<void>(resolve => reservation.close(()=>resolve())); |
| 91 | const origin = `http://127.0.0.1:${port}`; |
| 92 | const bootstrap = randomUUID(); |
| 93 | const server = spawn(process.execPath, ['apps/server/src/bin.ts','start','--bootstrap-fd','3','--log-level','debug'], { |
| 94 | cwd: root, stdio: ['ignore','pipe','pipe','pipe'], |
| 95 | env: {...process.env, PATH:path.dirname(process.execPath)+':'+process.env.PATH, T3CODE_TELEMETRY_DISABLED:'1'}, |
| 96 | }); |
| 97 | fs.writeFileSync(path.join(out,'server.pid'), String(server.pid)); |
| 98 | const pipe = server.stdio[3]; |
| 99 | assert.ok(pipe && 'end' in pipe); |
| 100 | pipe.end(JSON.stringify({mode:'desktop',noBrowser:true,port,t3Home:home,host:'127.0.0.1',desktopBootstrapToken:bootstrap,tailscaleServeEnabled:false,tailscaleServePort:443})); |
| 101 | const ready = Promise.withResolvers<void>(); |
| 102 | const failed = Promise.withResolvers<never>(); |
| 103 | let log = ''; |
| 104 | let onFailure: (() => void) | undefined; |
| 105 | let failureCount = 0; |
| 106 | for (const stream of [server.stdout,server.stderr]) stream?.on('data', data => { |
| 107 | const text = data.toString().replaceAll(bootstrap,'[REDACTED]'); |
| 108 | fs.appendFileSync(path.join(out,'server.log'),text); |
| 109 | log += text; |
| 110 | if(log.includes('startup phase: complete')) ready.resolve(); |
| 111 | if(text.includes('orchestration V2 idle assistant text flush failed')) { |
| 112 | failureCount++; |
| 113 | try { onFailure?.(); } catch(error) { failed.reject(error); } |
| 114 | } |
| 115 | }); |
| 116 | server.once('error', error => {ready.reject(error);failed.reject(error)}); |
| 117 | server.once('exit', code => {ready.reject(new Error('Server exited '+code));}); |
| 118 | const deadline = setTimeout(()=>failed.reject(new Error('Runtime verification deadline exceeded')), 60000); |
| 119 | let db: DatabaseSync | undefined; |
| 120 | const filler = path.join(out, 'storage', 'filler.bin'); |
| 121 | const canary = path.join(out, 'storage', 'write-canary'); |
| 122 | let locked = false; |
| 123 | let verdict: object = {status:'failed',head}; |
| 124 | const threadId = ThreadId.make(randomUUID()); |
| 125 | const events = () => db!.prepare(`SELECT sequence,event_type,occurred_at,payload_json FROM orchestration_events WHERE stream_id=? ORDER BY sequence`).all(threadId).map(row=>({...row,payload:JSON.parse(String(row.payload_json)),payload_json:undefined})); |
| 126 | const state = () => { |
| 127 | const stored = events(); |
| 128 | const messages = db!.prepare('SELECT message_id,run_id,streaming,payload_json FROM orchestration_v2_projection_messages WHERE thread_id=?').all(threadId).map(row=>({...row,payload:JSON.parse(String(row.payload_json)),payload_json:undefined})); |
| 129 | const items = db!.prepare('SELECT turn_item_id,run_id,type,status,payload_json FROM orchestration_v2_projection_turn_items WHERE thread_id=?').all(threadId).map(row=>({...row,payload:JSON.parse(String(row.payload_json)),payload_json:undefined})); |
| 130 | const runs = db!.prepare('SELECT run_id,status FROM orchestration_v2_projection_runs WHERE thread_id=?').all(threadId); |
| 131 | return {stored,messages,items,runs}; |
| 132 | }; |
| 133 | let dropped = false; |
| 134 | let retried = false; |
| 135 | let storageFailureRecorded = false; |
| 136 | try { |
| 137 | await Promise.race([ready.promise,failed.promise]); |
| 138 | record('server-started', {head,node:process.version,pid:server.pid,machine:'physical corex',sqlite:'file-backed statev2.sqlite',provider:'free synthetic ACP replay',sourceDirty:false}); |
| 139 | db = new DatabaseSync(path.join(home,'userdata','statev2.sqlite')); |
| 140 | db.exec('PRAGMA busy_timeout=5000'); |
| 141 | assert.equal(Number(fs.statfsSync(path.join(out,'storage')).type), 0x01021994, 'Only the bounded private tmpfs may be filled'); |
| 142 | onFailure = () => { |
| 143 | if(storageFailureRecorded) return; |
| 144 | storageFailureRecorded = true; |
| 145 | const snapshot = state(); |
| 146 | assert.ok(!snapshot.stored.some(row=>row.payload.text===heldText)); |
| 147 | assert.ok(!snapshot.messages.some(row=>row.payload.text===heldText)); |
| 148 | assert.ok(!snapshot.items.some(row=>row.payload.text===heldText)); |
| 149 | assert.equal(snapshot.runs[0]?.status,'running'); |
| 150 | assert.ok(locked && !dropped); |
| 151 | assert.throws(() => fs.writeFileSync(canary,'cannot allocate'), (error: any) => error.code === 'ENOSPC'); |
| 152 | assert.equal(fs.statfsSync(path.join(out,'storage')).bavail,0); |
| 153 | fs.writeFileSync(path.join(out,'during-failure.json'),JSON.stringify(snapshot,null,2)); |
| 154 | record('idle-write-failed-with-real-disk-full',{failureCount,independentFilesystemWriteAlsoFailsENOSPC:true,fullTextEvents:0,fullTextMessageProjections:0,fullTextItemProjections:0,runStatus:'running'}); |
| 155 | fs.unlinkSync(filler); |
| 156 | dropped = true; |
| 157 | record('disk-space-restored',{freeBlocks:fs.statfsSync(path.join(out,'storage')).bavail}); |
| 158 | fs.writeFileSync(canary,'restored'); |
| 159 | record('independent-filesystem-write-succeeds-after-restoration'); |
| 160 | failureObserved.resolve(); |
| 161 | }; |
| 162 | const tokenResponse=await fetch(origin+'/oauth/token',{method:'POST',body:new URLSearchParams({grant_type:'urn:ietf:params:oauth:grant-type:token-exchange',subject_token:bootstrap,subject_token_type:'urn:t3:params:oauth:token-type:environment-bootstrap',requested_token_type:'urn:ietf:params:oauth:token-type:access_token'})}); |
| 163 | assert.ok(tokenResponse.ok); |
| 164 | const token = await tokenResponse.json() as {access_token:string}; |
| 165 | const headers={Authorization:'Bearer '+token.access_token,'Content-Type':'application/json',[ORCHESTRATION_PROTOCOL_HEADER]:String(ORCHESTRATION_PROTOCOL_VERSION)}; |
| 166 | const projectId=ProjectId.make(randomUUID()); |
| 167 | const created=await fetch(origin+'/api/projects/mutate',{method:'POST',headers,body:JSON.stringify({type:'project.create',commandId:randomUUID(),projectId,title:'SQLite recovery runtime probe',workspaceRoot:workspace})}); |
| 168 | assert.ok(created.ok,await created.text()); |
| 169 | const ticketResponse=await fetch(origin+'/api/auth/websocket-ticket',{method:'POST',headers}); |
| 170 | assert.ok(ticketResponse.ok); |
| 171 | const ticket=await ticketResponse.json() as {ticket:string}; |
| 172 | const layerSocket=Socket.layerWebSocket(`${origin.replace('http:','ws:')}/ws?orchestrationProtocol=${ORCHESTRATION_PROTOCOL_VERSION}&wsTicket=${encodeURIComponent(ticket.ticket)}`).pipe(Layer.provide(Socket.layerWebSocketConstructorGlobal)); |
| 173 | const protocol=RpcClient.layerProtocolSocket({pingInterval:'5 seconds',pingTimeout:'45 seconds'}).pipe(Layer.provide(layerSocket),Layer.provide(RpcSerialization.layerJson)); |
| 174 | const program=Effect.gen(function*(){ |
| 175 | const client=yield* RpcClient.make(WsRpcGroup); |
| 176 | yield* client['orchestration.launchThread']({commandId:CommandId.make(randomUUID()),threadId,projectId,title:'Synthetic storage recovery',modelSelection:{instanceId:ProviderInstanceId.make('storageReplay'),model:'storage-recovery'},runtimeMode:'full-access',interactionMode:'default',workspaceStrategy:{type:'root'},initialMessage:{text:'Run the controlled storage recovery replay.',attachments:[]}}); |
| 177 | return yield* client['orchestration.subscribeThread']({threadId}).pipe(Stream.mapEffect(item=>Effect.sync(()=>{ |
| 178 | fs.appendFileSync(path.join(out,'wire-events.ndjson'),JSON.stringify(item)+'\n'); |
| 179 | const snapshot=state(); |
| 180 | if(!locked && snapshot.items.some(row=>row.payload.text==='First paragraph.\n\n' && row.payload.streaming===true)) { |
| 181 | assert.ok(!snapshot.stored.some(row=>row.payload.text===heldText)); |
| 182 | assert.equal(snapshot.runs[0]?.status,'running'); |
| 183 | const checkpoint=db!.prepare('PRAGMA wal_checkpoint(TRUNCATE)').get(); |
| 184 | assert.equal(checkpoint!.busy,0); |
| 185 | const fd=fs.openSync(filler,'w'); |
| 186 | const chunk=Buffer.alloc(65536,0x5a); |
| 187 | let enospc=false; |
| 188 | try { |
| 189 | for(;;) fs.writeSync(fd,chunk); |
| 190 | } catch(error) { |
| 191 | assert.equal((error as NodeJS.ErrnoException).code,'ENOSPC'); |
| 192 | enospc=true; |
| 193 | } finally { fs.closeSync(fd); } |
| 194 | assert.ok(enospc); |
| 195 | assert.equal(fs.statfsSync(path.join(out,'storage')).bavail,0); |
| 196 | locked = true; |
| 197 | assert.throws(()=>fs.writeFileSync(canary,'cannot allocate'),(error:any)=>error.code==='ENOSPC'); |
| 198 | record('private-tmpfs-filled-while-full-text-held',{filesystem:'tmpfs',capacityBytes:16*1024*1024,freeBlocks:0,independentWriteError:'ENOSPC',walCheckpointTruncated:true,heldFullTextAbsent:true,runStatus:'running'}); |
| 199 | fs.writeFileSync(path.join(out,'write-lock-acquired.json'),JSON.stringify(snapshot,null,2)); |
| 200 | } |
| 201 | if(dropped && !retried && snapshot.messages.some(row=>row.payload.text===heldText && row.streaming===1) && snapshot.items.some(row=>row.payload.text===heldText && row.payload.streaming===true)) { |
| 202 | assert.equal(snapshot.runs[0]?.status,'running'); |
| 203 | const fullEvents=snapshot.stored.filter(row=>row.payload.text===heldText && row.payload.streaming===true); |
| 204 | assert.deepEqual(fullEvents.map(row=>row.event_type).sort(),['message.updated','turn-item.updated']); |
| 205 | retried=true; |
| 206 | fs.writeFileSync(path.join(out,'successful-idle-retry.json'),JSON.stringify(snapshot,null,2)); |
| 207 | record('held-text-retried-without-new-text-or-prompt-completion',{events:fullEvents.map(row=>({sequence:row.sequence,eventType:row.event_type,streaming:row.payload.streaming})),messageProjection:heldText,turnItemProjection:heldText,runStatus:'running',providerPromptStillOpen:true}); |
| 208 | recovered.resolve(); |
| 209 | } |
| 210 | return retried && snapshot.runs[0]?.status==='completed' && snapshot.stored.some(row=>row.payload.nativeMetadata?.title===afterMarker); |
| 211 | })),Stream.filter(Boolean),Stream.take(1),Stream.runCollect); |
| 212 | }).pipe(Effect.scoped,Effect.provide(protocol)); |
| 213 | await Promise.race([Effect.runPromise(program),failed.promise]); |
| 214 | assert.ok(storageFailureRecorded && dropped && retried); |
| 215 | assert.ok(/database or disk is full|SQLITE_FULL|SQLITE\(13\)/.test(log)); |
| 216 | assert.ok(log.includes('orchestration V2 idle assistant text flush failed')); |
| 217 | assert.equal(fs.readFileSync(canary,'utf8'),'restored'); |
| 218 | const finalState=state(); |
| 219 | fs.writeFileSync(path.join(out,'final-database.json'),JSON.stringify(finalState,null,2)); |
| 220 | record('provider-completed-after-proof',{runStatus:finalState.runs[0]?.status,failureCount}); |
| 221 | verdict={status:'passed',head,base:'15fe2832727893cbdd2214383f9f971474849695',machine:'physical corex',node:process.version,provider:'controlled free ACP replay',realSQLiteFailure:true,realDiskFull:true,independentFilesystemWritesFailENOSPC:true,providerUpdatePersistedAfterRestoration:true,heldTextRetriedWhilePromptOpen:true,failureCount,threadId}; |
| 222 | } catch(error) { |
| 223 | verdict={status:'failed',head,error:String(error),stack:(error as Error).stack}; |
| 224 | console.error(error); |
| 225 | process.exitCode=1; |
| 226 | } finally { |
| 227 | clearTimeout(deadline); |
| 228 | if(locked && !dropped && fs.existsSync(filler)) fs.unlinkSync(filler); |
| 229 | db?.close(); |
| 230 | if(server.exitCode===null) { |
| 231 | server.kill('SIGTERM'); |
| 232 | await new Promise<void>(resolve=>server.once('exit',()=>resolve())); |
| 233 | } |
| 234 | failureObserved.resolve();recovered.resolve(); |
| 235 | gate.closeAllConnections(); |
| 236 | await new Promise<void>(resolve=>gate.close(()=>resolve())); |
| 237 | fs.writeFileSync(path.join(out,'verdict.json'),JSON.stringify(verdict,null,2)); |
| 238 | console.log(JSON.stringify(verdict)); |
| 239 | } |
R16 complete crash verifier, local paths replaced with placeholders
| 1 | import assert from 'node:assert/strict'; |
| 2 | import { spawn, execFileSync } from 'node:child_process'; |
| 3 | import { randomUUID } from 'node:crypto'; |
| 4 | import * as fs from 'node:fs'; |
| 5 | import * as http from 'node:http'; |
| 6 | import * as path from 'node:path'; |
| 7 | import { DatabaseSync } from 'node:sqlite'; |
| 8 | import * as Effect from 'effect/Effect'; |
| 9 | import * as Layer from 'effect/Layer'; |
| 10 | import * as Stream from 'effect/Stream'; |
| 11 | import { Socket } from 'effect/socket'; |
| 12 | import { RpcClient, RpcSerialization } from 'effect/rpc'; |
| 13 | import { CommandId, ProjectId, ThreadId, ProviderInstanceId, WsRpcGroup, |
| 14 | ORCHESTRATION_PROTOCOL_VERSION, ORCHESTRATION_PROTOCOL_HEADER } from '@t3tools/contracts'; |
| 15 | |
| 16 | const root = '<checkout>'; |
| 17 | const out = '<isolated-task>/crash'; |
| 18 | const home = path.join(out, 'home'); |
| 19 | const workspace = path.join(out, 'workspace'); |
| 20 | const heldText = 'First paragraph.\n\nThis pending paragraph is not yet durable at the crash cutpoint.'; |
| 21 | const marker = 'PROVIDER_INGESTION_AFTER_DATABASE_UNLOCK'; |
| 22 | const afterMarker = 'PROVIDER_INGESTION_AFTER_STORAGE_RECOVERY'; |
| 23 | const head = execFileSync('git', ['rev-parse', 'HEAD'], {cwd: root, encoding: 'utf8'}).trim(); |
| 24 | assert.equal(head, '27c2e13d491a3c0ad19e18f8f41fc2067771b88d'); |
| 25 | assert.equal(execFileSync('git', ['status', '--porcelain'], {cwd: root, encoding: 'utf8'}), ''); |
| 26 | fs.mkdirSync(path.join(home, 'userdata'), {recursive: true}); |
| 27 | fs.mkdirSync(workspace, {recursive: true}); |
| 28 | execFileSync('git', ['init', '-q', workspace]); |
| 29 | execFileSync('git', ['-C', workspace, '-c', 'user.name=Runtime verifier', '-c', 'user.email=verifier@localhost', 'commit', '--allow-empty', '-qm', 'synthetic workspace']); |
| 30 | const milestones: unknown[] = []; |
| 31 | function record(kind: string, data: object = {}) { |
| 32 | const entry = {kind, at: new Date().toISOString(), ...data}; |
| 33 | milestones.push(entry); |
| 34 | fs.appendFileSync(path.join(out, 'milestones.ndjson'), JSON.stringify(entry) + '\n'); |
| 35 | console.log(JSON.stringify(entry)); |
| 36 | } |
| 37 | const failureObserved = Promise.withResolvers<void>(); |
| 38 | const recovered = Promise.withResolvers<void>(); |
| 39 | const gate = http.createServer(async (req, res) => { |
| 40 | if (req.url === '/failure-observed') { |
| 41 | await failureObserved.promise; |
| 42 | res.end('marker may now be emitted'); |
| 43 | } else if (req.url === '/recovered') { |
| 44 | await recovered.promise; |
| 45 | res.end('held text persisted; finish the replay'); |
| 46 | } else res.writeHead(404).end(); |
| 47 | }); |
| 48 | gate.listen(0, '127.0.0.1'); |
| 49 | await new Promise<void>(resolve => gate.once('listening', resolve)); |
| 50 | const gateAddress = gate.address(); |
| 51 | assert.ok(gateAddress && typeof gateAddress !== 'string'); |
| 52 | const gateOrigin = `http://127.0.0.1:${gateAddress.port}`; |
| 53 | fs.writeFileSync(path.join(out, 'agent.mjs'), ` |
| 54 | import readline from 'node:readline'; |
| 55 | const send = value => process.stdout.write(JSON.stringify({jsonrpc:'2.0',...value})+'\\n'); |
| 56 | const sid = 'sqlite-runtime-replay'; |
| 57 | const update = value => send({method:'session/update',params:{sessionId:sid,update:value}}); |
| 58 | async function handle(m) { |
| 59 | if (m.id === undefined) return; |
| 60 | let result = {}; |
| 61 | switch(m.method) { |
| 62 | case 'initialize': result={protocolVersion:1,agentCapabilities:{loadSession:true,promptCapabilities:{image:false,audio:false,embeddedContext:true}},authMethods:[],agentInfo:{name:'sqlite-runtime-replay',version:'1'}}; break; |
| 63 | case 'session/new': result={sessionId:sid,models:{currentModelId:'storage-recovery',availableModels:[{modelId:'storage-recovery',name:'Storage recovery'}]}}; break; |
| 64 | case 'session/load': result={}; break; |
| 65 | case 'session/prompt': |
| 66 | update({sessionUpdate:'tool_call',toolCallId:'background-probe',kind:'execute',title:'Synthetic background command',status:'in_progress',rawInput:{command:'synthetic held-open provider'}}); |
| 67 | update({sessionUpdate:'agent_message_chunk',content:{type:'text',text:${JSON.stringify(heldText)}}}); |
| 68 | await fetch(${JSON.stringify(gateOrigin + '/failure-observed')}); |
| 69 | update({sessionUpdate:'session_info_update',title:${JSON.stringify(marker)}}); |
| 70 | await fetch(${JSON.stringify(gateOrigin + '/recovered')}); |
| 71 | update({sessionUpdate:'session_info_update',title:${JSON.stringify(afterMarker)}}); |
| 72 | update({sessionUpdate:'tool_call_update',toolCallId:'background-probe',status:'completed'}); |
| 73 | result={stopReason:'end_turn'}; break; |
| 74 | } |
| 75 | send({id:m.id,result}); |
| 76 | } |
| 77 | const rl=readline.createInterface({input:process.stdin}); rl.on('close',()=>process.exit(0)); rl.on('line',line=>{void handle(JSON.parse(line)).catch(error=>{console.error(error);process.exitCode=1})}); |
| 78 | `); |
| 79 | fs.writeFileSync(path.join(home, 'userdata', 'settings.json'), JSON.stringify({ |
| 80 | responseStreamingMode: 'paragraph', enableProviderUpdateChecks: false, continueThreadsAfterServerUpdate:false, |
| 81 | providerInstances: {storageReplay: {driver:'acpRegistry',displayName:'SQLite runtime replay',enabled:true, |
| 82 | config:{source:'local',commandPath:process.execPath,commandArgs:[path.join(out, 'agent.mjs')]}}}, |
| 83 | })); |
| 84 | const reservation = http.createServer(); |
| 85 | reservation.listen(0, '127.0.0.1'); |
| 86 | await new Promise<void>(resolve => reservation.once('listening', resolve)); |
| 87 | const address = reservation.address(); |
| 88 | assert.ok(address && typeof address !== 'string'); |
| 89 | const port = address.port; |
| 90 | await new Promise<void>(resolve => reservation.close(()=>resolve())); |
| 91 | const origin = `http://127.0.0.1:${port}`; |
| 92 | const bootstrap = randomUUID(); |
| 93 | let server: ReturnType<typeof spawn>; |
| 94 | let ready: ReturnType<typeof Promise.withResolvers<void>>; |
| 95 | let log = ''; |
| 96 | const failed = Promise.withResolvers<never>(); |
| 97 | function startServer(logName:string) { |
| 98 | ready = Promise.withResolvers<void>(); |
| 99 | const instance=spawn(process.execPath,['apps/server/src/bin.ts','start','--bootstrap-fd','3','--log-level','debug'],{ |
| 100 | cwd:root,stdio:['ignore','pipe','pipe','pipe'], |
| 101 | env:{...process.env,PATH:path.dirname(process.execPath)+':'+process.env.PATH,T3CODE_TELEMETRY_DISABLED:'1'}, |
| 102 | }); |
| 103 | server=instance; |
| 104 | fs.appendFileSync(path.join(out,'server-pids.txt'),String(instance.pid)+'\n'); |
| 105 | const bootstrapPipe=instance.stdio[3];assert.ok(bootstrapPipe && 'end' in bootstrapPipe); |
| 106 | bootstrapPipe.end(JSON.stringify({mode:'desktop',noBrowser:true,port,t3Home:home,host:'127.0.0.1',desktopBootstrapToken:bootstrap,tailscaleServeEnabled:false,tailscaleServePort:443})); |
| 107 | const instanceReady=ready; |
| 108 | for(const stream of [instance.stdout,instance.stderr]) stream?.on('data',data=>{ |
| 109 | const text=data.toString().replaceAll(bootstrap,'[REDACTED]'); |
| 110 | fs.appendFileSync(path.join(out,logName),text);log+=text; |
| 111 | if(text.includes('startup phase: complete')) instanceReady.resolve(); |
| 112 | }); |
| 113 | instance.once('error',error=>{instanceReady.reject(error);failed.reject(error)}); |
| 114 | instance.once('exit',code=>instanceReady.reject(new Error('Server exited '+code))); |
| 115 | } |
| 116 | startServer('before-crash-server.log'); |
| 117 | const deadline = setTimeout(()=>failed.reject(new Error('Runtime verification deadline exceeded')), 60000); |
| 118 | let db: DatabaseSync | undefined; |
| 119 | |
| 120 | let verdict: object = {status:'failed',head}; |
| 121 | const threadId = ThreadId.make(randomUUID()); |
| 122 | const events = () => db!.prepare(`SELECT sequence,event_type,occurred_at,payload_json FROM orchestration_events WHERE stream_id=? ORDER BY sequence`).all(threadId).map(row=>({...row,payload:JSON.parse(String(row.payload_json)),payload_json:undefined})); |
| 123 | const state = () => { |
| 124 | const stored = events(); |
| 125 | const messages = db!.prepare('SELECT message_id,run_id,streaming,payload_json FROM orchestration_v2_projection_messages WHERE thread_id=?').all(threadId).map(row=>({...row,payload:JSON.parse(String(row.payload_json)),payload_json:undefined})); |
| 126 | const items = db!.prepare('SELECT turn_item_id,run_id,type,status,payload_json FROM orchestration_v2_projection_turn_items WHERE thread_id=?').all(threadId).map(row=>({...row,payload:JSON.parse(String(row.payload_json)),payload_json:undefined})); |
| 127 | const runs = db!.prepare('SELECT run_id,status FROM orchestration_v2_projection_runs WHERE thread_id=?').all(threadId); |
| 128 | return {stored,messages,items,runs}; |
| 129 | }; |
| 130 | let crashQueued=false; |
| 131 | let originalRunId:string | undefined; |
| 132 | const crashExited=Promise.withResolvers<void>(); |
| 133 | try { |
| 134 | await Promise.race([ready.promise,failed.promise]); |
| 135 | record('server-started', {head,node:process.version,pid:server.pid,machine:'physical corex',sqlite:'file-backed statev2.sqlite',provider:'free synthetic ACP replay',sourceDirty:false}); |
| 136 | db = new DatabaseSync(path.join(home,'userdata','statev2.sqlite')); |
| 137 | db.exec('PRAGMA busy_timeout=5000'); |
| 138 | const tokenResponse=await fetch(origin+'/oauth/token',{method:'POST',body:new URLSearchParams({grant_type:'urn:ietf:params:oauth:grant-type:token-exchange',subject_token:bootstrap,subject_token_type:'urn:t3:params:oauth:token-type:environment-bootstrap',requested_token_type:'urn:ietf:params:oauth:token-type:access_token'})}); |
| 139 | assert.ok(tokenResponse.ok); |
| 140 | const token = await tokenResponse.json() as {access_token:string}; |
| 141 | const headers={Authorization:'Bearer '+token.access_token,'Content-Type':'application/json',[ORCHESTRATION_PROTOCOL_HEADER]:String(ORCHESTRATION_PROTOCOL_VERSION)}; |
| 142 | const projectId=ProjectId.make(randomUUID()); |
| 143 | const created=await fetch(origin+'/api/projects/mutate',{method:'POST',headers,body:JSON.stringify({type:'project.create',commandId:randomUUID(),projectId,title:'SQLite recovery runtime probe',workspaceRoot:workspace})}); |
| 144 | assert.ok(created.ok,await created.text()); |
| 145 | const ticketResponse=await fetch(origin+'/api/auth/websocket-ticket',{method:'POST',headers}); |
| 146 | assert.ok(ticketResponse.ok); |
| 147 | const ticket=await ticketResponse.json() as {ticket:string}; |
| 148 | const layerSocket=Socket.layerWebSocket(`${origin.replace('http:','ws:')}/ws?orchestrationProtocol=${ORCHESTRATION_PROTOCOL_VERSION}&wsTicket=${encodeURIComponent(ticket.ticket)}`).pipe(Layer.provide(Socket.layerWebSocketConstructorGlobal)); |
| 149 | const protocol=RpcClient.layerProtocolSocket({pingInterval:'5 seconds',pingTimeout:'45 seconds'}).pipe(Layer.provide(layerSocket),Layer.provide(RpcSerialization.layerJson)); |
| 150 | const program=Effect.gen(function*(){ |
| 151 | const client=yield* RpcClient.make(WsRpcGroup); |
| 152 | yield* client['orchestration.launchThread']({commandId:CommandId.make(randomUUID()),threadId,projectId,title:'Synthetic storage recovery',modelSelection:{instanceId:ProviderInstanceId.make('storageReplay'),model:'storage-recovery'},runtimeMode:'full-access',interactionMode:'default',workspaceStrategy:{type:'root'},initialMessage:{text:'Run the controlled storage recovery replay.',attachments:[]}}); |
| 153 | return yield* client['orchestration.subscribeThread']({threadId}).pipe(Stream.mapEffect(item=>Effect.sync(()=>{ |
| 154 | fs.appendFileSync(path.join(out,'wire-events.ndjson'),JSON.stringify(item)+'\n'); |
| 155 | const snapshot=state(); |
| 156 | if(!crashQueued && snapshot.items.some(row=>row.payload.text==='First paragraph.\n\n' && row.payload.streaming===true)) { |
| 157 | assert.ok(!snapshot.stored.some(row=>row.payload.text===heldText)); |
| 158 | assert.equal(snapshot.runs[0]?.status,'running'); |
| 159 | originalRunId=String(snapshot.runs[0]!.run_id); |
| 160 | fs.writeFileSync(path.join(out,'before-crash.json'),JSON.stringify(snapshot,null,2)); |
| 161 | record('sigkill-with-idle-paragraph-pending',{runId:originalRunId,runStatus:'running',persistedPrefix:'First paragraph.\n\n',fullHeldTextAbsent:true,pid:server.pid}); |
| 162 | crashQueued=true; |
| 163 | server.once('exit',(code,signal)=>{record('crashed-server-exited',{code,signal});crashExited.resolve()}); |
| 164 | server.kill('SIGKILL'); |
| 165 | } |
| 166 | return crashQueued; |
| 167 | })),Stream.filter(Boolean),Stream.take(1),Stream.runCollect); |
| 168 | }).pipe(Effect.scoped,Effect.provide(protocol)); |
| 169 | await Promise.race([Effect.runPromise(program).catch(error=>{if(!crashQueued)throw error}),failed.promise]); |
| 170 | assert.ok(crashQueued); |
| 171 | await crashExited.promise; |
| 172 | const afterExit=state(); |
| 173 | assert.ok(!afterExit.stored.some(row=>row.payload.text===heldText)); |
| 174 | assert.equal(afterExit.runs[0]?.status,'running'); |
| 175 | fs.writeFileSync(path.join(out,'after-crash-before-restart.json'),JSON.stringify(afterExit,null,2)); |
| 176 | record('after-process-exit-before-restart',{originalRunStatus:'running',fullHeldTextAbsent:true,messagePrefixOnly:true}); |
| 177 | startServer('after-restart-server.log'); |
| 178 | await Promise.race([ready.promise,failed.promise]); |
| 179 | const response=await fetch(origin+'/api/orchestration/threads/'+threadId,{headers}); |
| 180 | if(!response.ok) throw new Error('Restart read failed: '+response.status+' '+await response.text()); |
| 181 | const projection=await response.json(); |
| 182 | fs.writeFileSync(path.join(out,'after-restart-api.json'),JSON.stringify(projection,null,2)); |
| 183 | const restored=state(); |
| 184 | const original=restored.runs.find(row=>row.run_id===originalRunId); |
| 185 | assert.equal(original?.status,'cancelled'); |
| 186 | const assistantMessages=restored.messages.filter(row=>row.payload.role==='assistant'); |
| 187 | assert.equal(assistantMessages.length,1); |
| 188 | assert.equal(assistantMessages[0]!.payload.text,'First paragraph.\n\n'); |
| 189 | assert.equal(assistantMessages[0]!.streaming,0); |
| 190 | assert.ok(!restored.stored.some(row=>row.payload.text===heldText)); |
| 191 | const assistantItems=restored.items.filter(row=>row.type==='assistant_message'); |
| 192 | assert.equal(assistantItems.length,1); |
| 193 | assert.equal(assistantItems[0]!.status,'cancelled'); |
| 194 | assert.equal(assistantItems[0]!.payload.text,'First paragraph.\n\n'); |
| 195 | assert.equal(restored.runs.length,1); |
| 196 | fs.writeFileSync(path.join(out,'after-restart-database.json'),JSON.stringify(restored,null,2)); |
| 197 | record('restart-reconciliation-observed',{originalRunStatus:original!.status,runCount:restored.runs.length,messageText:assistantMessages[0]!.payload.text,messageStreaming:assistantMessages[0]!.streaming,turnItemStatus:assistantItems[0]!.status,turnItemStreaming:assistantItems[0]!.payload.streaming,fullHeldTextAbsent:true,continueThreadsAfterServerUpdate:false}); |
| 198 | verdict={status:'passed',head,base:'15fe2832727893cbdd2214383f9f971474849695',machine:'physical corex',node:process.version,provider:'controlled free ACP replay',crashSignal:'SIGKILL',restartSameHome:true,continueThreadsAfterServerUpdate:false,originalRunStatus:'cancelled',persistedPrefixRetained:true,unpersistedTailRecovered:false,threadId}; |
| 199 | } catch(error) { |
| 200 | verdict={status:'failed',head,error:String(error),stack:(error as Error).stack}; |
| 201 | console.error(error); |
| 202 | process.exitCode=1; |
| 203 | } finally { |
| 204 | clearTimeout(deadline); |
| 205 | |
| 206 | db?.close(); |
| 207 | if(server.exitCode===null && server.signalCode===null) { |
| 208 | server.kill('SIGTERM'); |
| 209 | await new Promise<void>(resolve=>server.once('exit',()=>resolve())); |
| 210 | } |
| 211 | failureObserved.resolve();recovered.resolve(); |
| 212 | gate.closeAllConnections(); |
| 213 | await new Promise<void>(resolve=>gate.close(()=>resolve())); |
| 214 | fs.writeFileSync(path.join(out,'verdict.json'),JSON.stringify(verdict,null,2)); |
| 215 | console.log(JSON.stringify(verdict)); |
| 216 | } |
Final-head tests and Android verification
[GPT-6.1-Sol][Codex] on behalf of shivam.
Final verification for head 27c2e13d491a3c0ad19e18f8f41fc2067771b88d, base 15fe2832727893cbdd2214383f9f971474849695.
Macroscope R11 is fixed in 380a8e7967c2ed6dace51e0bfdca71a91f1d0a35. A newer snapshot now wakes an idle timer sleeping on a later retry deadline. Its regression fails an assertion against the preceding timer implementation and passes with the fix. Fresh independent delta review accepted it, with no open in-scope findings. CodeRabbit R12 is fixed in ae9a341c083e0b275f70379dfcb54b69c5f59e91: the original paragraph regression explicitly awaits the full idle delivery before offering later snapshots. The final commit captures narrowed test text before a callback to satisfy TypeScript. Four focused idle/retry cases and the server typecheck passed locally.
All production files are byte-identical to the reviewed and Android-recorded 380a8e7967c2ed6dace51e0bfdca71a91f1d0a35; the two later commits change tests only. The replacement Android video in the PR body comes from that server commit on physical corex's POCO F4, Android 14, T3 Code Dev native fingerprint 541b3da3e5f7b2ad504aaa6303bdcea8d7194d22, isolated state and the free Replay(dev) provider. It shows Send, both paragraphs, the synthetic background command still Running, and Stop available. The main-server video remains valid. The replay fixture was restored and the phone connection, phone lease, dev server and Metro were cleaned up.
Boat job j_5jqg64rhfhvtniyqh3pv, stage typecheck, large worker bx_gtez4ehm, attempt 1, exit 0, pinned to this final head. Command: vp i --frozen-lockfile then vp run check:changed "$FLEET_SOURCE_BASE". Format, lint, knip, GraphQL generation, affected typechecks and all 68 tests passed.
An earlier timer-fix job passed all 68 tests but failed a test-only TypeScript narrowing error. It is not counted as a pass. The queued job for the superseded test commit was cancelled; only the final passing job above covers this final head.
Unverified: a real Antigravity turn, desktop and iOS rendering, and live database failure recovery. Storage-error and timer-race behavior is proved through controlled service tests. The separate wake fix remains https://github.com/pingdotgg/t3code/pull/28204. No merge, auto-merge or approval was performed.
Final check output
| 1 | --- |
| 2 | vp run: 0/3 cache hit (0%), 3/3 successful. @t3tools/scripts#typecheck not cached because it modified its inputs. (Run `vp run --last-details` for full details) |
| 3 | |
| 4 | > vp test (3 files) pass 14.3s |
| 5 | |
| 6 | RUN v5.0.5 <boat-checkout> |
| 7 | |
| 8 | ✓ t3 src/orchestration-v2/RunExecutionService.test.ts (64 tests) 102ms |
| 9 | ✓ t3 src/provider/AgentContextBudget.test.ts (1 test) 61ms |
| 10 | ✓ t3 src/orchestration-v2/assistantStreaming.test.ts (3 tests) 3ms |
| 11 | |
| 12 | Test Files 3 passed (3) |
| 13 | Tests 68 passed (68) |
| 14 | Start at 18:18:41 |
| 15 | Duration 11.59s (transform 68%, import 22%, setup 8%, tests 2%) |
| 16 | |
| 17 | check:changed summary |
| 18 | pass format 0.2s |
| 19 | pass lint 0.4s |
| 20 | pass knip 1/2 5.9s |
| 21 | pass knip 2/2 11.0s |
| 22 | pass graphql (apps/server) 0.7s |
| 23 | pass typecheck (3 packages) 7.0s |
| 24 | pass test (3 files) 14.3s |
| 25 | CI also runs: full test suites, build |
| 26 | unused patch check skipped: no dependency files changed |
| 27 | pass not cached: files changed during the run |