* fix(codex): stop rebuilding shared Codex state from a read that failed (STA-4823)
Six shared files were rebuilt, erased or reported healthy after a read that had
only failed. Batch A of the STA-4606 split: every one of these is reachable and
testable on the host lane, so none of them wait on the WSL work.
- `config-toml-trust.ts` upsertHookTrustEntries: `existsSync` reported a locked
config.toml as absent, so the base content became '' and the upsert rewrote
the file from the trust entries alone — a trust-only stub, with the user's
model, provider, MCP servers, approvals and comments gone. It refuses now;
every hook-service caller already turns that into "trust entries could not be
written. Run /hooks in Codex to approve."
- `codex-trust-grant-ledger.ts`: an unreadable ledger degraded to empty and the
next write persisted a file holding only the home being written, dropping
every other home's grants. The write paths refuse; the read path still
degrades, and a corrupt ledger is still rebuilt.
- `codex-pane-account-registry.ts`: an unreadable registry erased every pane's
attribution AND cached that erasure, so it survived the file recovering. The
failure is no longer cached, and both write sites refuse rather than persist a
registry derived from an empty stand-in.
- `hooks-json-read.ts`: the read arm already separated "no hooks" from "could
not read", but the `existsSync` arm in front of it returned a valid empty
config for a file that could not be opened. One read now classifies both.
- `config-settings-baseline.ts`: absent, unparseable and unreadable all collapsed
into `null`, so the snapshot rebuilt a baseline it could not read — recording
an in-Codex edit as Orca's own write, after which promotion skips it forever.
- `config-sync-stall.ts`: an unreadable runtime config read as absent and the
status reported `synced` while the mirror was refusing. It reports
`managed-home-unavailable`, the existing reason for exactly this, rather than
borrowing a source-side one and blaming the wrong path.
Absent and malformed still rebuild throughout — resetting corrupt state is the
intent, and conflating it with unreadable would wedge a user on a broken file.
* fix(codex): close shared state read-denial gaps
* fix(codex): recover oversized settings baselines
* fix(codex): name the stalled managed config
* fix(codex): preserve hooks after failed source reads
* test(codex): correct what the denyExistence rig actually models
MEASURED on both platforms: a file-permission denial leaves existsSync TRUE
and fails only the content read — chmod 000 gives EACCES on macOS, icacls
/deny (R) gives EPERM errno -4048 on Windows, with stat/lstat succeeding in
both. The rig's docblock claimed this mode modelled that denial. It does not.
What it models is the UNC / \\wsl$ transport, where an unreachable distro
reports errno UNKNOWN at every level and existsSync folds it to false.
The distinction decides what the D29 guard is worth: under a permission denial
the pre-fix code already failed safe, because existsSync was true so it took
the read branch and threw. Only a transport that lies about existence reaches
the rebuild-from-empty path. No behaviour change; the comment was wrong, not
the code.
* test(codex): exercise live baseline read denial
* fix(codex): retry pane attribution writes
* fix(codex): retry reconciliation registry writes
* fix(codex): report unreadable sync baselines