Skip to content

Commit 8b6b27a

Browse files
committed
chore(release): prepare v1.3.0
- Move release notes from draft to docs/releases/v1.3.0.md - Bump version 1.2.7 -> 1.3.0 - Update README release-notes pointer (if applicable) - Update CHANGELOG.md (if present) Full post-audit Phase 1: 20 PRs + follow-up #413 + 7 audit fixes. Staging-merge validated: 3527 tests green, 8-check battery passing.
1 parent f4d9bd3 commit 8b6b27a

5 files changed

Lines changed: 184 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,42 @@ This top-level changelog preserves the foundational `0.x` milestones and points
99

1010
## [Unreleased]
1111

12+
## [1.3.0] - 2026-04-17
13+
14+
Phase 1 post-audit hardening: 20 focused PRs + 7 audit-fix commits + 1 follow-up PR (#413). 3527 tests (+182 from v1.2.7). Zero breaking changes, one opt-in flag (`routingMutex`). See [docs/releases/v1.3.0.md](docs/releases/v1.3.0.md) for full details.
15+
1216
### Added
1317

1418
- `routingMutex` plugin config flag (PR-N / R4) with values `"enabled" | "legacy"` (default `"legacy"`). When `"enabled"`, cursor-mutation sites in the account pool (`markSwitchedLocked`, `markAccountCoolingDownLocked`, `setActiveIndexLocked`) are serialized through a promise-chain async mutex in `lib/routing-mutex.ts`, closing the TOCTOU race described in design items D-02/D-09. The flag defaults to `"legacy"` for one full release cycle so existing deployments see zero behavior change; users can opt in via settings or the `CODEX_AUTH_ROUTING_MUTEX=enabled` environment variable. A new `SelectionRecord` type is threaded out of the rotation decision path so the fetch loop can hand structured selection metadata to observability, why-selected, and failure-policy consumers.
19+
- `codex auth why-selected [--now|--last] [--json]` diagnostic command surfacing per-candidate hybrid scoring breakdown (PR-P).
20+
- `codex auth verify [--paths|--flagged|--all] [--json]` self-test command walking the storage path resolution chain and exercising the `resolvePath()` sandbox (PR-P). `verify-flagged` retained as back-compat alias.
21+
- Zod `safeParseJson<T>(raw, schema, context)` helper; 12 storage-read sites migrated to schema-validated JSON parsing with `AnyAccountStorageSchema` as authoritative normalizer (PR-L / AUDIT-M20).
22+
- New types exported: `SelectionRecord`, `HybridSelectionCandidateTrace`, `HybridSelectionTraceResult`, `FlaggedAccountStorageV1Schema`, `AccountsJournalEntrySchema`.
23+
- `docs/audits/MASTER_AUDIT.md` + `docs/audits/evidence/findings-index.json` published (PR #393).
24+
- Phase 1 regression suite locking in audit invariants (PKCE S256, state entropy, SSE failover) (PR-S / AUDIT-L01).
25+
26+
### Changed
27+
28+
- `resolvePath()` now rejects lookalike-prefix paths (e.g. `HomeX` vs `Home/`) via `path.relative()` comparison, closing a sandbox-escape class (PR-A / AUDIT-C1 / AUDIT-H1).
29+
- OAuth URLs redacted in user-facing login output to prevent token leakage through clipboard or terminal scrollback (PR-B / AUDIT-H4).
30+
- OAuth callback host unified through `AUTH_REDIRECT` SSOT (`127.0.0.1:1455`) across bind, copy, and HTML; 4 duplicate hardcoded sites removed (PR-C / AUDIT-H5 / M14 / M30).
31+
- Hybrid selector now returns `null` when no accounts are available instead of a stale fallback (PR-D / AUDIT-H2).
32+
- Short-429 retry marks the account unavailable BEFORE the retry sleep, closing a TOCTOU race between two requests targeting the same rate-limited account (PR-E / AUDIT-H3).
33+
- Active-account pointer normalized on disable/remove; residual `removeAccount` last-in-family dangle resolved in follow-up #413 (PR-F / #413 / AUDIT-H10).
34+
- Recovery storage migrated to atomic write + retry-safe delete pattern; atomic write migration completed for `injectTextPart` / `prependThinkingPart`; `renameSync` retries on `EBUSY`/`EPERM` (PR-H / audit-fix `f877c85` / AUDIT-M01).
35+
- Account-clear ordering writes the reset marker BEFORE deletion and retries `EPERM` on read (PR-I / AUDIT-M04 / M05).
36+
- Per-project vs CLI-sync config conflict surfaced to the user instead of silently bypassing project-scoped isolation (PR-J / AUDIT-M09).
37+
- Malformed SSE JSON chunks surface as structured warnings instead of silent buffer drops; 10MB buffer cap documented; deprecation/sunset headers logged uniformly across success and failure paths (PR-K / AUDIT-H9 / M16 / M18 / M34).
38+
- `lib/codex-manager/settings-hub.ts` (808 LOC) split into 5 focused sub-modules under `lib/codex-manager/settings-hub/` (`dashboard`, `backend`, `experimental`, `shared`, `index`), each <500 LOC; original file retained as a 9-line re-export stub for test compatibility (PR-M / AUDIT-M24 / G-01 / JN-03).
39+
- `getAccountHealth()` now reads the tracker directly; field-name drift vs `ManagedAccount` documented (PR-O / AUDIT-M08 / D-04).
40+
- `npm run pack:check` builds first; tests migrated to `os.tmpdir()`; 6 stray `tmp*` directories removed from repo root (PR-G / AUDIT-H7 / M31).
41+
- Dual-linter scope documented: ESLint in lint-staged, Biome manual, CI enforcement via `ci.yml` + `pr-ci.yml`; husky `prepare` hook side effect documented (PR-T / audit-fix `d9f7253` / AUDIT-M21 / M22 / M23 partial).
42+
- `lib/AGENTS.md` staleness fixed; `docs/reference/storage-paths.md` `deriveProjectKey` typo corrected (PR-Q / AUDIT-H8 / M32 / L04).
1543

1644
### Rollout plan
1745

18-
- Release N: flag shipped with default `"legacy"`. Advanced users opt in via config or env.
19-
- Release N+1: evaluate enablement based on telemetry and flip default to `"enabled"`.
46+
- v1.3.0: `routingMutex` shipped with default `"legacy"`. Advanced users opt in via config or env.
47+
- v1.4.0: evaluate enablement based on telemetry and flip default to `"enabled"`.
2048

2149
## [0.1.8] - 2026-03-11
2250

@@ -192,3 +220,4 @@ Historical entries from pre-`0.1.0` internal iteration cycles are preserved in:
192220
[0.1.5]: https://github.com/ndycode/codex-multi-auth/releases/tag/v0.1.5
193221
[0.1.6]: https://github.com/ndycode/codex-multi-auth/releases/tag/v0.1.6
194222
[0.1.7]: https://github.com/ndycode/codex-multi-auth/releases/tag/v0.1.7
223+
[1.3.0]: https://github.com/ndycode/codex-multi-auth/releases/tag/v1.3.0

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ codex auth doctor --json
333333

334334
## Release Notes
335335

336-
- Current stable: [docs/releases/v1.2.7.md](docs/releases/v1.2.7.md)
336+
- Current stable: [docs/releases/v1.3.0.md](docs/releases/v1.3.0.md)
337337
- Previous stable: [docs/releases/v1.2.6.md](docs/releases/v1.2.6.md)
338338
- Earlier stable: [docs/releases/v1.2.5.md](docs/releases/v1.2.5.md)
339339
- Full release archive: [docs/README.md#release-history](docs/README.md#release-history)

docs/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ Public documentation for `codex-multi-auth`.
3030

3131
| Document | Focus |
3232
| --- | --- |
33-
| [releases/v1.2.7.md](releases/v1.2.7.md) | Current stable release notes |
33+
| [releases/v1.3.0.md](releases/v1.3.0.md) | Current stable release notes |
34+
| [releases/v1.2.7.md](releases/v1.2.7.md) | Prior stable release notes |
3435
| [releases/v1.2.6.md](releases/v1.2.6.md) | Previous stable release notes |
3536
| [releases/v1.2.5.md](releases/v1.2.5.md) | Earlier stable release notes |
3637
| [releases/v1.2.4.md](releases/v1.2.4.md) | Stable archive |
@@ -63,7 +64,7 @@ Public documentation for `codex-multi-auth`.
6364
| [reference/storage-paths.md](reference/storage-paths.md) | Canonical and compatibility storage paths |
6465
| [reference/public-api.md](reference/public-api.md) | Public API stability and semver contract |
6566
| [reference/error-contracts.md](reference/error-contracts.md) | CLI, JSON, and helper error semantics |
66-
| [releases/v1.2.7.md](releases/v1.2.7.md) | Current stable release notes |
67+
| [releases/v1.3.0.md](releases/v1.3.0.md) | Current stable release notes |
6768
| [releases/v0.1.0-beta.0.md](releases/v0.1.0-beta.0.md) | Archived prerelease reference |
6869
| [Release history](#release-history) | Stable, previous, and archived release notes |
6970

docs/releases/v1.3.0.md

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Release v1.3.0
2+
3+
Release line: `stable`
4+
5+
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.
6+
7+
## Scope
8+
9+
- Published package version: `1.3.0`
10+
- Previous stable release: `v1.2.7`
11+
- Base audit: `docs/audits/MASTER_AUDIT.md` (commit range v1.2.4..v1.3.0)
12+
- Total PRs merged: 20 (#393 docs, #394-#412 implementation) + 7 post-audit remediation commits + 1 follow-up PR (#413)
13+
- Tests: 3345 → 3527 (+182)
14+
- Semver rationale: minor bump. No breaking public-API changes. One opt-in feature flag (`routingMutex`) added with legacy default.
15+
16+
## What Changed
17+
18+
### Security & Path Hardening
19+
20+
- **`resolvePath()`** now rejects lookalike-prefix paths (e.g. `HomeX` vs `Home/`) using `path.relative()` comparison — blocks a sandbox-escape class (AUDIT-C1 / AUDIT-H1) (PR-A)
21+
- **OAuth URLs redacted** in user-facing login output — prevents token-leak via clipboard or terminal scrollback (AUDIT-H4) (PR-B)
22+
- **`AUTH_REDIRECT` single source of truth** — OAuth callback host unified to `127.0.0.1:1455` across bind, copy, and HTML; removes 4 duplicate hardcoded sites (AUDIT-H5 / M14 / M30) (PR-C)
23+
24+
### Routing & Account Selection
25+
26+
- **Hybrid selector returns `null`** when no accounts are available (was returning a stale fallback) (AUDIT-H2) (PR-D)
27+
- **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)
28+
- **Active-account pointer normalization** on disable/remove — prevents a dangling pointer when the active account is administratively removed (AUDIT-H10) (PR-F)
29+
- **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 via `CODEX_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)
30+
31+
### Storage & Recovery
32+
33+
- **Recovery storage** migrated to atomic write + retry-safe delete pattern (AUDIT-M01) (PR-H)
34+
- **Account-clear ordering** now writes the reset marker BEFORE deletion and retries `EPERM` on read (AUDIT-M04 / M05) (PR-I)
35+
- **Per-project vs CLI-sync config conflict** now surfaced to the user instead of silently bypassing project-scoped isolation (AUDIT-M09) (PR-J)
36+
- **Zod at JSON.parse boundaries** — storage reads are now Zod-validated at 12 sites in `lib/storage/**`. New `safeParseJson<T>(raw, schema, context)` helper guards both `SyntaxError` and schema violations. V3 storage schema is the authoritative normalizer via `AnyAccountStorageSchema`. Recovery + request + config Zod migration deferred to follow-up PRs (AUDIT-M20) (PR-L)
37+
38+
### Request Pipeline & SSE
39+
40+
- **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)
41+
42+
### CLI Dashboard & Diagnostics
43+
44+
- **`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)
45+
- **`codex auth verify [--paths|--flagged|--all] [--json]`** — new self-test command that walks the storage path resolution chain and exercises `resolvePath()` sandbox with known-good and known-bad inputs. Confirms the PR-A lookalike-prefix fix is live. `verify-flagged` kept as back-compat alias (PR-P)
46+
- **`lib/codex-manager/settings-hub.ts`** (808 LOC) split into 5 sub-concern files under `lib/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.md` updated to retire the stale 2100-LOC claim (AUDIT-M24 / G-01 / JN-03) (PR-M)
47+
48+
### Observability
49+
50+
- **`getAccountHealth()`** reads the tracker directly; documents the field-name drift vs `ManagedAccount`. `CHANGELOG.md` updated (AUDIT-M08 / D-04) (PR-O)
51+
52+
### Tooling, Docs & Tests
53+
54+
- **`pack:check` + tmp hygiene**`npm run pack:check` now builds first and tests use `os.tmpdir()`; 6 stray `tmp*` directories at repo root cleaned up (AUDIT-H7 / M31) (PR-G)
55+
- **Dual-linter scope documented** — ESLint vs Biome boundaries clarified; husky `prepare` hook side-effect documented (AUDIT-M21 / M22 / M23 partial) (PR-T)
56+
- **Truthup docs pass**`lib/AGENTS.md` staleness fixed; `docs/reference/storage-paths.md` `deriveProjectKey` typo corrected; CHANGELOG drift analysis deferred (AUDIT-H8 / M32 / L04) (PR-Q)
57+
- **Phase 1 regression suite** — locked in audit invariants (PKCE S256, state entropy, SSE failover) (AUDIT-L01 / M03 partial) (PR-S)
58+
- **Master audit report**`docs/audits/MASTER_AUDIT.md` + `docs/audits/evidence/findings-index.json` published (PR #393)
59+
60+
## Config Changes
61+
62+
- **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.
63+
64+
## Known Deferred (Phase 1.5 / Phase 2)
65+
66+
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:
67+
68+
- **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)
69+
- **LOW (11)**: L02, L03, L05, L06, L07, L08, L09, L10, L12, L13, L14 — batch-deferred to single janitorial PR
70+
71+
### Post-Audit Remediation Commits
72+
73+
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`.
74+
75+
The following 7 fix commits were pushed on top of the original PR heads to close the HIGH findings:
76+
77+
| PR | Fix commit | Addressed |
78+
|----|-----------|-----------|
79+
| #412 | `21c466a` | Property tests now use external concurrency observer (proves mutex actually serializes) |
80+
| #401 | `f877c85` | Completed atomic write migration (`injectTextPart`, `prependThinkingPart`) + `renameSync` retry on EBUSY/EPERM |
81+
| #410 | `b3c2945` | `verify --paths` sandbox probe hardened against `cwd=/` edge case + new commands documented |
82+
| #399 | `d7af34d` | All-disabled dangle + `cursorByFamily` divergence + 3 edge-case regression tests |
83+
| #396 | `f18d721` | `lib/ui/copy.ts` routed through `AUTH_REDIRECT` SSOT |
84+
| #396 | `475ea37` | Added `AUTH_REDIRECT` to codex-manager-cli test mock (follow-up to `f18d721`) |
85+
| #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`) |
86+
87+
## Residual Known Deferred
88+
89+
- **Pre-existing `removeAccount` dangle** (`lib/accounts/pointer.ts`): when the active account is removed and it was the last in its family array, the pointer is set to `-1` instead 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.
90+
91+
## Validation
92+
93+
- `npm test` — 3527 tests pass (was 3345, +182)
94+
- `npm run typecheck` — exit 0
95+
- `npm run lint` — exit 0
96+
- `npm run audit:ci` — 0 vulnerabilities
97+
- `npm run pack:check` — 822KB / 968 files (under budget)
98+
- `npm run vendor:verify` — 2 components / 8 files verified
99+
- `npm run clean:repo:check` — green
100+
- `npm run build` — exit 0
101+
102+
## Semver Verification (AUDIT-M33)
103+
104+
Commit range v1.2.4..v1.3.0 audited for silent breaking changes:
105+
106+
- **v1.2.4 → v1.2.5**: observability wording clarifications — docs only
107+
- **v1.2.5 → v1.2.6**: forwarded observability fix — patch-appropriate
108+
- **v1.2.6 → v1.2.7**: native Codex CLI install support — wrapper additive, no removal
109+
- **v1.2.7 → v1.3.0**: Phase 1 post-audit — MINOR appropriate:
110+
- New public surface: `codex auth why-selected`, `codex auth verify` commands
111+
- New opt-in flag: `routingMutex` (default legacy = zero behavior change)
112+
- New types exported: `SelectionRecord`, `HybridSelectionCandidateTrace`, `HybridSelectionTraceResult`, `FlaggedAccountStorageV1Schema`, `AccountsJournalEntrySchema`
113+
- No removed exports, no changed function signatures, no changed storage format
114+
- `verify-flagged` preserved as alias (no breaking removal)
115+
116+
**Conclusion**: minor bump is correct; no major bump justified.
117+
118+
## Related
119+
120+
- Audit report: `docs/audits/MASTER_AUDIT.md`
121+
- Audit findings index: `docs/audits/evidence/findings-index.json`
122+
- Previous release notes: `docs/releases/v1.2.7.md`
123+
124+
## PR Index
125+
126+
| PR # | Branch | Title |
127+
|---|---|---|
128+
| #393 | docs/master-repository-audit-2026-04-17 | docs(audit): add master repository audit report and evidence |
129+
| #394 | fix/phase1-tests-green-and-resolvepath | fix(phase1): reject resolvePath lookalike-prefix paths + restore auth list reset message |
130+
| #395 | fix/oauth-url-redaction | fix(auth): redact OAuth URL in user-facing login output |
131+
| #396 | refactor/redirect-uri-ssot | refactor(auth): introduce AUTH_REDIRECT single source of truth |
132+
| #397 | fix/hybrid-selector-null-contract | fix(routing): hybrid selector returns null when no accounts are available |
133+
| #398 | fix/short-429-race | fix(routing): mark account unavailable before short-429 retry sleep |
134+
| #399 | fix/active-pointer-normalization | fix(accounts): normalize active pointer when the active account is disabled |
135+
| #400 | fix/release-hygiene | fix(release): pack:check builds first + tests use os.tmpdir |
136+
| #401 | fix/recovery-atomic-writes | fix(recovery): atomic writes + retry-safe deletes for recovery storage (R6) |
137+
| #402 | fix/storage-clear-ordering | fix(storage): write reset marker before deletions + retry EPERM on read |
138+
| #403 | fix/project-scope-no-silent-bypass | fix(routing): surface the per-project vs CLI-sync config conflict |
139+
| #404 | fix/request-observability | fix(request): surface malformed SSE JSON chunks as structured warnings |
140+
| #405 | docs/truthup | docs: truthup AGENTS.md staleness + deriveProjectKey typo |
141+
| #406 | chore/config-hygiene | chore(config): document dual-linter scope + husky prepare-hook side effect |
142+
| #407 | test/audit-regression-suite | test(audit): add Phase 1 regression suite locking in audit invariants |
143+
| #408 | refactor/health-unify | docs(health): document field-name drift vs ManagedAccount (AUDIT-M08) |
144+
| #409 | refactor/zod-storage-boundaries | refactor(types): add Zod safeParseJson at storage JSON boundaries |
145+
| #410 | feat/cli-why-selected-and-verify-paths | feat(cli): add codex auth why-selected and verify --paths commands |
146+
| #411 | refactor/settings-hub-split | refactor(codex-manager): split settings-hub.ts into 5 sub-concern files |
147+
| #412 | refactor/routing-mutex | refactor(routing): add feature-flagged routing mutex and SelectionRecord |
148+
| #413 | fix/active-pointer-followup | fix(accounts): resolve residual removeAccount dangle (follow-up to #399) |

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codex-multi-auth",
3-
"version": "1.2.7",
3+
"version": "1.3.0",
44
"description": "Multi-account OAuth manager and codex auth wrapper for the official @openai/codex CLI, with switching, health checks, and recovery tools",
55
"main": "./dist/index.js",
66
"types": "./dist/index.d.ts",

0 commit comments

Comments
 (0)