Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
158 commits
Select commit Hold shift + click to select a range
d375d75
chore(release): open dev at 2.51.0 before releasing 2.50.0
github-actions[bot] Sep 10, 2026
cf44f6f
Merge pull request #4194 from lidge-jun/codex/dev-version-2.51.0
lidge-jun Sep 10, 2026
14d307a
docs(devlog): plan the 2.50.0 regression audit and release train
lidge-jun Sep 10, 2026
480ecd7
docs(devlog): fold the roadmap audit findings into the 2.50.0 plan
lidge-jun Sep 10, 2026
ecd27aa
docs(devlog): pin the 2.50.0 promotion source and method
lidge-jun Sep 10, 2026
fb2118e
docs(devlog): write the wp3 triage and remediation protocol
lidge-jun Sep 10, 2026
5110586
docs(devlog): specify the six audit lane dispatch packets
lidge-jun Sep 10, 2026
d206d3c
docs(devlog): close the packet and triage holes the third audit round…
lidge-jun Sep 10, 2026
b8f6db2
docs(devlog): record the six-lane regression audit result for 2.50.0
lidge-jun Sep 10, 2026
aa3dfcc
docs(devlog): record the 2.50.0 release-readiness decision
lidge-jun Sep 10, 2026
2cf35c8
docs(devlog): write the 2.50.0 execution runbook
lidge-jun Sep 10, 2026
af190ef
docs(devlog): record the 2.50.0 promotion through main
lidge-jun Sep 10, 2026
aeeddfc
docs(devlog): record the verified 2.50.0 release artifacts
lidge-jun Sep 10, 2026
188d925
docs(devlog): close the 2.50.0 unit with the delivery record
lidge-jun Sep 10, 2026
6d3ad12
Merge pull request #4196 from lidge-jun/codex/260910-250-regression-a…
lidge-jun Sep 10, 2026
39409f9
docs(devlog): plan the 260911 lane dispatch round
lidge-jun Sep 10, 2026
1ae5c24
docs(devlog): record the round PR and lane seed commits in the ledger
lidge-jun Sep 10, 2026
1ff816a
docs(devlog): fold audit round 1 into the lane territories and packets
lidge-jun Sep 10, 2026
555321e
docs(devlog): fold audit round 2 into explicit lane ownership and nam…
lidge-jun Sep 10, 2026
ccda2d2
docs(devlog): regenerate the ledger from live git and gh output
lidge-jun Sep 10, 2026
33d4ee2
docs(devlog): fold audit round 3 and record the one rejected finding
lidge-jun Sep 10, 2026
538668b
docs(devlog): add the per-lane dispatch handoff
lidge-jun Sep 10, 2026
0aa6850
Merge pull request #4217 from lidge-jun/codex/260911-lane-dispatch-round
lidge-jun Sep 10, 2026
0f61ab6
docs(devlog): record the published lane branches and the wp2 publish …
lidge-jun Sep 10, 2026
6a3774c
docs(devlog): record the wp2 push execution and its remote verification
lidge-jun Sep 10, 2026
183e1bb
Merge pull request #4220 from lidge-jun/codex/260911-round-ledger-1
lidge-jun Sep 10, 2026
9f550ae
docs(devlog): mechanize the lane tracking refresh and fix the wp4 mer…
lidge-jun Sep 10, 2026
cc871a8
Merge pull request #4221 from lidge-jun/codex/260911-round-tracking-1
lidge-jun Sep 10, 2026
f73fcc5
docs(devlog): fold the seven-lane feasibility audit into lane ownersh…
lidge-jun Sep 10, 2026
6101140
Merge pull request #4223 from lidge-jun/codex/260911-round-feasibility-1
lidge-jun Sep 10, 2026
6db04ea
docs(devlog): seed the L1 dispatch packet
lidge-jun Sep 10, 2026
9e75542
fix(opencode-go): give sessionless requests an isolated session lane
lidge-jun Sep 10, 2026
7b249fb
docs(devlog): seed the L5 dispatch packet
lidge-jun Sep 10, 2026
6b033f1
fix(integrations): refuse an integration write that would take the fi…
lidge-jun Sep 10, 2026
814ce99
Merge pull request #4226 from lidge-jun/codex/260911-l1-responses-core
lidge-jun Sep 10, 2026
ed839a3
Merge pull request #4227 from lidge-jun/codex/260911-l5-integrations-io
lidge-jun Sep 10, 2026
aa78890
docs(devlog): seed the L7 dispatch packet
lidge-jun Sep 10, 2026
0f0e226
docs(devlog): seed the L3 dispatch packet
lidge-jun Sep 10, 2026
64cc0ad
docs(devlog): seed the L2 dispatch packet
lidge-jun Sep 10, 2026
1bce6fa
docs(devlog): seed the L6 dispatch packet
lidge-jun Sep 10, 2026
4863316
docs(devlog): seed the L4 dispatch packet
lidge-jun Sep 10, 2026
8fa67f9
fix(bigmodel): admit the Responses Coding Plan preset to the quota re…
lidge-jun Sep 10, 2026
7f91737
fix(codex): warm up new ChatGPT Free accounts instead of blaming the …
lidge-jun Sep 10, 2026
d4d52fc
docs(devlog): record the L3 WP1 carry and the diagnostic gap it closed
lidge-jun Sep 10, 2026
bf7585a
fix(update): support native pnpm self-updates
lidge-jun Sep 10, 2026
7556be8
docs(devlog): record the #4201 quota admission and the Flash decision
lidge-jun Sep 10, 2026
2d79b39
docs(devlog): record the upstream evidence behind the Flash deferral
lidge-jun Sep 10, 2026
030316c
fix(responses): classify Codex WS failures instead of restating them
lidge-jun Sep 10, 2026
803c5b4
test(update): cover pnpm's isolated virtual store in the ownership rule
lidge-jun Sep 10, 2026
abec9ee
fix(codex): name the account that left the pool instead of only dropp…
lidge-jun Sep 10, 2026
dfe2b5e
docs(devlog): record the L3 WP2 attribution unit and its two out-of-s…
lidge-jun Sep 10, 2026
ad36a59
Merge pull request #4230 from lidge-jun/codex/260911-l3-account-pool
lidge-jun Sep 10, 2026
df65a25
Merge pull request #4231 from lidge-jun/codex/260911-l2-catalog-provider
lidge-jun Sep 10, 2026
785a3a2
fix(qoder): refuse vendor CLI scaffolding in routed output
lidge-jun Sep 10, 2026
848adbb
fix(update): key the Bun size gate on the directory, as before the carry
lidge-jun Sep 10, 2026
de1d887
feat(codex): let an operator keep a downgraded account out of pool ro…
lidge-jun Sep 10, 2026
46e2a37
docs(devlog): record the L3 WP3 plan-policy unit and its packet depar…
lidge-jun Sep 10, 2026
091b7f3
docs(devlog): lock the L7 wordings for #4215 and #4200
lidge-jun Sep 10, 2026
8dcd1c1
test(codex): pin the boot-probe guard by what it wraps, not by adjacency
lidge-jun Sep 10, 2026
1ada8f5
fix(stop): name the real refusal cause instead of asserting ownership
lidge-jun Sep 10, 2026
9e4654a
docs(devlog): record the wp2 stop-refusal work-phase
lidge-jun Sep 10, 2026
6db526a
docs(providers): state which account each provider login spends
lidge-jun Sep 10, 2026
ee61b48
docs(devlog): record the #4215 delivery and what three audits changed
lidge-jun Sep 10, 2026
9627fe2
fix(client): refuse a hub catalog the local Codex CLI cannot parse
lidge-jun Sep 10, 2026
8f3e0b8
docs(devlog): record the wp3 client-catalog work-phase
lidge-jun Sep 10, 2026
59d2dc4
docs(remote-hub): make the setup runnable on a fresh config and close…
lidge-jun Sep 10, 2026
cdf52e0
docs(devlog): record the #4200 delivery and the loopback-bind finding
lidge-jun Sep 10, 2026
9cdcb43
fix(qoder): stop forwarding the region between a suppressed block and…
lidge-jun Sep 11, 2026
99028b6
fix(qoder): close three more scaffolding leaks in the routed-output g…
lidge-jun Sep 11, 2026
160a58f
Merge pull request #4233 from lidge-jun/codex/260911-l3-account-attri…
lidge-jun Sep 11, 2026
55c74ac
Merge pull request #4235 from lidge-jun/codex/260911-l4-service-cli
lidge-jun Sep 11, 2026
9341de0
Merge pull request #4232 from lidge-jun/codex/260911-l6-streaming-tools
lidge-jun Sep 11, 2026
271a0ab
Merge pull request #4239 from lidge-jun/codex/260911-l7-docs
lidge-jun Sep 11, 2026
59f3778
Merge pull request #4238 from lidge-jun/codex/260911-l3-pool-plan-policy
lidge-jun Sep 11, 2026
bb17773
Merge pull request #4237 from lidge-jun/codex/260911-l4-stop-refusal
lidge-jun Sep 11, 2026
0589579
Merge pull request #4234 from lidge-jun/codex/260911-l6-qoder-scaffold
lidge-jun Sep 11, 2026
09e99eb
Merge pull request #4241 from lidge-jun/codex/260911-l7-remote-hub
lidge-jun Sep 11, 2026
babb764
Merge pull request #4240 from lidge-jun/codex/260911-l4-client-catalog
lidge-jun Sep 11, 2026
7ff8d07
fix(service): make macOS repair verify, roll back, and stop evicting …
lidge-jun Sep 11, 2026
13ca31d
test(service): cover the launchd repair protocol and the load tri-state
lidge-jun Sep 11, 2026
1b98b68
docs(devlog): record the launchd repair/status lane
lidge-jun Sep 11, 2026
6b62524
fix(service): repair on the tri-state probe, and evict both launchd d…
lidge-jun Sep 11, 2026
cef096d
test(service): cover the probe-driven repair, PATH reuse and both dom…
lidge-jun Sep 11, 2026
5a8bffe
docs(devlog): record the review round of the launchd repair lane
lidge-jun Sep 11, 2026
3d2e957
docs(devlog): name both unguarded writers of the live launchd assets
lidge-jun Sep 11, 2026
ee6a20a
fix(service): make ocx service restart restart a healthy launchd job
lidge-jun Sep 11, 2026
8e669ec
test(service,doctor): restart is its own verb and the skew advice nam…
lidge-jun Sep 11, 2026
95b95b6
fix(gui): open the local management ingress on a hub instead of the t…
lidge-jun Sep 11, 2026
cdd64f3
feat(server): bind a same-port loopback companion on a non-loopback hub
lidge-jun Sep 11, 2026
06a506c
fix(codex): honor the companion port everywhere, and name the hub gat…
lidge-jun Sep 11, 2026
d015bc7
test(server,cli): pin the companion listener, the drift set, and the …
lidge-jun Sep 11, 2026
98dc39b
test(clients): give the extracted handleEnsure harness its new startu…
lidge-jun Sep 11, 2026
62b905e
docs(devlog): record the hub single-port companion unit (PR2)
lidge-jun Sep 11, 2026
e958961
fix(codex): report the hub gate only when the toggle is on, and refus…
lidge-jun Sep 11, 2026
195158b
feat(server): admit the hub's own client wires on the loopback listener
lidge-jun Sep 11, 2026
216032d
test(service): point the gui-url source oracle at selectDefaultGuiUrl
lidge-jun Sep 11, 2026
d64b10d
feat(service): provision the hub's data-plane token instead of demand…
lidge-jun Sep 11, 2026
3055a2f
feat(cli): load the installed data-plane token before a foreground st…
lidge-jun Sep 11, 2026
65ec21a
feat(config): add the optional hub.dataPublicOrigin advertised data o…
lidge-jun Sep 11, 2026
bf7fed2
feat(cli): add ocx hub invite and one hub block in ocx status
lidge-jun Sep 11, 2026
e933af6
docs(cli): document the hub topology, invites, and token auto-provisi…
lidge-jun Sep 11, 2026
0dc12be
fix(claude): resolve a local client's two destinations instead of one…
lidge-jun Sep 11, 2026
5387377
test(service,cli): pin token provisioning, the hub block, and invite …
lidge-jun Sep 11, 2026
eda6356
docs(remote-hub): the one-port recipe, invite flow, and launchd seman…
lidge-jun Sep 11, 2026
4d044d6
test(claude,server,vision): pin both local destinations per module
lidge-jun Sep 11, 2026
cd58bd1
docs(devlog): record the hub token UX unit (PR4)
lidge-jun Sep 11, 2026
de8674f
docs(skill): teach the ocx skill the one-port hub, invites, and launc…
lidge-jun Sep 11, 2026
83ce218
docs(devlog): record the hub local-clients unit (PR3)
lidge-jun Sep 11, 2026
08b5f03
test(service): drop the installLaunchd import the restack left unused
lidge-jun Sep 11, 2026
a3dde46
docs(cli): say that a macOS restart is a no-op and that --management-…
lidge-jun Sep 11, 2026
0e37f38
fix(lib,claude,server,vision): fall back to the bind address for loca…
lidge-jun Sep 11, 2026
926c62e
fix(cli): refuse a loopback data origin and name the origin an invite…
lidge-jun Sep 11, 2026
7bc6647
docs(devlog): record the docs and skill unit (PR5)
lidge-jun Sep 11, 2026
6d555ff
feat(server): admit POST /v1/messages/count_tokens on the loopback li…
lidge-jun Sep 11, 2026
db3a899
fix(service,cli): re-check the admin-token collision on the reused to…
lidge-jun Sep 11, 2026
1db8136
docs(remote-hub): reconcile with PR4's review round across both local…
lidge-jun Sep 11, 2026
59c45c1
docs(structure,devlog): record the local-destination review round
lidge-jun Sep 11, 2026
8142a85
test(cli,service): pin the review-round refusals and the admin-collis…
lidge-jun Sep 11, 2026
7d6b5b4
docs(remote-hub): ocx service restart now restarts a healthy launchd job
lidge-jun Sep 11, 2026
0fd205f
test(server): the translated wires now prove Reserve admission on the…
lidge-jun Sep 11, 2026
902fc02
docs(devlog): record the PR4 review round
lidge-jun Sep 11, 2026
eff7204
docs(cli): lifecycle reference no longer calls restart an alias of re…
lidge-jun Sep 11, 2026
2a53d7e
feat(server): serve the hub's provider, login and roster state on the…
lidge-jun Sep 11, 2026
a9ea013
feat(client): read hub state over the data plane, and never degrade t…
lidge-jun Sep 11, 2026
e832345
feat(cli): ocx status on a connected client reports the hub, and labe…
lidge-jun Sep 11, 2026
60d9e5d
fix(claude): write the roster on a connected client, from the hub's list
lidge-jun Sep 11, 2026
bb67373
feat(cli): label a client in ocx config show and stop printing the ca…
lidge-jun Sep 11, 2026
da65f9c
docs(devlog): record the client hub-state unit (PR6)
lidge-jun Sep 11, 2026
44b256c
fix(server,client): hub state exports only enabled providers and name…
lidge-jun Sep 11, 2026
d4680d9
fix(cli,client): observe a client's connection, and drop its hub-stat…
lidge-jun Sep 11, 2026
fe65afe
test(server): pin that /v1/hub-state stays off the unauthenticated lo…
lidge-jun Sep 11, 2026
af7c114
docs(devlog,structure): record the PR6 review round and name hub-stat…
lidge-jun Sep 11, 2026
fee6e88
Merge pull request #4249 from lidge-jun/codex/260911-l4-launchd-repair
lidge-jun Sep 11, 2026
24ef1ec
Merge pull request #4250 from lidge-jun/codex/260911-l4-hub-loopback-…
lidge-jun Sep 11, 2026
c3419e5
Merge pull request #4251 from lidge-jun/codex/260911-l4-hub-local-cli…
lidge-jun Sep 11, 2026
5557612
Merge pull request #4252 from lidge-jun/codex/260911-l4-hub-token-ux
lidge-jun Sep 11, 2026
6d8ed37
Merge pull request #4254 from lidge-jun/codex/260911-l7-hub-docs-skill
lidge-jun Sep 11, 2026
42184ea
Merge pull request #4255 from lidge-jun/codex/260911-l4-client-hub-state
lidge-jun Sep 11, 2026
60cec19
fix(codex): emit max/ultra efforts unconditionally and expire stale c…
lidge-jun Sep 11, 2026
9d0f493
docs(devlog): record the catalog presentation and effort projection unit
lidge-jun Sep 11, 2026
dac34a6
docs(devlog): close the catalog presentation and effort projection cycle
lidge-jun Sep 11, 2026
b4a3a80
fix(codex): repair an orphaned exempt default instead of advertising …
lidge-jun Sep 11, 2026
18e553a
Merge pull request #4257 from lidge-jun/codex/260911-clamp-expiry
lidge-jun Sep 11, 2026
846dfad
provider: seed GLM-5.3-Flash on the BigModel Responses preset
lidge-jun Sep 11, 2026
c838ff5
docs(providers): seed Flash in the BigModel Responses roster the page…
lidge-jun Sep 11, 2026
a8f7619
Merge pull request #4244 from lidge-jun/codex/260911-r2-catalog-pool
lidge-jun Sep 11, 2026
d277926
pool: name the account when a refresh fails or its models vanish
lidge-jun Sep 11, 2026
ac3c3d6
Merge pull request #4248 from lidge-jun/codex/260911-r2-pool-account-…
lidge-jun Sep 11, 2026
635a3d1
client: report local Codex readiness instead of bare connected state
lidge-jun Sep 11, 2026
efefde4
client: fold adversarial review into the readiness report
lidge-jun Sep 11, 2026
87f5b52
client: give the write-time gate the same observer in production
lidge-jun Sep 11, 2026
6b04f13
client: keep ocx config show out of the local Codex probe
lidge-jun Sep 11, 2026
75d4cde
Merge pull request #4246 from lidge-jun/codex/260911-r2-client-display
lidge-jun Sep 11, 2026
9cc825f
docs(i18n): make the remote hub guide runnable in every locale
lidge-jun Sep 11, 2026
b550d24
Merge pull request #4247 from lidge-jun/codex/260911-r2-docs-locales
lidge-jun Sep 11, 2026
b83a0db
fix(responses): keep the retryable main-refresh refusal an overload, …
lidge-jun Sep 11, 2026
42fcf9a
Merge pull request #4269 from lidge-jun/codex/260911-main-refresh-cla…
lidge-jun Sep 11, 2026
cf456e8
release: promote verified 2.51.0 product tree to main
lidge-jun Sep 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
293 changes: 222 additions & 71 deletions bin/ocx.mjs

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions devlog/_plan/260910_250_regression_audit_release/000_plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# 2.50.0 regression audit and release — scope

