pi-oracle 0.1.12 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,583 @@
1
+ # pi-oracle design
2
+
3
+ Status: isolated-profile concurrency architecture implemented in code; major live validation now passes, but a few non-blocking hardening items remain.
4
+ Date: 2026-04-03
5
+
6
+ Companion doc:
7
+ - `docs/ORACLE_RECOVERY_DRILL.md` — safe expired-auth recovery validation drill
8
+
9
+ Compatibility target:
10
+ - `pi` 0.65.0+
11
+ - current extension lifecycle only; no backward-compatibility shims for removed `session_switch` / `session_fork` events
12
+
13
+ ## Goal
14
+
15
+ Create a `pi` extension that lets the user or agent consult ChatGPT.com through the web product instead of the API, with:
16
+
17
+ - manual invocation via `/oracle ...`
18
+ - automatic invocation by the agent in rare high-difficulty cases
19
+ - mandatory project-context archive upload (`.tar.zst`)
20
+ - long-running execution in the background
21
+ - durable response/artifact persistence plus best-effort wake-the-agent behavior when the oracle response is ready
22
+ - oracle requires a persisted pi session identity; in-memory/no-session contexts are rejected instead of risking cross-session wake-up misdelivery
23
+ - legacy project-scoped jobs from the older no-session model remain inspectable by project, but are treated as manual/status-only instead of being rebound to a different persisted session for wake-up delivery
24
+ - persisted responses and artifacts under `/tmp`
25
+ - optional same-thread follow-up questions later
26
+
27
+ ## Architecture decision
28
+
29
+ The production architecture is now:
30
+
31
+ - use `agent-browser`
32
+ - do **not** automate the user’s real Chrome in production
33
+ - maintain one authenticated **seed profile** via `/oracle-auth`
34
+ - clone that seed into a **per-job runtime profile** for each oracle run
35
+ - launch each job in its own **runtime browser session**
36
+ - persist same-thread continuity by saved `chatUrl`, not by keeping tabs or browsers alive
37
+ - allow parallel jobs only when they do not target the same ChatGPT conversation
38
+
39
+ ## Rejected production path
40
+
41
+ The old real-Chrome/CDP architecture is rejected for production.
42
+
43
+ Why:
44
+
45
+ - `agent-browser tab new <url>` opens a new tab and selects it
46
+ - `agent-browser tab <index>` switches the active tab
47
+ - upstream `agent-browser` source calls `Page.bringToFront` during tab switching
48
+ - this stole focus in the user’s real environment and disrupted typing
49
+
50
+ That violates a hard requirement.
51
+
52
+ Real-Chrome automation was useful for investigation and earlier smoke tests, but it is no longer the target architecture.
53
+
54
+ ## Current extension surface
55
+
56
+ The extension now follows the current `pi` session lifecycle model:
57
+
58
+ - session transitions are handled from `session_start`
59
+ - previous runtimes are expected to clean up in `session_shutdown`
60
+ - no new logic depends on removed post-transition events
61
+
62
+ ### Prompt template
63
+
64
+ - `/oracle <request>`
65
+ - implemented as a prompt template, not an extension command
66
+ - asks the agent to gather context and dispatch an oracle job
67
+ - intentionally uses native pi prompt/template queueing so submissions survive streaming and compaction
68
+
69
+ ### Commands
70
+
71
+ - `/oracle-auth`
72
+ - syncs ChatGPT cookies from the user’s real Chrome into the isolated oracle profile and verifies them there
73
+ - `/oracle-status [job-id]`
74
+ - shows job status
75
+ - `/oracle-cancel [job-id]`
76
+ - cancels a queued or active job
77
+ - `/oracle-clean <job-id|all>`
78
+ - removes temp files for terminal jobs only
79
+
80
+ ### Tools
81
+
82
+ - `oracle_submit`
83
+ - low-level agent-facing dispatch tool
84
+ - creates archive and launches a detached worker
85
+ - supports optional `followUpJobId` to continue the same ChatGPT thread by persisted URL
86
+ - `oracle_read`
87
+ - reads job status and outputs
88
+ - `oracle_cancel`
89
+ - cancels a queued or active job
90
+
91
+ ## High-level flow
92
+
93
+ ### `/oracle ...`
94
+
95
+ `/oracle <request>` should not directly drive ChatGPT.
96
+ It expands through the prompt-template path so pi can apply its native queueing semantics before the agent starts work.
97
+
98
+ Instead it instructs the agent to:
99
+
100
+ 1. understand the task
101
+ 2. gather repo context
102
+ 3. choose exact archive inputs
103
+ 4. craft the oracle prompt
104
+ 5. call `oracle_submit`
105
+ 6. stop and wait for the completion wake-up (best-effort; durable oracle response/artifact state is already persisted outside session history)
106
+
107
+ ### `/oracle-auth`
108
+
109
+ Auth bootstrap flow:
110
+
111
+ 1. load oracle config
112
+ 2. acquire the global auth-maintenance lock
113
+ 3. read ChatGPT cookies directly from the user’s real Chrome cookie store in read-only mode
114
+ - configurable source profile / cookie DB path
115
+ - no launch or mutation of the real Chrome profile
116
+ 4. validate that `browser.authSeedProfileDir` is an absolute safe path and not inside the real Chrome user-data tree
117
+ 5. create a staged seed-profile path next to the target seed profile
118
+ 6. launch the isolated auth browser headed with:
119
+ - dedicated auth `--session`
120
+ - dedicated staged seed `--profile`
121
+ - configured executable path / user agent / launch args if set
122
+ 7. clear isolated browser cookies and seed the staged profile with imported ChatGPT cookies
123
+ 8. open ChatGPT in the isolated browser
124
+ 9. verify auth with `/backend-api/me` plus ChatGPT UI readiness checks
125
+ 10. on success, close the isolated browser so Chrome flushes profile state cleanly
126
+ 11. atomically swap the staged profile into `browser.authSeedProfileDir`, keeping `*.prev` as rollback
127
+ 12. write a seed-generation marker used by future runtime clones
128
+ 13. if ChatGPT presents a challenge page, leave the staged auth browser/profile open for the user to solve and reuse
129
+
130
+ This keeps production oracle jobs off the user’s real Chrome while using the user’s existing authenticated ChatGPT cookies as the bootstrap source.
131
+
132
+ The authenticated seed profile remains the source of truth for future oracle runtimes.
133
+
134
+ ### `oracle_submit`
135
+
136
+ 1. validate config and model options
137
+ 2. resolve optional `followUpJobId` into a prior `chatUrl` and `conversationId`
138
+ 3. build the archive first into a temporary path
139
+ 4. allocate a unique runtime:
140
+ - `runtimeId`
141
+ - `runtimeSessionName`
142
+ - `runtimeProfileDir`
143
+ 5. under the global admission lock, first promote any older queued jobs that can now run
144
+ 6. if runtime capacity is still available:
145
+ - acquire the runtime lease
146
+ - acquire the conversation lease for follow-up jobs
147
+ - create `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/...` job state as `submitted`
148
+ 7. otherwise create `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/...` job state as `queued`
149
+ 8. move the prepared archive into the job directory with a unique filename
150
+ 9. spawn a detached worker only for submitted jobs
151
+ 10. return immediately
152
+ 11. stop the agent turn until the completion wake-up arrives (best-effort; durable oracle response/artifact state is already persisted outside session history)
153
+
154
+ ### Worker run flow
155
+
156
+ Per job:
157
+
158
+ 1. clone the authenticated seed profile into the job’s `runtimeProfileDir` under the auth lock
159
+ 2. launch a fresh isolated browser with:
160
+ - the job’s `runtimeSessionName`
161
+ - the job’s `runtimeProfileDir`
162
+ - headless by default
163
+ 3. open either:
164
+ - the saved `chatUrl` for follow-up jobs, or
165
+ - the configured default ChatGPT URL
166
+ 4. classify page state before touching the UI
167
+ 5. fail fast on:
168
+ - login required
169
+ - challenge/verification page
170
+ - transient outage after one retry
171
+ 6. configure model family / effort
172
+ 7. upload archive
173
+ 8. wait for upload confirmation scoped to the active composer
174
+ 9. fill prompt
175
+ 10. send
176
+ 11. wait for a stable conversation URL and persist `chatUrl` / `conversationId`
177
+ 12. wait for completion anchored to the current turn only
178
+ 13. persist plain-text response
179
+ 14. download any response-local artifacts directly into the job artifact directory
180
+ 15. close the isolated browser session and delete the runtime profile in `finally`
181
+
182
+ ## Persistence model
183
+
184
+ ### Default auth persistence
185
+
186
+ Default and recommended:
187
+
188
+ - auth seed via `--profile <authSeedProfileDir>` for durable ChatGPT authentication state
189
+ - per-job runtime via unique `--session <runtimeSessionName>` + unique `--profile <runtimeProfileDir>`
190
+
191
+ Not the default:
192
+
193
+ - `--session-name`
194
+ - `state save/load` as the primary auth bootstrap path
195
+
196
+ Reason:
197
+
198
+ `--profile` is the broadest persistence primitive and preserves full browser profile state such as cookies, localStorage, IndexedDB, service workers, cache, and login sessions. The safe concurrent design is therefore:
199
+
200
+ - one persistent authenticated seed profile
201
+ - many disposable runtime profile clones derived from that seed
202
+
203
+ ## Config files
204
+
205
+ Merged config locations:
206
+
207
+ - global: `~/.pi/agent/extensions/oracle.json`
208
+ - project: `.pi/extensions/oracle.json`
209
+
210
+ Project config remains restricted to safe overrides only.
211
+
212
+ Browser/auth settings are global-only because they control local privileged browser state.
213
+
214
+ ### Current config shape
215
+
216
+ ```json
217
+ {
218
+ "defaults": {
219
+ "modelFamily": "pro",
220
+ "effort": "extended",
221
+ "autoSwitchToThinking": false
222
+ },
223
+ "browser": {
224
+ "sessionPrefix": "oracle",
225
+ "authSeedProfileDir": "<absolute path to oracle auth seed profile>",
226
+ "runtimeProfilesDir": "<absolute path to oracle runtime profiles dir>",
227
+ "maxConcurrentJobs": 2,
228
+ "cloneStrategy": "apfs-clone",
229
+ "chatUrl": "https://chatgpt.com/",
230
+ "authUrl": "https://chatgpt.com/auth/login",
231
+ "runMode": "headless",
232
+ "executablePath": "<optional absolute path to Chrome executable>",
233
+ "userAgent": "<optional real-Chrome UA override>",
234
+ "args": ["--disable-blink-features=AutomationControlled"]
235
+ },
236
+ "auth": {
237
+ "pollMs": 1000,
238
+ "bootstrapTimeoutMs": 600000,
239
+ "chromeProfile": "<optional Chrome profile name>",
240
+ "chromeCookiePath": "<optional absolute path to Chrome Cookies DB>"
241
+ },
242
+ "worker": {
243
+ "pollMs": 5000,
244
+ "completionTimeoutMs": 5400000
245
+ },
246
+ "poller": {
247
+ "intervalMs": 5000
248
+ },
249
+ "artifacts": {
250
+ "capture": true
251
+ },
252
+ "cleanup": {
253
+ "completeJobRetentionMs": 1209600000,
254
+ "failedJobRetentionMs": 2592000000
255
+ }
256
+ }
257
+ ```
258
+
259
+ ## Cleanup maintenance model
260
+
261
+ Long-run hygiene is intentionally conservative:
262
+
263
+ - runtime profiles, runtime leases, and conversation leases are cleaned immediately as part of worker/command cleanup paths
264
+ - browser close is time-bounded so cleanup can continue even if `agent-browser close` wedges
265
+ - `/oracle-clean` performs runtime cleanup before removing the persisted job directory, but refuses terminal jobs whose worker is still live or whose wake-up was just sent inside a short post-send retention grace window
266
+ - stale lock directories are swept before reconcile maintenance
267
+ - old auth `.staging-*` profiles are swept during `/oracle-auth` startup when the auth browser session is not still active
268
+ - terminal job directories are retained for inspection, then pruned later based on configurable retention windows
269
+
270
+ Current retention policy is configurable via `cleanup.*`:
271
+
272
+ - `cleanup.completeJobRetentionMs`
273
+ - applies to `complete` and `cancelled` jobs based on terminal-job age; wake-up delivery remains best-effort only, with a short post-send grace so saved response/artifact paths survive the follow-up turn
274
+ - `cleanup.failedJobRetentionMs`
275
+ - applies to `failed` jobs
276
+
277
+ Cleanup warnings are treated as diagnostics, not silent no-ops:
278
+
279
+ - worker cleanup warnings are appended to `logs/worker.log`
280
+ - command-side cleanup warnings are surfaced to the user
281
+ - cancellation/stale-job recovery persists cleanup warnings into `job.json`
282
+ - terminal cleanup recovery will terminate stale live cleanup workers before retrying teardown so blocked capacity does not wedge indefinitely
283
+
284
+ ## Job layout under the configured jobs dir
285
+
286
+ Default location: `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/`
287
+
288
+ ```text
289
+ ${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/
290
+ job.json
291
+ prompt.md
292
+ context-<job-id>.tar.zst
293
+ response.md
294
+ artifacts.json
295
+ artifacts/
296
+ ...downloaded files...
297
+ logs/
298
+ worker.log
299
+ ...diagnostic captures on failure...
300
+ ```
301
+
302
+ ### `job.json` fields
303
+
304
+ Important fields include:
305
+
306
+ - `id`
307
+ - `status`: `queued | preparing | submitted | waiting | complete | failed | cancelled`
308
+ - `phase`: `queued | submitted | cloning_runtime | launching_browser | verifying_auth | configuring_model | uploading_archive | awaiting_response | extracting_response | downloading_artifacts | complete | complete_with_artifact_errors | failed | cancelled`
309
+ - `phaseAt`
310
+ - `createdAt`
311
+ - `queuedAt`
312
+ - `submittedAt`
313
+ - `completedAt`
314
+ - `heartbeatAt`
315
+ - `cwd`
316
+ - `projectId`
317
+ - `sessionId`
318
+ - `originSessionFile`
319
+ - `requestSource`
320
+ - `chatModelFamily`
321
+ - `effort`
322
+ - `autoSwitchToThinking`
323
+ - `followUpToJobId`
324
+ - `chatUrl`
325
+ - `conversationId`
326
+ - `responsePath`
327
+ - `responseFormat` (`text/plain`)
328
+ - `artifactPaths`
329
+ - `artifactsManifestPath`
330
+ - `archivePath`
331
+ - `archiveSha256`
332
+ - `archiveDeletedAfterUpload`
333
+ - `notifiedAt`
334
+ - `notificationEntryId`
335
+ - `notificationSessionKey`
336
+ - `wakeupAttemptCount`
337
+ - `wakeupLastRequestedAt`
338
+ - `wakeupSettledAt`
339
+ - `notifyClaimedAt`
340
+ - `notifyClaimedBy`
341
+ - `artifactFailureCount`
342
+ - `error`
343
+ - `cleanupWarnings`
344
+ - `lastCleanupAt`
345
+ - `workerPid`
346
+ - `workerNonce`
347
+ - `workerStartedAt`
348
+ - `runtimeId`
349
+ - `runtimeSessionName`
350
+ - `runtimeProfileDir`
351
+ - `seedGeneration`
352
+ - `config`
353
+
354
+ ## Response format
355
+
356
+ Canonical oracle response format remains:
357
+
358
+ - `text/plain`
359
+
360
+ The saved file path is currently `response.md` for continuity with earlier job layouts, but the content contract is normalized plain text for agent consumption.
361
+
362
+ ## ChatGPT page-state classifier
363
+
364
+ Before upload/send, the worker classifies ChatGPT as one of:
365
+
366
+ - `authenticated_and_ready`
367
+ - `login_required`
368
+ - `challenge_blocking`
369
+ - `transient_outage_error`
370
+ - `unknown`
371
+
372
+ Signals used:
373
+
374
+ - current URL
375
+ - accessibility snapshot
376
+ - body text
377
+
378
+ ### Ready
379
+
380
+ Require all of:
381
+
382
+ - ChatGPT origin is correct
383
+ - not on `/auth/*`
384
+ - composer exists
385
+ - `Add files and more` exists
386
+ - model selector / selected model control exists
387
+ - no login/challenge/outage signals
388
+
389
+ ### Login required
390
+
391
+ Any of:
392
+
393
+ - URL on `/auth/*`
394
+ - login/provider signals like `Log in`, `Sign up`, `Continue with Google`, etc.
395
+ - logged-out page shape where a composer may exist but required oracle controls do not
396
+ - redirect away from the expected ChatGPT origin
397
+
398
+ ### Challenge blocking
399
+
400
+ Examples:
401
+
402
+ - `Just a moment`
403
+ - `Verify you are human`
404
+ - `Cloudflare`
405
+ - captcha / turnstile markers
406
+ - suspicious or unusual activity messages
407
+
408
+ ### Transient outage
409
+
410
+ Examples:
411
+
412
+ - `Something went wrong`
413
+ - `A network error occurred`
414
+ - websocket error text
415
+ - `Try again later`
416
+
417
+ ## Artifact strategy
418
+
419
+ The artifact path is now direct and browser-local.
420
+
421
+ Use response-local candidate detection exactly as before, but replace browser-download-manager scraping with direct `agent-browser` downloads:
422
+
423
+ - find artifact candidates only in the current assistant response region
424
+ - for each candidate ref:
425
+ - call `agent-browser download <ref> <dest>`
426
+ - write directly into `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/artifacts`
427
+ - compute size / sha256 / detected type
428
+ - append manifest entry
429
+
430
+ This deliberately avoids:
431
+
432
+ - `chrome://downloads`
433
+ - downloads-tab ownership logic
434
+ - browser-global download history heuristics
435
+ - focus-sensitive tab hacks
436
+
437
+ Visible labels are still not trusted as authoritative filenames. They are treated primarily as display metadata.
438
+
439
+ ## Same-thread follow-ups
440
+
441
+ Same-thread continuity is persisted as data, not runtime browser state.
442
+
443
+ Approach:
444
+
445
+ - store `chatUrl` only after the conversation URL stabilizes
446
+ - derive and persist `conversationId` from that URL when possible
447
+ - for a follow-up job, resolve `followUpJobId` to the prior `chatUrl`
448
+ - acquire a conversation lease before launching the follow-up
449
+ - launch a fresh isolated browser using a fresh runtime clone of the auth seed
450
+ - open that URL
451
+ - continue there if authentication and page-state checks pass
452
+
453
+ Do not keep a browser or tab alive between jobs just to preserve thread continuity.
454
+ Do not allow concurrent jobs to target the same `conversationId`.
455
+
456
+ ## Poller / wake-up model
457
+
458
+ The extension still uses the same general `pi`-native background completion pattern, but notification semantics are now explicit:
459
+
460
+ - detached worker writes `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-*` state
461
+ - poller scans jobs on an interval
462
+ - completed job durability lives in oracle job state plus saved response/artifact files, not in synthetic session-history assistant messages
463
+ - when a matching job reaches `complete`, `failed`, or `cancelled`, the poller issues bounded best-effort wake-up reminders to whichever matching session is currently live
464
+ - manual `oracle_read` or `/oracle-status` inspection settles further reminder retries once the terminal job has been opened
465
+ - if no wake-up lands, the job remains available via `/oracle-status`, `oracle_read`, and the saved `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/` response/artifact files
466
+ - because completion delivery is best-effort, pruning uses explicit terminal-job age policy instead of pretending a durable session notification happened
467
+ - recently sent wake-ups keep response/artifact files retained briefly so follow-up turns do not point at deleted paths if cleanup or pruning races with delivery
468
+
469
+ ## What was removed by this pivot
470
+
471
+ The isolated-profile design deletes or supersedes the old real-Chrome-specific machinery:
472
+
473
+ - CDP attach/verification to port `9222`
474
+ - `cdpVerified` / `cdpUrl` job state
475
+ - dedicated oracle tab parking/reuse in the user’s browser
476
+ - wrong-tab drift handling
477
+ - selected-tab / tab-index tracking
478
+ - temporary `chrome://downloads` tabs
479
+ - browser download-manager scraping via `downloads-manager.items_`
480
+ - copy-from-`~/Downloads` artifact recovery flow
481
+
482
+ ## Current implementation status
483
+
484
+ Implemented in code for the pivot and concurrency redesign:
485
+
486
+ - config now uses `browser.*` + `auth.*`
487
+ - `/oracle-auth` now syncs real-Chrome ChatGPT cookies into the authenticated seed profile instead of opening a manual-login browser
488
+ - `oracle_submit` supports follow-ups via persisted `chatUrl`
489
+ - job state no longer stores CDP verification fields
490
+ - workers now run with per-job runtime sessions and per-job runtime profile clones
491
+ - runtime admission is controlled by runtime leases and `browser.maxConcurrentJobs`
492
+ - queued jobs are workerless and do not consume runtime or conversation leases until promotion
493
+ - follow-up jobs now acquire conversation leases
494
+ - persisted job state now records explicit lifecycle phases instead of relying only on coarse statuses
495
+ - poller notifications now use per-job notification claims rather than broad global scan serialization
496
+ - worker now uses a structured ChatGPT page-state classifier
497
+ - worker now downloads artifacts directly with `agent-browser download <ref> <dest>`
498
+ - poller scans are now best-effort/non-fatal with per-session in-flight guards
499
+ - worker heartbeats during artifact downloads, writes artifact manifests incrementally, and reopens the saved conversation before artifact capture/download
500
+ - artifact-only responses are treated as valid completion content
501
+ - the repo now includes a repeatable sanity harness: `npm run sanity:oracle`
502
+ - the repo now includes a safe expired-auth recovery drill: `docs/ORACLE_RECOVERY_DRILL.md`
503
+ - worker closes the isolated browser, removes the runtime profile, and releases leases in `finally`
504
+
505
+ Retained from the earlier MVP:
506
+
507
+ - `/oracle`, `/oracle-status`, `/oracle-cancel`, `/oracle-clean`
508
+ - `oracle_submit`, `oracle_read`, `oracle_cancel`
509
+ - detached background worker model
510
+ - `${PI_ORACLE_JOBS_DIR:-/tmp}/oracle-<job-id>/...` state layout
511
+ - shell-safe archive creation using `tar` piped to `zstd`
512
+ - private permissions and atomic writes
513
+ - stale-worker reconciliation
514
+ - upload ordering: attach → confirm → fill → send
515
+ - current-turn response anchoring
516
+ - plain-text canonical response extraction
517
+ - wake-the-agent poller integration
518
+ - unique archive filenames per job
519
+ - worker PID identity checks using recorded process start time
520
+ - composer-scoped upload confirmation
521
+ - stable `chatUrl` capture after send
522
+ - redacted `oracle_read` details and same-project job scoping
523
+ - serialized poller scans
524
+
525
+ ## Live validation status
526
+
527
+ Live-validated after the concurrency redesign:
528
+
529
+ - `/oracle-auth` happy path still works against the seed profile
530
+ - headless normal oracle runs still work using per-job runtime clones
531
+ - two concurrent runs in different projects work with isolated runtimes
532
+ - two concurrent runs in the same project but different `pi` sessions work when they target different conversations
533
+ - same-conversation concurrent follow-up rejection works and fails fast with a clear lease error
534
+ - runtime profile cleanup works on completion and cancellation
535
+ - runtime/conversation lease cleanup works on completion and cancellation
536
+ - global browser args overrides (for example `--disable-gpu`) apply to real jobs
537
+ - artifact-producing runs work with direct `download <ref> <dest>`
538
+ - multi-artifact runs complete, target the correct `pi` session, and persist both downloaded files with correct contents
539
+ - the poller no longer needs the worker to stay alive just to observe completion for artifact-producing runs
540
+ - expired/missing auth now fails as a clean auth-related error instead of generic UI/config drift
541
+ - `/oracle-auth` repairs the seed profile and a post-repair probe succeeds again
542
+ - live auth recovery also exposed and corrected a real source-profile misconfiguration during validation; the configured Chrome profile must actually contain the active ChatGPT session cookies
543
+
544
+ ## Known remaining work
545
+
546
+ Still to verify live after this pivot:
547
+
548
+ - model-selection verification against the current ChatGPT UI under additional real-world variation
549
+ - optional richer terminal semantics for partial artifact failure (`complete_with_artifact_errors`) in more live scenarios
550
+
551
+ ## Production readiness criteria
552
+
553
+ This architecture is now live-validated for the core release path:
554
+
555
+ - no interaction with the user’s real Chrome during normal jobs
556
+ - no focus disruption during normal jobs
557
+ - the seed profile survives browser restarts and can be cloned into runtime profiles repeatedly
558
+ - different projects / sessions can run in parallel without co-mingled data
559
+ - same-conversation follow-ups are rejected while another job owns that conversation lease
560
+ - artifact capture works without `chrome://downloads`
561
+ - artifact-only responses and multi-artifact responses both complete correctly
562
+ - same-thread follow-ups reopen correctly from persisted `chatUrl`
563
+ - failure modes are clearly classified as auth / challenge / outage / UI drift
564
+ - expired/missing auth now fails cleanly, `/oracle-auth` repairs the seed profile, and the post-repair probe succeeds again
565
+
566
+ ### Current readiness summary
567
+
568
+ Current release blockers for the validated scope:
569
+ - none currently known
570
+
571
+ Remaining non-blocking hardening work:
572
+ - broaden live proof of the new lifecycle/state-machine model across more degraded paths
573
+ - broaden live proof of notification-claim semantics under more concurrent completions
574
+ - extend regression-harness coverage for browser/download failure classes
575
+ - polish partial-artifact terminal semantics (`complete_with_artifact_errors`)
576
+ - keep hardening model-selection verification against future ChatGPT UI variation
577
+
578
+ Recent proof points:
579
+ - expired-auth drill fail path: `a2460bc1-7d89-4041-b67d-39680d310325`
580
+ - `/oracle-auth` repair evidence: `/tmp/oracle-auth.log`
581
+ - expired-auth drill post-repair success: `fa26a2a7-0057-4a21-b3e0-71c1d020facf`
582
+ - successful multi-artifact completion: `b6b3599c-6b91-4315-adfa-8a83aa5eda9b`
583
+ - repo-owned sanity harness: `npm run sanity:oracle`