diff --git a/devlog/_plan/260927_release_train_4/provider-compat/000_plan.md b/devlog/_plan/260927_release_train_4/provider-compat/000_plan.md index f3d339f1e8a..38fe13e7a4d 100644 --- a/devlog/_plan/260927_release_train_4/provider-compat/000_plan.md +++ b/devlog/_plan/260927_release_train_4/provider-compat/000_plan.md @@ -39,3 +39,9 @@ Independent reviewer handle `01a0e348-c775-7a50-b2e5-25d11a5efb92` first returne Append the exact branch/PR, source and CI SHA, commands/results, issue disposition, and D conclusion for later phases without claiming an unrun suite. - **wp1 resume, 2026-09-28:** A successor task picked up the lane from the stopped thread's handoff, whose content is folded here, and removed the transfer note. The Codex P1 on #6097 (scoped key could reach Pool credential resolution before the scope check) is fixed by filtering forward candidates by admission scope before `resolveFirstUsableOpenAiSidecar`; see `010_images.md` "wp1 re-entry". An independent sol reviewer re-audited the plan (PASS) and re-verified all eleven non-carried verdicts in `001_candidate_inventory.md` against `origin/dev` `24b2f39b77` (AGREE on all; deferred PR heads unchanged). +- **wp1 D, 2026-09-28:** PR [#6097](https://github.com/lidge-jun/opencodex/pull/6097) (carry of #5927, @rrmlima credited by trailer) rebased onto `6d64ea26a7`, head `362d3df88e`. Adds the scope-before-credential filter and the client-cancel lease release on top of the original carry. Evidence: fixture red-green (`010_images.md`), `test:changed` 2,619 pass / 1 skip / 0 fail at `ad40e8d46a` (same tree for our paths), sol security review PASS after one accepted and one rebutted pre-existing finding. Merge waits on exact-head CI. +- **wp2 D, 2026-09-28:** PR [#6112](https://github.com/lidge-jun/opencodex/pull/6112) (carry of #5497, @hulkbig credited by trailer), head `95c59fc8da` on `dev`, code only; its devlog lives with this unit. Evidence: 261/261 focused, red-green 10/27 → 27/27, sol review PASS. `test:changed` selected most of the suite and hit the local 900 s wrapper limit under seven-lane lock contention; the flagged files reran 348 pass / 3 environment-only launcher failures (a real proxy on port 10100). Merge waits on exact-head CI. Did not improve: local broad coverage for this PR is left to hosted CI. +- **wp3 D, 2026-09-28:** comments on #4213, #4143 and #3765; #2511, #3506 and #5270 keep their existing maintainer dispositions (see `030_issues.md`). Direction change: the #4213 trial-prompt cause turned out to be fixable, so `wp5` (`050_trial_nux.md`) implements it before the `wp4` closing receipts. +- **wp4 D, 2026-09-28:** five deferred-PR dispositions posted after an independent audit (`040_integration.md`). +- **wp5 D, 2026-09-28:** #4213 trial-prompt fix opened as #6117 (`050_trial_nux.md`); a Codex P2 (surrogate pair split at the cap) was fixed with a red-green case. +- **wp6 D, 2026-09-28:** merged to `dev`: #6097 → `468b954cc4` (exact-head CI run 36334532836 plus `enforce-target` success), #6112 → `773c24bbbb` (run 36339549490 plus `enforce-target` success), #6117 → `890eb6d660`. #6117's earlier head `36bd4f2847` passed run 36341789245; the coordinator then replaced per-PR CI with local union checks and one final `dev` run, so after rebasing onto `2115563ca6` for a test-layout registry overlap it merged on 114/114 focused tests plus typecheck, structure and privacy checks. Squash commits keep the co-author trailers for #5927 (@rrmlima) and #5497 (@hulkbig); both originals were closed with thanks and links. #4213 has a follow-up naming both fixes and stays open for user confirmation. Post-merge Cross-platform CI on the final `dev` is run once by the coordinator after all lanes; this lane did not dispatch it. Did not improve: the #3765, #4143, #3506, #5270 and #2511 issues remain open, waiting on reporter data or design decisions. diff --git a/devlog/_plan/260927_release_train_4/provider-compat/010_images.md b/devlog/_plan/260927_release_train_4/provider-compat/010_images.md index 54891ec0b75..819a84531f2 100644 --- a/devlog/_plan/260927_release_train_4/provider-compat/010_images.md +++ b/devlog/_plan/260927_release_train_4/provider-compat/010_images.md @@ -49,4 +49,5 @@ Plan, confirmed against `src/server/images.ts:667-760` at `acc750effa`: - Tests: the two local WIP cases (forbidden Pool → 403 with zero sends; allowed Pool+model → 200 with the Pool bearer). Add one more: a key allowed only for the keyed provider falls through to the keyed branch without Pool resolution. Review dispositions on #6097: the Codex co-author finding is already satisfied by the genuine trailer on `acc750effa`; the squash message will repeat it as a standalone trailer. The CodeRabbit request to apply an RFC 6750 `b64token` pattern to the managed Pool bearer is declined: the Pool token is a stored credential the proxy never builds from request input, the header is assembled through `Headers.set`, and a stricter local pattern would only turn an upstream 401 into a local 500 without closing a reachable path. -\nSecurity re-review (sol reviewer) of `46515c2a2e` returned FAIL with two findings, both present on `origin/dev` before this PR. Accepted: a client cancel during the in-flight Images send returned 499 without releasing a selected Pool account's recovery probe lease; the 499 branch now releases it, with a red-green fixture. Rebutted for this PR and recorded as follow-up: `selectImagesProvider` (`src/providers/openai-sidecar.ts:263,301`, unchanged here) resolves the keyed provider's API-key reference before the keyed branch scope check. That is a local config/keychain read with no network send or account-state change, and moving it changes keyed selection and error precedence for every Images caller; it belongs in a separate hardening change.\n \ No newline at end of file + +Security re-review (sol reviewer) of `46515c2a2e` returned FAIL with two findings, both present on `origin/dev` before this PR. Accepted: a client cancel during the in-flight Images send returned 499 without releasing a selected Pool account's recovery probe lease; the 499 branch now releases it, with a red-green fixture. Rebutted for this PR and recorded as follow-up: `selectImagesProvider` (`src/providers/openai-sidecar.ts:263,301`, unchanged here) resolves the keyed provider's API-key reference before the keyed branch scope check. That is a local config/keychain read with no network send or account-state change, and moving it changes keyed selection and error precedence for every Images caller; it belongs in a separate hardening change. diff --git a/devlog/_plan/260927_release_train_4/provider-compat/020_response_tier.md b/devlog/_plan/260927_release_train_4/provider-compat/020_response_tier.md index 1062097fc81..28cd0cecc93 100644 --- a/devlog/_plan/260927_release_train_4/provider-compat/020_response_tier.md +++ b/devlog/_plan/260927_release_train_4/provider-compat/020_response_tier.md @@ -32,3 +32,19 @@ This is one PR and one atomic runtime contract. Do not split observation from co ## Verification Run `bun test tests/routing/fastwire-response-authority.test.ts tests/routing/fastwire-observability.test.ts tests/providers/model-rename-migration.test.ts tests/providers/provider-config-validation.test.ts tests/usage/usage-cost.test.ts tests/usage/cost-cap-unknown-evidence.test.ts`; these direct paths exercise the changed route, config and cost contracts. Also run `bun run test:changed`, `bun run typecheck`, `bun run privacy:scan`, `bun run structure:check`, `git diff --check`, and the docs-site frozen-install/build required by `docs-site/AGENTS.md`. The test fixture has a fake relay; it is not live evidence of a particular provider's billing. Record the resource exception for the local full suite and rely on completed exact-head required CI before merge. The PR credits @hulkbig with a `Co-authored-by` trailer, targets latest `dev`, and gets an independent review of the full current-dev diff. + +## wp2 P stale check (2026-09-28) + +Previous D (wp1): #6097 head `ad40e8d46a` verified (focused 19/19, `test:changed` 2,619 pass / 1 skip / 0 fail, sol security PASS); merge waits on exact-head CI. Direction kept: carry #5497 next. + +A sol explorer re-mapped PR #5497 head `1152e3c9fc21` onto `origin/dev` `24b2f39b77`. No hunk is obsolete; the old docs anchors moved. Findings that amend the change map above: + +- `src/providers/fastwire.ts:398` already reads `context.responseTierAuthoritative !== false`; the carry only copies the defined flag into `AttemptTierOutcome` near line 341. +- Context conflicts only (newer `fastEnabled` neighbours): `src/config/schema/leaf-validators.ts:299`, `src/server/auth-cors.ts:975` (editor roster is exhaustive at 1104; classify `"editor"`), `src/usage/log.ts:658,680`, `src/providers/openai-tiers-destination.ts:26`, `src/server/responses/core-normalize.ts:20`. +- `src/providers/model-rename-fields.ts:18` needs the field as provider-scoped `"none"`; its role union is exhaustive at 141. +- None of the grown files is listed in `tests/fixtures/file-size-baseline.json`, so no ratchet cap applies. The new `tests/routing/fastwire-response-authority.test.ts` needs both layout registries. +- Docs: English `reference/configuration/providers.md` (field row near 207; section before FastWire migration near 568, after the newer root `auto_review_model` section at 552), zh-cn providers (87, 175), and the Codex guide (134). `structure/gui-and-management-api.md` lost the old usage paragraph; place the note near lines 223/231. + +Existing-user contract: omission keeps legacy authoritative relay semantics; canonical ChatGPT forwarding stays observational even with `true`; `false` changes evidence and pricing only, never the outbound tier or a local unsupported-route downgrade. + +Audit amendment (sol reviewer, NEAR-PASS): `serviceTierContextFromOutcome` in `src/usage/cost.ts:441` checks the `canonical === "priority" && confirmation === "confirmed"` branch before the raw echo, and `normalizeAttemptTierOutcome` (`src/usage/log.ts:635`) does not tie the new flag to `confirmation`. The carry therefore gates **both** branches on `responseTierAuthoritative !== false`, as #5497's cost diff does, and the fixture covers a persisted record whose flag is `false` while `confirmation` says `confirmed`. No GUI, dashboard, usage-summary or CLI consumer needs a change: they read the outcome through the shared cost path. diff --git a/devlog/_plan/260927_release_train_4/provider-compat/030_issues.md b/devlog/_plan/260927_release_train_4/provider-compat/030_issues.md index 7905419d799..a67f54a8e4e 100644 --- a/devlog/_plan/260927_release_train_4/provider-compat/030_issues.md +++ b/devlog/_plan/260927_release_train_4/provider-compat/030_issues.md @@ -30,3 +30,10 @@ The existing opt-in `maxUpstreamBodyBytes` checks the final serialized outbound ## Verification For each posted comment, read it back from GitHub and confirm number, body, open/closed state and link. A current source/merged-commit link is needed for any claim of resolution. For #4213 and #4143, no local user's app configuration is changed. If the diagnostic records arrive during this phase, amend this doc, run a separate repair cycle if a bounded regression is feasible, and update the issue with verified evidence. + +## wp3 result (2026-09-28) + +- #4213: diagnostic comment posted ([issuecomment-5857691131](https://github.com/lidge-jun/opencodex/issues/4213#issuecomment-5857691131)). It separates image-tool gating (client side: plan, feature flag, auth mode, model input modalities) from a failed Images call, links #6097 for the Pool admission failure, and names the trial-prompt cause: the live roster's `availability_nux` is dropped by `parseAccountModels`, so native rows keep the pinned `null`. The fix is `wp5` (`050_trial_nux.md`). Stays open. +- #4143: current-build reproduction request posted ([issuecomment-5857853055](https://github.com/lidge-jun/opencodex/issues/4143#issuecomment-5857853055)); stays open. +- #3765: `OPENCODEX_CACHE_DEBUG=1` two-turn comparison request posted ([issuecomment-5857853190](https://github.com/lidge-jun/opencodex/issues/3765#issuecomment-5857853190)); stays open. +- #2511, #3506, #5270: no new comment. An independent sol audit found the drafts repeated or contradicted the existing maintainer comments, and the posting conditions above (no current scoped decision / new evidence / landed Qoder client integration) are not met. The existing maintainer comments remain the recorded disposition; all three stay open. diff --git a/devlog/_plan/260927_release_train_4/provider-compat/040_integration.md b/devlog/_plan/260927_release_train_4/provider-compat/040_integration.md index ed169efa838..d1969f5fb96 100644 --- a/devlog/_plan/260927_release_train_4/provider-compat/040_integration.md +++ b/devlog/_plan/260927_release_train_4/provider-compat/040_integration.md @@ -21,3 +21,15 @@ Dependency: `wp3`. Recheck every candidate's latest head, base, review state, an ## Evidence and final report Append the current PR number, head SHA, exact check run URL/attempt/conclusion, merge SHA, original-PR closure link, issue disposition links, commands/exit codes, and remaining limitations to `000_plan.md`. The final user report lists each assigned PR and issue, the two carried methods, dev CI result, test scope, risk, and overlap files. If a required check or branch protection is pending, keep the relevant PR open and state its exact status instead of claiming completion. + +## wp4 result (2026-09-28) + +Deferred PRs stay open with an English disposition naming the blocking contract and re-entry proof, checked by an independent sol audit (FAIL on three overstated claims, PASS after revision): + +- #5995 keyless Zen — [comment](https://github.com/lidge-jun/opencodex/pull/5995#issuecomment-5857896648): client identity minting and injected tools without a published third-party contract; needs OpenCode permission, three-turn tool fixture, security review. +- #3282 Copilot context tier — [comment](https://github.com/lidge-jun/opencodex/pull/3282#issuecomment-5857896879): tier advertised for any configured model; needs per-model verified windows and `modelContextTiers`/`modelContextWindows` interaction on current `dev`. +- #6003 first-output failover — [comment](https://github.com/lidge-jun/opencodex/pull/6003#issuecomment-5857897106): unbounded body read before rule match; timer armed only after headers; needs bounded read, one pre-dispatch deadline, cancellation fixtures, client-visible signal if required. +- #4732 registry cache — [comment](https://github.com/lidge-jun/opencodex/pull/4732#issuecomment-5857897341): snapshot traversal predates `snapshot-select.ts`; needs router-only split with benchmark. +- #3741 Antigravity TLS — [comment](https://github.com/lidge-jun/opencodex/pull/3741#issuecomment-5857897561): transport chosen before the physical-send egress decision (an integration gap; `dev` still fails closed); needs egress integration fixtures, security review with #5083, full exact-head CI. + +Closing the carried originals #5927 and #5497 and checking post-merge `dev` CI moved to `wp6`, because both depend on the replacement PRs merging. diff --git a/devlog/_plan/260927_release_train_4/provider-compat/050_trial_nux.md b/devlog/_plan/260927_release_train_4/provider-compat/050_trial_nux.md new file mode 100644 index 00000000000..594b62fb2f3 --- /dev/null +++ b/devlog/_plan/260927_release_train_4/provider-compat/050_trial_nux.md @@ -0,0 +1,36 @@ +# Phase 5 — #4213 trial prompt: account-scoped model availability NUX + +Previous D (wp2): #5497 carry is PR #6112 at `95c59fc8da` (focused 261/261, sol review PASS); merge waits on exact-head CI alongside #6097. Direction change with reason: #4213 was planned as diagnosis only (`030_issues.md`). A sol explorer traced a concrete, fixable proxy-side cause for the trial-prompt half, so this phase fixes it, and `wp3` keeps only the comment follow-up. + +## Cause + +The public Codex client shows a model availability prompt only when a model row carries `availability_nux: { message }` (`openai/codex` `18344a972`: `codex-rs/tui/src/app/startup_prompts.rs:250-266`; type `codex-rs/protocol/src/openai_models.rs:222-224,277`). Behind OpenCodex, Codex reads the catalog written to `model_catalog_json`. OpenCodex fetches each account's live ChatGPT roster, but `parseAccountModels` (`src/codex/model-entitlements.ts:641-667`) keeps only slugs and `available_access_programs`, and native rows come from the pinned template where `availability_nux` is `null` (`src/codex/data/upstream-models.json:71`). An account-specific "try this model" prompt therefore cannot reach the app while OpenCodex owns the catalog. The Desktop trial UI source is not public, so this is the strongest verified candidate, not a captured trace. + +## Change map + +| Path | Action | Before → after | +| --- | --- | --- | +| `src/codex/model-entitlements.ts` | MODIFY | `parseAccountModels` also returns `availabilityNuxByModel`: only rows it already keeps, only a plain object whose `message` is a non-empty string after trim, capped at 2,000 characters, stored as `{ message }` (no other fields). `CachedAccountModels` carries it; `CodexModelEntitlementSnapshot` gains optional `availabilityNuxByAccount`, populated only for confirmed rosters, mirroring `accessProgramsByAccount`. | +| `src/codex/catalog/access-programs.ts` | MODIFY | `applyNativeAccessPrograms` additionally projects the NUX onto **bare native rows of the main account only**, when that roster is confirmed and lists the slug: set `availability_nux` to the live `{ message }`, otherwise `null`. Combo rows, native alias rows and account-bound (selector) rows get `availability_nux` removed, so a Pool account's prompt never shows and one account's prompt is never duplicated. Routed/derived/Reserve stripping elsewhere stays untouched. | +| `tests/codex-integration/codex-native-availability-nux.test.ts` | NEW | Fixture roster through `resolveCodexModelEntitlements` with a fake fetcher (main row with NUX, Pool row with a different NUX, malformed shapes) and the projection over catalog rows. Register in both layout registries. | +| `structure/catalog.md` (or the owner that documents access-program projection) | MODIFY | Document the NUX projection next to the access-program projection. | +| `docs-site/src/content/docs/guides/codex-integration.md` | MODIFY | One sentence: OpenCodex forwards the main account's live model availability prompt on native rows. | + +The online `GET /v1/models?client_version=` fallback does not project access programs today (`src/server/index/serve-options.ts:1071-1110`); it stays unchanged here, and the plan records that parity gap. + +## Activation and observable checks + +1. Main account's confirmed roster lists `gpt-6-astra` with `availability_nux: { message: "M" }` → bare native `gpt-6-astra` row carries exactly `{ message: "M" }`. +2. Same roster without the field, or with `null`, and a stale on-disk row carrying an old NUX → row ends with `null`. +3. Unconfirmed main roster (fetch failure) → `null`; never an old value. +4. A Pool account's roster carries a NUX for the same slug → no bare row and no account-bound row shows it. +5. Malformed NUX (string, array, missing/empty/non-string message, extra fields) → dropped or reduced to `{ message }`; roster confirmation and access programs unaffected. +6. Combo and native alias rows claiming the slug → no `availability_nux`. + +## Verification + +`bun test` on the new file plus `codex-forward-access-programs-writer`, `codex-model-entitlements-program-shape`, `codex-convergence-account-selectors`, `reserve-catalog`, `gpt6-native-rows`, layout and ratchet guards; `bun run typecheck`, `structure:check`, `privacy:scan`, `git diff --check`, docs-site build; `test:changed` in the `/private/tmp` checkout. Red-green on the new file. Independent review. Separate PR against `dev`, linked from #4213; the issue stays open until a user confirms the prompt on a current build. + +## Audit amendment (sol reviewer, NEAR-PASS) + +The account boundary, both writer call sites and the later catalog steps were confirmed. One blocker folded in: the new test also drives **both on-disk writers** (retained sync and convergence, following the pattern in `tests/codex-integration/codex-convergence-account-selectors.test.ts:343`) and asserts the *written catalog* for the main-account NUX, its removal on a later roster without it, Pool isolation, and alias/selector cleanup. Recorded limitations: the bare row follows the account in the Codex home OpenCodex manages, which is the App's login only when the App uses that home; convergence already republishes the catalog even when unchanged (`src/codex/convergence.ts:614`), which predates this change. `tests/codex-integration/codex-catalog.test.ts` sits 11 lines under its cap and is not touched. diff --git a/devlog/_plan/260927_release_train_4/provider-compat/060_merge.md b/devlog/_plan/260927_release_train_4/provider-compat/060_merge.md new file mode 100644 index 00000000000..769f96e756b --- /dev/null +++ b/devlog/_plan/260927_release_train_4/provider-compat/060_merge.md @@ -0,0 +1,25 @@ +# Phase 6 — merge and integration receipts + +Previous D (wp5): #6117 at `d2bfd2808f` verified (red-green 11/11, focused 451/451, flagged `test:changed` files rerun 617/0, sol review PASS). Direction: land #6097, #6112 and #6117 on `dev`, then close the carried originals and record receipts. + +## Gate per PR (repeat until merged) + +1. `git fetch origin`. If the PR head does not contain `origin/dev`, rebase onto it, check that the diff for this PR's paths is unchanged (or rerun the focused set if the union touched them), push with `--force-with-lease` naming the old head, and wait again. +2. Every Cross-platform CI job and `enforce-target` on the exact head must have concluded `success` (or `skipped` by the workflow's path filter); pending, cancelled or older-head runs don't count. `label` and CodeRabbit are advisory. +3. Codex and CodeRabbit threads: correct findings fixed, others answered. +4. Post a maintainer-integration comment (MAINTAINERS.md: dev-only integration without a second approval) naming the exact head, the CI run id and the security review, then squash-merge with `--admin` and a message that keeps the `Co-authored-by` trailer. + +Order: #6097 (oldest CI), then #6112, then #6117. Each merge puts the others behind `dev`, so they are rebased right after it and CI reruns. + +## After all three land + +- Comment on #5927 and #5497 with a thank-you, the replacement PR and the merge SHA, then close them. +- Comment on #4213 with the merged fixes (image Pool path, trial prompt); keep it open until a user confirms. +- One devlog PR from `dev` carrying `devlog/_plan/260927_release_train_4/provider-compat/` with all phase results. +- Dispatch Cross-platform CI on `dev` (it does not run on push) and confirm the run's head SHA equals the final `dev` head and every job succeeds. If `dev` breaks from this lane's change, fix it through a new PR. + +## Audit amendment (sol reviewer, FAIL → folded) + +1. CI evidence is the Cross-platform CI aggregate `ci` job on the exact head concluding `success`, plus every job that the workflow requested for that event and lane concluding `success`. Jobs gated to `workflow_dispatch` (Windows shards, macOS control) or to other paths are legitimately skipped on a PR (`.github/workflows/ci.yml:757,839`), and the aggregate already tells real failures from legitimate skips (`ci.yml:1532,1684`). `enforce-target` must succeed for the PR event. After merge, dispatch `ci.yml` on `dev` with the default `lane=all` and apply the same rule (`privacy-gate` is PR-only, `ci.yml:1199`). +2. Union check before every push that follows a rebase: both test-layout registries must list every new test file from the PRs already merged, and the layout guards, file-size ratchet, typecheck and the PR's focused set must pass on the rebased head. +3. Merge with `gh pr merge --squash --admin --match-head-commit --subject --body-file <file>`. The body file carries the summary and a standalone `Co-authored-by` trailer where the PR carries someone else's work; read the squash commit back after the merge to confirm the trailer.