## Baseline and candidate

- Released baseline: `v2.49.0`, `main` at `2f3f736299dca38861f8fb9c4326a4b4d7c664bc`.
- Audit candidate: `origin/dev` at `12c248f52bed88ea13be5b284c79a238feb592d1`, `package.json` version `2.50.0`.
- Delta: 127 commits (`git rev-list --count 2f3f73629..origin/dev`), 1066 changed files
(`git diff --name-only 2f3f73629...origin/dev | wc -l`).

### File counts, by `git diff --name-only 2f3f73629...origin/dev | cut -d/ -f1 | sort | uniq -c`

| Group | Files | Note |
| --- | --- | --- |
| `devlog` | 885 | No runtime. Not audited. |
| `src` | 62 | Audited: L1-L6 (L4 owns `src/server/management/*`, `src/server/{management-api,auth-cors,index}.ts`, `src/service.ts`) |
| `tests` | 42 | Read as evidence by every lane, not a lane of its own |
| `docs-site` | 28 | Not release-blocking on its own |
| `gui` | 27 | Audited: L4 |
| `readme` + root docs | 13 | `readme/` 8, plus `README.md`, `AGENTS.md`, `AGENTS_INSTALL.md`, `SECURITY.md`, `package.json` |
| `skills` | 3 | Audited: L6 (`skills/ocx` surface map) |
| `.github` | 3 | PR assets only, no workflow change |
| `structure` | 2 | Maintainer invariants |
| `scripts` | 1 | `scripts/test-layout/layout.json` |

