Release line: stable
This minor release lands the Phase 1 post-audit hardening pass: 20 focused PRs addressing 49 findings from the 2026-04-17 master audit, spanning path-guard hardening, OAuth hygiene, routing correctness, storage atomicity, schema discipline, and CLI diagnostics.
- Published package version:
1.3.0 - Previous stable release:
v1.2.7 - Base audit:
docs/audits/MASTER_AUDIT.md(commit range v1.2.4..v1.3.0) - Total PRs merged: 20 (#393 docs, #394-#412 implementation) + 7 post-audit remediation commits + 1 follow-up PR (#413)
- Tests: 3345 → 3527 (+182)
- Semver rationale: minor bump. No breaking public-API changes. One opt-in feature flag (
routingMutex) added with legacy default.
resolvePath()now rejects lookalike-prefix paths (e.g.HomeXvsHome/) usingpath.relative()comparison — blocks a sandbox-escape class (AUDIT-C1 / AUDIT-H1) (PR-A)- OAuth URLs redacted in user-facing login output — prevents token-leak via clipboard or terminal scrollback (AUDIT-H4) (PR-B)
AUTH_REDIRECTsingle source of truth — OAuth callback host unified to127.0.0.1:1455across bind, copy, and HTML; removes 4 duplicate hardcoded sites (AUDIT-H5 / M14 / M30) (PR-C)
- Hybrid selector returns
nullwhen no accounts are available (was returning a stale fallback) (AUDIT-H2) (PR-D) - Short-429 retry now marks the account unavailable BEFORE the retry sleep, closing a TOCTOU window that let two requests race to the same rate-limited account (AUDIT-H3) (PR-E)
- Active-account pointer normalization on disable/remove — prevents a dangling pointer when the active account is administratively removed (AUDIT-H10) (PR-F)
- Routing mutex + SelectionRecord type — opt-in
routingMutex: "enabled" | "legacy"config flag wraps cursor mutation in an async mutex. Default is"legacy"for one release cycle; enable viaCODEX_AUTH_ROUTING_MUTEX=enabled. Four property tests (400 generated cases, fast-check) prove linearizability under concurrent requests (AUDIT-M06 / D-02 / D-09) (PR-N)
- Recovery storage migrated to atomic write + retry-safe delete pattern (AUDIT-M01) (PR-H)
- Account-clear ordering now writes the reset marker BEFORE deletion and retries
EPERMon read (AUDIT-M04 / M05) (PR-I) - Per-project vs CLI-sync config conflict now surfaced to the user instead of silently bypassing project-scoped isolation (AUDIT-M09) (PR-J)
- Zod at JSON.parse boundaries — storage reads are now Zod-validated at 12 sites in
lib/storage/**. NewsafeParseJson<T>(raw, schema, context)helper guards bothSyntaxErrorand schema violations. V3 storage schema is the authoritative normalizer viaAnyAccountStorageSchema. Recovery + request + config Zod migration deferred to follow-up PRs (AUDIT-M20) (PR-L)
- Malformed SSE JSON chunks now surface as structured warnings instead of silent buffer drops. 10MB buffer cap documented. Deprecation/sunset headers logged uniformly across success AND failure paths (AUDIT-H9 / M16 / M18 / M34) (PR-K)
codex auth why-selected [--now|--last] [--json]— new diagnostic command surfacing per-candidate hybrid scoring breakdown (health, tokens, freshness, capability boost, PID bonus, final score) with reason text explaining the selection decision (PR-P)codex auth verify [--paths|--flagged|--all] [--json]— new self-test command that walks the storage path resolution chain and exercisesresolvePath()sandbox with known-good and known-bad inputs. Confirms the PR-A lookalike-prefix fix is live.verify-flaggedkept as back-compat alias (PR-P)lib/codex-manager/settings-hub.ts(808 LOC) split into 5 sub-concern files underlib/codex-manager/settings-hub/:dashboard,backend,experimental,shared,index. Each <500 LOC. Old file kept as a 9-line re-export stub for test compat.lib/AGENTS.mdupdated to retire the stale 2100-LOC claim (AUDIT-M24 / G-01 / JN-03) (PR-M)
getAccountHealth()reads the tracker directly; documents the field-name drift vsManagedAccount.CHANGELOG.mdupdated (AUDIT-M08 / D-04) (PR-O)
pack:check+ tmp hygiene —npm run pack:checknow builds first and tests useos.tmpdir(); 6 straytmp*directories at repo root cleaned up (AUDIT-H7 / M31) (PR-G)- Dual-linter scope documented — ESLint vs Biome boundaries clarified; husky
preparehook side-effect documented (AUDIT-M21 / M22 / M23 partial) (PR-T) - Truthup docs pass —
lib/AGENTS.mdstaleness fixed;docs/reference/storage-paths.mdderiveProjectKeytypo corrected; CHANGELOG drift analysis deferred (AUDIT-H8 / M32 / L04) (PR-Q) - Phase 1 regression suite — locked in audit invariants (PKCE S256, state entropy, SSE failover) (AUDIT-L01 / M03 partial) (PR-S)
- Master audit report —
docs/audits/MASTER_AUDIT.md+docs/audits/evidence/findings-index.jsonpublished (PR #393)
- NEW:
PluginConfig.routingMutex: "enabled" | "legacy"— default"legacy". Env override:CODEX_AUTH_ROUTING_MUTEX. Recommended flip to"enabled"default in v1.4.0 after one release cycle of opt-in use.
Per .sisyphus/notepads/phase1-implementation/f1-defer-notes.md, the following audit findings have explicit defer rationale and are tracked for a follow-up "Audit follow-up" ticket:
- MEDIUM (11): M07 (tracker persistence), M10 (stream failover policy), M11 (callback graceful close), M12 (JWT exp decode), M15 (manual-paste UX), M17 (observability envelope), M26 (--json coverage audit), M27 (codex-cli vs codex-manager boundary docs), M28 (experimental stability policy), M29 (error taxonomy), M33 (semver verification — THIS release)
- LOW (11): L02, L03, L05, L06, L07, L08, L09, L10, L12, L13, L14 — batch-deferred to single janitorial PR
After the 20 PRs landed, a full 7-risk-category audit was run against every PR. Findings: 0 CRITICAL, 13 HIGH (all addressed), 31 MEDIUM (report-only per tier policy), 29 LOW. Per-PR JSON reports at .sisyphus/notepads/phase1-audit/reports/pr###.json. Triage: .sisyphus/notepads/phase1-audit/TRIAGE-SUMMARY.md.
The following 7 fix commits were pushed on top of the original PR heads to close the HIGH findings:
| PR | Fix commit | Addressed |
|---|---|---|
| #412 | 21c466a |
Property tests now use external concurrency observer (proves mutex actually serializes) |
| #401 | f877c85 |
Completed atomic write migration (injectTextPart, prependThinkingPart) + renameSync retry on EBUSY/EPERM |
| #410 | b3c2945 |
verify --paths sandbox probe hardened against cwd=/ edge case + new commands documented |
| #399 | d7af34d |
All-disabled dangle + cursorByFamily divergence + 3 edge-case regression tests |
| #396 | f18d721 |
lib/ui/copy.ts routed through AUTH_REDIRECT SSOT |
| #396 | 475ea37 |
Added AUTH_REDIRECT to codex-manager-cli test mock (follow-up to f18d721) |
| #406 | d9f7253 |
Dual-linter docs corrected to match actual wiring (ESLint-only in lint-staged; Biome manual; CI enforcement confirmed in ci.yml + pr-ci.yml) |
- Pre-existing
removeAccountdangle (lib/accounts/pointer.ts): when the active account is removed and it was the last in its family array, the pointer is set to-1instead of falling back to another remaining account in that family. Out of scope for PR #399 per tier policy (pre-existing). Follow-up PR #413 closes this specific case with its own regression tests.
npm test— 3527 tests pass (was 3345, +182)npm run typecheck— exit 0npm run lint— exit 0npm run audit:ci— 0 vulnerabilitiesnpm run pack:check— 822KB / 968 files (under budget)npm run vendor:verify— 2 components / 8 files verifiednpm run clean:repo:check— greennpm run build— exit 0
Commit range v1.2.4..v1.3.0 audited for silent breaking changes:
- v1.2.4 → v1.2.5: observability wording clarifications — docs only
- v1.2.5 → v1.2.6: forwarded observability fix — patch-appropriate
- v1.2.6 → v1.2.7: native Codex CLI install support — wrapper additive, no removal
- v1.2.7 → v1.3.0: Phase 1 post-audit — MINOR appropriate:
- New public surface:
codex auth why-selected,codex auth verifycommands - New opt-in flag:
routingMutex(default legacy = zero behavior change) - New types exported:
SelectionRecord,HybridSelectionCandidateTrace,HybridSelectionTraceResult,FlaggedAccountStorageV1Schema,AccountsJournalEntrySchema - No removed exports, no changed function signatures, no changed storage format
verify-flaggedpreserved as alias (no breaking removal)
- New public surface:
Conclusion: minor bump is correct; no major bump justified.
- Audit report:
docs/audits/MASTER_AUDIT.md - Audit findings index:
docs/audits/evidence/findings-index.json - Previous release notes:
docs/releases/v1.2.7.md
| PR # | Branch | Title |
|---|---|---|
| #393 | docs/master-repository-audit-2026-04-17 | docs(audit): add master repository audit report and evidence |
| #394 | fix/phase1-tests-green-and-resolvepath | fix(phase1): reject resolvePath lookalike-prefix paths + restore auth list reset message |
| #395 | fix/oauth-url-redaction | fix(auth): redact OAuth URL in user-facing login output |
| #396 | refactor/redirect-uri-ssot | refactor(auth): introduce AUTH_REDIRECT single source of truth |
| #397 | fix/hybrid-selector-null-contract | fix(routing): hybrid selector returns null when no accounts are available |
| #398 | fix/short-429-race | fix(routing): mark account unavailable before short-429 retry sleep |
| #399 | fix/active-pointer-normalization | fix(accounts): normalize active pointer when the active account is disabled |
| #400 | fix/release-hygiene | fix(release): pack:check builds first + tests use os.tmpdir |
| #401 | fix/recovery-atomic-writes | fix(recovery): atomic writes + retry-safe deletes for recovery storage (R6) |
| #402 | fix/storage-clear-ordering | fix(storage): write reset marker before deletions + retry EPERM on read |
| #403 | fix/project-scope-no-silent-bypass | fix(routing): surface the per-project vs CLI-sync config conflict |
| #404 | fix/request-observability | fix(request): surface malformed SSE JSON chunks as structured warnings |
| #405 | docs/truthup | docs: truthup AGENTS.md staleness + deriveProjectKey typo |
| #406 | chore/config-hygiene | chore(config): document dual-linter scope + husky prepare-hook side effect |
| #407 | test/audit-regression-suite | test(audit): add Phase 1 regression suite locking in audit invariants |
| #408 | refactor/health-unify | docs(health): document field-name drift vs ManagedAccount (AUDIT-M08) |
| #409 | refactor/zod-storage-boundaries | refactor(types): add Zod safeParseJson at storage JSON boundaries |
| #410 | feat/cli-why-selected-and-verify-paths | feat(cli): add codex auth why-selected and verify --paths commands |
| #411 | refactor/settings-hub-split | refactor(codex-manager): split settings-hub.ts into 5 sub-concern files |
| #412 | refactor/routing-mutex | refactor(routing): add feature-flagged routing mutex and SelectionRecord |
| #413 | fix/active-pointer-followup | fix(accounts): resolve residual removeAccount dangle (follow-up to #399) |