Context
Each Codex Desktop session that runs Playwright unit tests touching .neo-ai-data/sqlite hits SqliteError: unable to open database file at the test setup step (12 tests fail at new Database(DB_PATH) before any assertion runs). @neo-gpt resolves the friction manually each session by re-running with escalated sandbox permissions, but the muscle-memory pattern repeats per fresh-context session boot. Empirical anchor: this session's PR #10713 review on bridge-daemon spec — sandboxed run failed all 12 tests at SQLite open; escalated host run passed 12/12 at ~11.2s.
This is structurally the same shape as the Claude Code worktree bootstrap problem solved by ai/scripts/bootstrapWorktree.mjs (#10095 / #10591): per-harness session-init substrate that should be one script invocation, not muscle memory re-derived each session.
The target shape is diagnostic-first (not auto-fix): the sandbox permission model is the user's choice, but a clear early probe + actionable error message removes the cost of pattern-matching 12 pathless SQLite failures back to "oh, escalation."
The Problem
Codex Desktop's sandbox runs in sandbox_permissions=require_escalated mode by default. SQLite file creation through the symlinked .neo-ai-data/sqlite/ path is blocked in that mode. Playwright's test-runner doesn't surface a useful diagnostic — the failure is a generic SqliteError: unable to open database file repeated 12 times, with no path, no symlink-target context, and no recommendation.
Empirical pain points (per @neo-gpt's PR #10713 review session, 2026-05-04):
- 12 generic failures instead of one probe + clear remediation message.
- No path printed — the failing absolute path (
/Users/Shared/codex/neomjs/neo/.neo-ai-data/sqlite/test-daemon-<id>.sqlite) and its symlink target (/Users/Shared/github/neomjs/neo/.neo-ai-data/sqlite/...) are invisible until the agent grep-traces the spec.
- No escalation guidance — the agent must independently know Codex's sandbox permission model and the
require_escalated workaround.
- Per-session rediscovery — every fresh context loses the muscle memory; the cycle repeats.
The Architectural Reality
ai/scripts/bootstrapWorktree.mjs — existing precedent. Resolves Claude Code's gitignored-config + .neo-ai-data/ substrate gaps via a single CLI invocation. Symmetric concern for Codex.
test/playwright/unit/ai/scripts/bridge-daemon.spec.mjs:27 — failure site. The spec sets DB_PATH = '.neo-ai-data/sqlite/test-daemon-${testId}.sqlite' and calls new Database(DB_PATH). The fs.ensureDirSync(path.dirname(DB_PATH)) call before it succeeds; the file creation is what fails under sandbox.
- Symlink topology in Codex's clone (per @neo-gpt empirical observation):
/Users/Shared/codex/neomjs/neo/.neo-ai-data/sqlite -> /Users/Shared/github/neomjs/neo/.neo-ai-data/sqlite. The sandbox blocks write through the symlink target.
- Repro scope (empirically observed): Playwright unit tests that create transient SQLite files under
.neo-ai-data/sqlite AND spawn the bridge-daemon subprocess. Pure unit specs not touching .neo-ai-data/sqlite are not in scope per current evidence.
The Fix
Add a new ai/scripts/bootstrapCodexSandbox.mjs (or generic bootstrapHarness.mjs that dispatches by detected harness) sibling to bootstrapWorktree.mjs. The script:
- Probes sandbox SQLite reachability: create + open + delete a transient SQLite file at
.neo-ai-data/sqlite/probe-<uuid>.sqlite. Same path shape as the bridge-daemon spec uses, so the probe matches the actual test-runner failure surface.
- Resolves symlink topology: if
.neo-ai-data/sqlite is a symlink, log the canonical target. Helps the agent diagnose cross-clone reachability.
- On probe success: emit
✓ Codex sandbox SQLite probe: ok (or equivalent) and exit 0. Tests proceed normally.
- On probe failure: emit a structured diagnostic — failing logical path, physical symlink target, sandbox permission mode if detectable, and the canonical remediation step ("re-run with escalated permissions" or "remove symlink + use local path"). Exit non-zero.
The agent (or human user) sees ONE clear failure with actionable guidance, not 12 pathless repetitions.
Acceptance Criteria
Out of Scope
- Auto-fixing the sandbox permission model. The escalation choice is the user's; this ticket is diagnostic-only. A separate ticket can pursue auto-escalation if warranted.
- Generalizing to all harnesses' sandbox models. Non-Codex harnesses (Claude Code, Antigravity) have different sandbox semantics and aren't covered here. The generic
bootstrapHarness.mjs framing is forward-compatible but this ticket's scope is Codex SQLite probe only.
- Removing the
.neo-ai-data/sqlite symlink entirely from Codex's clone. That would diverge from the cross-clone substrate unification pattern in bootstrapWorktree.mjs. Probe-and-diagnose preserves the symlink while making its failure mode actionable.
- Unifying with
bootstrapWorktree.mjs immediately. Initial shape can be sibling script; merging into a generic dispatcher is a follow-up if multiple harness-bootstrap scripts emerge.
Avoided Traps / Gold Standards Rejected
- Rejected: auto-elevate sandbox permissions. The user's permission model is intentional; auto-bypassing it would violate operator trust. Diagnostic-first is the correct shape.
- Rejected: catch-and-rethrow inside Playwright spec setup. Adding error handlers to every
.spec.mjs would scatter the diagnostic concern across many files. Centralized probe-first is the right substrate.
- Rejected: probe inside
bootstrapWorktree.mjs. That script's responsibility is gitignored-config + symlink setup for Claude Code worktrees. Codex sandbox probe is a different concern (different harness, different failure mode); coupling them would dilute both.
Related
Empirical Anchor Source
@neo-gpt supplied the exact error path, symlink topology, and require_escalated sandbox permission model context via A2A on 2026-05-04 during PR #10713 cross-family review. This ticket would not exist without his per-session manual diagnosis being re-derivable into a substrate-level fix.
Origin Session ID: 7e52099b-9632-4c67-a2a1-4e1a1ad1c414
Retrieval Hint: query_raw_memories(query="Codex sandbox SQLite require_escalated bootstrap probe diagnostic harness session bootstrap symmetry bootstrapWorktree")
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
ai/scripts/diagnostics/bootstrapCodexSandbox.mjs (new) |
#10714 + structural fast-path: diagnostics sibling pattern under ai/scripts/diagnostics/ |
Probe .neo-ai-data/sqlite by creating, opening, closing, and deleting a transient SQLite file with the same path shape as affected Codex unit-test runs. Success exits 0 with a concise confirmation. |
Failure exits non-zero and prints logical path, resolved physical path / symlink target when present, SQLite error code/message, and explicit Codex sandbox remediation guidance. Cleanup is attempted even after open failure. |
Script JSDoc usage block. |
Unit tests with injected success/failure paths; manual script run in the current checkout. |
package.json script ai:bootstrap-codex-sandbox |
#10714 AC7 + existing ai:* script namespace |
Exposes the diagnostic as an explicit opt-in command. |
Does not hook into test-unit; agents run it when Codex sandbox SQLite access is in scope, avoiding routine test cost. |
AGENTS_STARTUP.md invocation guidance. |
Script lookup plus unit/command validation. |
AGENTS_STARTUP.md Codex sandbox guidance |
#10714 AC6 + live AGENTS_STARTUP.md worktree-bootstrap section |
Documents when Codex agents should run the probe and how to interpret success/failure. Also keeps worktree-bootstrap path aligned to the live ai/scripts/migrations/bootstrapWorktree.mjs surface. |
Keeps Claude/Antigravity worktree bootstrap distinct from Codex sandbox diagnostics; no auto-escalation. |
AGENTS_STARTUP.md. |
Doc diff plus targeted grep for command/path. |
Context
Each Codex Desktop session that runs Playwright unit tests touching
.neo-ai-data/sqlitehitsSqliteError: unable to open database fileat the test setup step (12 tests fail atnew Database(DB_PATH)before any assertion runs). @neo-gpt resolves the friction manually each session by re-running with escalated sandbox permissions, but the muscle-memory pattern repeats per fresh-context session boot. Empirical anchor: this session's PR #10713 review on bridge-daemon spec — sandboxed run failed all 12 tests at SQLite open; escalated host run passed 12/12 at ~11.2s.This is structurally the same shape as the Claude Code worktree bootstrap problem solved by
ai/scripts/bootstrapWorktree.mjs(#10095 / #10591): per-harness session-init substrate that should be one script invocation, not muscle memory re-derived each session.The target shape is diagnostic-first (not auto-fix): the sandbox permission model is the user's choice, but a clear early probe + actionable error message removes the cost of pattern-matching 12 pathless SQLite failures back to "oh, escalation."
The Problem
Codex Desktop's sandbox runs in
sandbox_permissions=require_escalatedmode by default. SQLite file creation through the symlinked.neo-ai-data/sqlite/path is blocked in that mode. Playwright's test-runner doesn't surface a useful diagnostic — the failure is a genericSqliteError: unable to open database filerepeated 12 times, with no path, no symlink-target context, and no recommendation.Empirical pain points (per @neo-gpt's PR #10713 review session, 2026-05-04):
/Users/Shared/codex/neomjs/neo/.neo-ai-data/sqlite/test-daemon-<id>.sqlite) and its symlink target (/Users/Shared/github/neomjs/neo/.neo-ai-data/sqlite/...) are invisible until the agent grep-traces the spec.require_escalatedworkaround.The Architectural Reality
ai/scripts/bootstrapWorktree.mjs— existing precedent. Resolves Claude Code's gitignored-config +.neo-ai-data/substrate gaps via a single CLI invocation. Symmetric concern for Codex.test/playwright/unit/ai/scripts/bridge-daemon.spec.mjs:27— failure site. The spec setsDB_PATH = '.neo-ai-data/sqlite/test-daemon-${testId}.sqlite'and callsnew Database(DB_PATH). Thefs.ensureDirSync(path.dirname(DB_PATH))call before it succeeds; the file creation is what fails under sandbox./Users/Shared/codex/neomjs/neo/.neo-ai-data/sqlite -> /Users/Shared/github/neomjs/neo/.neo-ai-data/sqlite. The sandbox blocks write through the symlink target..neo-ai-data/sqliteAND spawn the bridge-daemon subprocess. Pure unit specs not touching.neo-ai-data/sqliteare not in scope per current evidence.The Fix
Add a new
ai/scripts/bootstrapCodexSandbox.mjs(or genericbootstrapHarness.mjsthat dispatches by detected harness) sibling tobootstrapWorktree.mjs. The script:.neo-ai-data/sqlite/probe-<uuid>.sqlite. Same path shape as the bridge-daemon spec uses, so the probe matches the actual test-runner failure surface..neo-ai-data/sqliteis a symlink, log the canonical target. Helps the agent diagnose cross-clone reachability.✓ Codex sandbox SQLite probe: ok(or equivalent) and exit 0. Tests proceed normally.The agent (or human user) sees ONE clear failure with actionable guidance, not 12 pathless repetitions.
Acceptance Criteria
ai/scripts/bootstrapCodexSandbox.mjs(or unifiedbootstrapHarness.mjs) exists..neo-ai-data/sqlite/write-reachability via a transient SQLite create+open+delete cycle.AGENTS_STARTUP.md §6(or equivalent harness-bootstrap section) as a Codex-side prerequisite alongside the existingbootstrapWorktree.mjsfor Claude Code.npm run test-unit:probe-firstor invoked automatically bytest-unitscript).Out of Scope
bootstrapHarness.mjsframing is forward-compatible but this ticket's scope is Codex SQLite probe only..neo-ai-data/sqlitesymlink entirely from Codex's clone. That would diverge from the cross-clone substrate unification pattern inbootstrapWorktree.mjs. Probe-and-diagnose preserves the symlink while making its failure mode actionable.bootstrapWorktree.mjsimmediately. Initial shape can be sibling script; merging into a generic dispatcher is a follow-up if multiple harness-bootstrap scripts emerge.Avoided Traps / Gold Standards Rejected
.spec.mjswould scatter the diagnostic concern across many files. Centralized probe-first is the right substrate.bootstrapWorktree.mjs. That script's responsibility is gitignored-config + symlink setup for Claude Code worktrees. Codex sandbox probe is a different concern (different harness, different failure mode); coupling them would dilute both.Related
ai/scripts/bootstrapWorktree.mjs(#10095, #10591).test/playwright/unit/ai/scripts/bridge-daemon.spec.mjs:27and any spec using the same.neo-ai-data/sqlitetest-DB pattern.Empirical Anchor Source
@neo-gpt supplied the exact error path, symlink topology, and
require_escalatedsandbox permission model context via A2A on 2026-05-04 during PR #10713 cross-family review. This ticket would not exist without his per-session manual diagnosis being re-derivable into a substrate-level fix.Origin Session ID:
7e52099b-9632-4c67-a2a1-4e1a1ad1c414Retrieval Hint:
query_raw_memories(query="Codex sandbox SQLite require_escalated bootstrap probe diagnostic harness session bootstrap symmetry bootstrapWorktree")Contract Ledger
ai/scripts/diagnostics/bootstrapCodexSandbox.mjs(new)ai/scripts/diagnostics/.neo-ai-data/sqliteby creating, opening, closing, and deleting a transient SQLite file with the same path shape as affected Codex unit-test runs. Success exits 0 with a concise confirmation.package.jsonscriptai:bootstrap-codex-sandboxai:*script namespacetest-unit; agents run it when Codex sandbox SQLite access is in scope, avoiding routine test cost.AGENTS_STARTUP.mdinvocation guidance.AGENTS_STARTUP.mdCodex sandbox guidanceAGENTS_STARTUP.mdworktree-bootstrap sectionai/scripts/migrations/bootstrapWorktree.mjssurface.AGENTS_STARTUP.md.