`git diff --shortstat 2f3f73629...origin/dev -- src gui docs-site scripts .github` is
121 files / +3322 / -280. That figure excludes `tests` and `package.json`; the full
non-`devlog` set is 181 files. An earlier revision of this doc attributed 121 to a
different folder set and derived the devlog count by subtraction; the reviewer
contradicted both with the commands above.

## What this unit does

Audit the product delta for release-blocking regressions, remediate anything blocking,
then run the 2.50.0 train: pre-move `dev`, promote the frozen candidate to `main`,
publish to npm, and verify the artifacts independently.

## Authorization in force

The user authorized parallel `xai/grok-4.6` subagents, a regression-audit PABCD cycle,
and the release itself. Subagents are read-only verifiers; the main session owns every
PABCD transition, every write, and every external action.

## Out of scope

- Landing unrelated open pull requests. 74 are open against `dev`
(`gh api "repos/lidge-jun/opencodex/pulls?state=open&base=dev&per_page=100" --jq 'length'`);
none is a release prerequisite, and pulling one in moves the candidate mid-audit.
- Re-auditing anything already released in 2.49.0.
- Any change to `devlog/` history or to third-party accounts.

## Terminal outcomes

- `DONE` — 2.50.0 on npm `latest` with `gitHead` matching the promoted `main` SHA, a
git tag, a GitHub release, and a recorded triage for every audit finding.
- `BLOCKED` — a release-blocking regression that cannot be fixed inside this scope,
or a missing external permission (npm trusted publishing, workflow dispatch).
- `NOOP` — the candidate is already published and verified.
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Audit lanes

Six read-only lanes, each dispatched to an independent `xai/grok-4.6` subagent with a
fresh context. Read scopes are stated per lane so a finding traces to one owner; the
lanes never write, and the main session de-duplicates the returns.

Every lane compares `2f3f73629...origin/dev` and must return exact `path:line` anchors.
A lane that finds nothing returns "no blocker" with the files it actually read.

Lane coverage is checked mechanically: every path in
`git diff --name-only 2f3f73629...origin/dev -- src gui scripts skills package.json`
belongs to at least one lane. The first revision of this map left
`src/cli/{capabilities,index,models-runtime,observe}.ts` unowned, which is why L6 exists.

## L1 — Responses and request pipeline

`src/server/responses/{core,compact,context-overflow,policy-fallback,codex-ws-wire}.ts`,
`src/server/{chat-completions,chat-native,claude-messages,images,search,request-decompress,request-log}.ts`,
`src/claude/inbound.ts`, `src/web-search/{passthrough-bridge,ollama-executor}.ts`.

Highest-risk lane: the new hosted web-search bridge (`passthrough-bridge.ts` +761), the
non-streaming context-overflow classification, agent-task recovery on mid-thread model
switches, and the configurable inbound body admission limit.

## L2 — Codex accounts, quota, OAuth

`src/codex/{account-runtime-state,account-store,account-usability,auth-api,auth-context,inject,quota,quota-auto-refresh}.ts`,
`src/oauth/{health,index,token-guardian}.ts`, `src/cli/{account,account-api,account-auth,account-extended}.ts`.

Deferred validation, revoked pool grants, reauth-state clearing, the new account plan
field, and the token guardian.

## L3 — Catalog, providers, combos, config

`src/codex/catalog/{parsing,provider-fetch,sync}.ts`, `src/providers/{registry,quota,google-ai-studio-model-discovery,opencode-zen-rate-limit}.ts`,
`src/combos/{index,resolve}.ts`, `src/config.ts`, `src/types.ts`, `src/types/{accounts,config,provider}.ts`,
`src/clients/config-export/zcode.ts`, `src/lib/errors.ts`.

Free-model pricing classification and filtering, quota-exhausted inactive marking, AI
Studio discovery restoration, cross-provider blocked-model redirects.

## L4 — Management API, service, GUI

`src/server/management/*`, `src/server/{management-api,auth-cors,index}.ts`, `src/service.ts`,
`gui/src/**`, `gui/tests/**`.

The routed-account log label, the decode-rate column, management auth, the stale launchd
bootout recovery, and nine i18n locale files that must not contradict `en`.

## L5 — Security, privacy, release surface

Cross-cutting read of `src/lib/privacy.ts`, the body-size admission path, web-search
bridge egress, `package.json`,
`scripts/test-layout/layout.json`, `structure/{02_config-and-codex-home,04_transports-and-sidecars}.md`,
and the repository invariants in `AGENTS.md`: the Lab/core import boundary, the
synchronous `startServer` window, no tracked gitlink, and no request-body or credential
logging.

The email-masking opt-out is the specific item to scrutinize: it deliberately weakens a
privacy default, so it must be off by default, must survive `bun run privacy:scan`, and
its CLI application in `src/cli/index.ts` (L6) must agree with the library default.

## L6 — Operator CLI surface

`src/cli/{capabilities,index,models-runtime,observe}.ts`, `skills/ocx/**`, and the
generated surface map that `tests/ci-workflows/skill-ocx.test.ts` asserts.

`src/cli/index.ts` applies `privacy.maskEmails` to `ocx status` and is a
`service-lifecycle.yml` gate path. `capabilities.ts` adds a mutating `ocx account refresh`
with a consent warning. `models-runtime.ts` adds `--free-only`, which filters on
`pricingStatus === "free"` and therefore drops entries with no status. `observe.ts` adds
`--account` log filtering.

## Blocker definition

A finding blocks the release when any of these hold.

1. **Regression against 2.49.0** — behavior that worked in the released tree and does not now.
2. **Crash, hang, or unbounded resource use** on any path a default install can reach.
3. **New-path functional breakage.** A feature introduced in this delta that does not do
what it claims still blocks, even though it is not a regression. This covers the
web-search bridge returning wrong or empty results, `--free-only` silently dropping
models with an absent `pricingStatus`, and a no-op `ocx account refresh`.
4. **Security or privacy weakening**, including a default that becomes less private, a
credential or request body reaching a log, or a loosened auth boundary.
5. **User-consent or identity-spend bypass**, per `AGENTS.md` "User-consent actions": any
path that spends the user's identity, credits, or reputation without the code-level
gate, including a CLI path that mints its own dashboard session.
6. **Core invariant violation**, per `AGENTS.md`: a Lab import reaching `src/router.ts`,
`src/server/lifecycle.ts`, or `src/server/responses/core.ts`, an `await` inside the
synchronous `startServer` activation window, or a tracked gitlink.
7. **Upgrade-path breakage**, not only first-run. An existing 2.49.0 install that keeps a
stale launchd job, a stale config, or a stale service unit after upgrading blocks.
8. **Broken release, packaging, or operator-surface contract**, including a `skills/ocx`
map that names a command the registry does not have.

Style, missing coverage for unchanged code, and defects that already shipped in 2.49.0
do not block; they are recorded as non-blockers with the evidence that they predate the delta.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# 2.50.0 release plan

Derived from `.github/workflows/release.yml` as it exists on `dev`, not from precedent.
The gates below are what the workflow actually enforces.

## What release.yml requires

| Gate | Line | Requirement |
| --- | --- | --- |
| Branch | `release.yml:153-170` | Must run from `refs/heads/main` or `refs/heads/preview`. `main` refuses any version containing `-`; `preview` refuses any version that is not `*-preview.*`. |
| dist-tag | `release.yml:174-177` | `main` -> `latest`, `preview` -> `preview`. |
| CI | `release.yml:179-197` | A **successful `ci.yml` run with `--event push` on the release branch for `$GITHUB_SHA`**. A pull-request run is explicitly rejected, and a `workflow_dispatch` run on `dev` does not qualify. |
| Service lifecycle | `release.yml:225-237` | If any of `src/service.ts`, `src/cli.ts`, `src/cli/index.ts`, `src/lib/bun-runtime.ts`, `package.json`, `bun.lock`, `service-lifecycle.yml`, `release.yml` changed since the previous tag, a successful `service-lifecycle.yml` run for `$GITHUB_SHA` is required. This delta changes `src/cli/index.ts` and `package.json`, so the gate is armed. |
| dev ahead | `release.yml:242-249` | `bun scripts/version-line.ts assert-ahead <dev version> <release version>`. `dev` is currently `2.50.0`, so publishing 2.50.0 fails until `dev` is pre-moved. |
| Publish | `release.yml:22-26` | `dry-run` defaults to **true**. A real publish needs `dry-run=false`. `expected-sha` is required and must equal the branch head at dispatch. |

`$GITHUB_SHA` on `main` is the **promotion merge commit**, not the frozen `dev` SHA.
2.49.0 published from merge `2f3f73629`, not from its promoted tree commit `62849dfa6`.
Both `ci.yml` (`push: branches: [main, preview, dev]`, `paths: src/**, gui/**, ...`) and
`service-lifecycle.yml` (`push`, paths including `package.json` and `src/cli/index.ts`)
fire automatically on that merge, so the required runs appear without a dispatch — but
they must be waited for on that exact SHA.

## Order

1. **Freeze the candidate.** Record the exact `dev` SHA. A `workflow_dispatch` `lane=all`
run on `dev` is audit evidence for the tree, not the release gate; it tells us whether
the tree is green before we spend a promotion on it.
2. **Land blockers first.** Any wp3 fix goes to `dev` through a pull request, which moves
the candidate. Re-freeze and re-verify on the new SHA; old-head green is not evidence.
3. **Pre-move `dev`.** Dispatch `dev-version-bump.yml` with `intended-version=2.50.0`,
`mode=pre-move`. It is `on: workflow_dispatch`, but `dev-version-bump.yml:79` refuses
a non-default ref, so dispatch it with `--ref main`. It opens a pull request and does
**not** push to `dev`, because the `Protect dev` ruleset requires review. Merge that PR
so `dev` reads 2.51.0 before the publish reaches `assert-ahead`. Use the workflow rather
than a hand-written one-file PR so its tag/npm/version-line proofs run.
4. **Promote the frozen SHA to `main`, not current `dev`.** After step 3, `origin/dev` is
2.51.0 and is no longer the candidate. Promotion always names the recorded freeze SHA
explicitly.

The freeze SHA is **not** an ancestor of `main`, and `main` carries commits `dev` does
not, so there is nothing to fast-forward. Replicate the 2.49.0 method: branch from
`main`, merge the freeze SHA into that branch as a single
`release: promote verified 2.50.0 product tree to main` commit, then open the PR into
`main`. For 2.49.0 that was branch `codex/release-249-main-01a08498`, promote commit
`62849dfa6` (parents `9a27e8699` = old `main`, `ad36c7be8` = the dev freeze), merged by
PR #4117 as `2f3f73629`.

The gate on this step is **tree equality**, not a green diff: after promotion,
`git rev-parse <main merge>^{tree}` must equal `git rev-parse <freeze SHA>^{tree}`.
For 2.49.0 all three of the promote commit, the dev freeze, and the merged `main` tip
resolved to tree `66294fb3eb15592afd732f8b8e29d0bcc644fe9e`. Any conflict resolution
that changes that tree means a different product shipped than the one audited.
Record the merge SHA.
5. **Wait for the release-branch gates on the merge SHA.** Push-event `ci.yml` and
`service-lifecycle.yml` on `main` for that exact SHA, both successful.
6. **Dry-run, then publish.** Dispatch `release.yml` with `--ref main`,
`version=2.50.0`, `tag=latest`, `expected-sha=<merge SHA>`, first with
`dry-run=true`, then with `dry-run=false` once the dry run is green.
7. **Verify artifacts.** `npm view @bitkyc08/opencodex dist-tags`, the `2.50.0`
`gitHead` against the promoted `main` SHA, the git tag, the GitHub release, tarball
integrity, and SLSA provenance. npm propagation lag returns 404 or a stale `latest`;
poll, never republish.
8. **`preview` is a separate line and is not part of this stable train.**
`origin/preview` is `2.49.0-preview.20260909`, and `release.yml:161-165` refuses a
preview publish whose version is not `*-preview.*`. Promoting the plain `2.50.0` tree
onto `preview` would break that branch's version line. If `preview` should carry this
tree, it needs its own `2.50.0-preview.<date>` commit, decided after the stable
release lands. A branch sync and a preview npm publication are distinct operations.

## Known failure modes to expect

- Branch-keyed CI concurrency cancels an older run when a newer commit lands. A cancelled
aggregate is neither a product failure nor passing evidence.
- The registry-availability smoke can time out after npm already accepted the publish.
Inspect metadata, provenance, and tarball before considering a retry.
- `dev-version-bump.yml` rejects a dispatch from a non-default ref as an early warning.
Loading
Loading