-
Notifications
You must be signed in to change notification settings - Fork 1.1k
docs(providers): state which account each provider login spends #4239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
aa78890
091b7f3
6db526a
ee61b48
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| # Dispatch packet — L7 (revision 5) | ||
|
|
||
| Round unit: `devlog/_plan/260911_lane_dispatch_round` on `dev`. Base freeze: `origin/dev` `6d3ad12e3` (2.51.0). | ||
| Five audit rounds shaped this packet. The last one was a seven-lane feasibility check that asked whether each stack is implementable inside its owned paths; three lanes came back with gaps, and the fixes are folded here. `010_lane_partition.md` is the authoritative ownership list; `130_wp4_feasibility.md` records why each path was granted. | ||
|
|
||
|
|
||
| ## Shared frame | ||
|
|
||
| **Repository.** Your worktree is named in your packet, already checked out on your lane branch, cut | ||
| from `origin/dev` `6d3ad12e3` (2.51.0). Work only there. Do not add, move, or remove a worktree. | ||
|
|
||
| **Loop.** Run `$codexclaw:cxc-loop` as HOTL for your lane: one work-phase per issue, in order. Your | ||
| goal ends when your last PR is green and reported, not when the code looks right. | ||
|
|
||
| **Subagents.** Unlimited `xai/grok-4.6` subagents, read-only, spawned with `spawn_agent` | ||
| (`model: "xai/grok-4.6"`). Use them to reproduce, to read the call sites you are about to change, to | ||
| find a second caller of a helper you are touching, and to review your staged diff adversarially | ||
| before you push. A finding enters your work only with an exact `path:line` anchor. Subagents never | ||
| write, commit, push, or call a mutating `gh`. Treat a `fail` verdict the way this round did: fold it | ||
| in and re-audit. This packet is at revision 3 because two audit rounds rejected revisions 1 and 2. | ||
|
|
||
| **MUST NOT.** | ||
|
|
||
| - No local product suite: no `bun test`, no `bun run test`, no `bun run test:changed`, no | ||
| `bun run typecheck`, no `bun run build:gui`, no `bun install`. Report them as `NOT RUN`. | ||
|
Comment on lines
+24
to
+25
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Permit the required
🤖 Prompt for AI Agents |
||
| - No merge, no release, no force-push to a shared branch, no direct push to `dev`. | ||
| - No path outside your owned list, including paths a carried PR happens to touch. Dropping a hunk | ||
| from a carried PR is expected; report what you dropped. | ||
| - No locale key in `gui/src/i18n/*`. If you need one, stop and report. | ||
| - No security write-up in `devlog/`; scratch space only, per `AGENTS.md`. | ||
|
|
||
| **MUST.** | ||
|
|
||
| - Prefix every mutating git command with `git -c core.hooksPath=/dev/null`. This repository's hooks | ||
| can start a GUI install, typecheck, and build, which the no-local-suite rule forbids. | ||
| - Push with `--no-verify`. | ||
| - Write the focused regression test `AGENTS.md` requires for a behaviour change, in the domain | ||
| directory beside the existing tests for that subsystem, and register it in both | ||
| `scripts/test-layout/layout.json` `explicit` and `tests/fixtures/test-layout-expected.json`. You | ||
| will not run it; hosted CI will. Those two maps are append-only and other lanes are adding to them | ||
| too; the orchestrator resolves the conflicts at merge, so do not skip the entry. | ||
| - Fill every section of `.github/PULL_REQUEST_TEMPLATE.md` and put `Closes #<issue>` in the body. In | ||
| **Verification**, state that the local suite, typecheck, and build were `NOT RUN` by operator | ||
| instruction and that hosted CI on the exact pushed head is the proof. | ||
| - When you carry another author's PR, add a `Co-authored-by` trailer in a branch commit. Resolve the | ||
| address with `gh api users/<login> --jq '.id'` and use `<id>+<login>@users.noreply.github.com`. | ||
| - Keep a devlog unit under `devlog/_plan/260911_l<N>_<slug>/`. | ||
|
|
||
| **Stacking.** First PR targets `dev`; the second targets the first PR's head branch, the third the | ||
| second. Retarget a child to `dev` after its parent lands. No native GitHub stacks. | ||
|
|
||
| **Decisions already made for you.** Both audit rounds found items where the issue left a real choice | ||
| open. Those calls are recorded in your packet in bold. Implement the recorded decision; if you think | ||
| it is wrong, report the reason and stop. | ||
|
|
||
| **Stop conditions.** Stop and report when the fix needs a path you do not own, when it needs a policy | ||
| no issue has fixed, when a locale key is unavoidable, or when hosted CI fails for a reason outside | ||
| your diff. | ||
|
|
||
| **Report format.** Per PR: number, exact head SHA, CI run id and conclusion, the issue it closes, the | ||
| co-authors credited, the hunks you dropped from a carried PR, and any decision you made. Say | ||
| `NOT RUN` for local checks. | ||
|
|
||
| **Decision boundary.** You do not merge, do not close another author's PR, and do not rank your lane | ||
| against another. When your last PR is green, report and stop. | ||
|
|
||
| ## L7 — documentation | ||
|
|
||
| Worktree `~/.codex/worktrees/260911-l7/opencodex`, branch `codex/260911-l7-docs`. | ||
|
|
||
| Owned: `docs-site/src/content/docs/guides/providers.md`, | ||
| `docs-site/src/content/docs/guides/remote-hub.md`. You are the only lane that may edit | ||
| `providers.md`; L2 will send you wording rather than editing it. | ||
|
Comment on lines
+71
to
+73
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Include the required test and registration paths in the owned scope. Lines 37-40 require one focused regression test and registrations in Add the test directory, both registration files, and the L7 devlog directory to the owned list. The roadmap lists these required paths in 🤖 Prompt for AI Agents |
||
|
|
||
| 1. **#4215 — state whether each provider login consumes a subscription allowance or bills per | ||
| token.** Write the rule per authentication mode, then one explicit line per provider supporting | ||
| both. | ||
| 2. **#4200 — the remote hub guide breaks on a fresh config** (nested `ocx config set` fails when the | ||
| parent object is absent) and has no macOS data-plane TLS example. English source first; | ||
| translations are a follow-up. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| # L7 roadmap — two documentation units, locked before either guide is edited | ||
|
|
||
| Base: `origin/dev` at rebase time, branch `codex/260911-l7-docs`. Packet: `000_packet.md`. | ||
|
|
||
| This is the docs-first cycle the loop requires. Nothing in `docs-site/` is edited until the two | ||
| wordings below are locked, because both issues propose wording that the source does not support and | ||
| writing first would have shipped two wrong claims. | ||
|
|
||
| ## Unit order | ||
|
|
||
| 1. **#4215** — `docs-site/src/content/docs/guides/providers.md`. The rule per authentication mode, | ||
| then one explicit line per provider that accepts both a subscription login and an API key. | ||
| Wording locked in `020_4215_wording.md`. | ||
| 2. **#4200** — `docs-site/src/content/docs/guides/remote-hub.md`. Fresh-config object | ||
| initialization, field preservation, data-plane versus management-plane separation, and a macOS | ||
| Tailscale Serve data-plane TLS path. Recipe locked in `030_4200_recipe.md`. | ||
|
|
||
| PR 1 targets `dev`. PR 2 targets PR 1's head branch and is retargeted to `dev` after PR 1 lands. | ||
|
|
||
| ## What this lane may touch | ||
|
|
||
| `docs-site/src/content/docs/guides/providers.md`, `docs-site/src/content/docs/guides/remote-hub.md`, | ||
| one new regression test per unit plus its two registrations in `scripts/test-layout/layout.json` and | ||
| `tests/fixtures/test-layout-expected.json`, and this devlog unit. Nothing else — no `src/`, no | ||
| `gui/src/i18n/`, and none of the seven translated copies of either guide. Translations are a | ||
| follow-up, which is what #4200's own review comment asks for. | ||
|
|
||
| ## Verification posture | ||
|
|
||
| The local product suite is NOT RUN by operator instruction: no `bun test`, no `bun run test`, no | ||
| `bun run test:changed`, no `bun run typecheck`, no `bun run build:gui`, no `bun install`. Hosted CI | ||
| on the exact pushed head is the only product evidence this round accepts. Every mutating git command | ||
| is prefixed with `git -c core.hooksPath=/dev/null` and every push uses `--no-verify`, because this | ||
| repository's hooks can start a GUI install, a typecheck, and a build — the same forbidden work | ||
| through a side door. | ||
|
|
||
| Confidence that would normally come from a local run comes instead from read-only `xai/grok-4.6` | ||
| subagents: every claim written into either guide carries a `path:line` anchor recorded in the unit | ||
| doc, and the staged diff is reviewed adversarially before each push. | ||
|
|
||
| ## Regression tests | ||
|
|
||
| Both guards live in `tests/ci-workflows/`, beside `docs-429-failover-claims.test.ts`, which is the | ||
| existing precedent for pinning a published claim that drifted away from the runtime. A subagent | ||
| confirmed that directory is the docs-guard home and that no test anywhere asserts the | ||
| `config parent path not found` behaviour today. | ||
|
|
||
| Each new file is registered in `scripts/test-layout/layout.json` `explicit` and in | ||
| `tests/fixtures/test-layout-expected.json`. Both maps are append-only and other lanes are appending | ||
| too; the orchestrator resolves the merge conflicts. | ||
|
|
||
| ## Open decisions this lane had to make | ||
|
|
||
| - **#4215 form.** The issue left "table or per-section sentence" open. The packet decided: the rule | ||
| per authentication mode first, then one explicit line per dual-mode provider. A table carries the | ||
| per-provider lines because the reader's question is a lookup. | ||
| - **#4215 scope.** The issue left "dual-mode providers only, or the full roster" open. The packet | ||
| decided dual-mode only. Providers that offer exactly one mode are already unambiguous. | ||
| - **#4215 verification surface.** The issue asks the guide to point at "the account card in the | ||
| dashboard". No such per-account badge exists. The guide points at the surface that does exist — | ||
| see `020_4215_wording.md`. | ||
| - **#4200 product change.** The issue's review comment explicitly leaves auto-creating a missing | ||
| parent object out of scope. This lane documents the CLI as it behaves and does not touch | ||
| `src/cli/config-command.ts`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| # #4215 — locked wording: which account a provider login spends | ||
|
|
||
| Target: `docs-site/src/content/docs/guides/providers.md`. Closes #4215. | ||
|
|
||
| Revision 3. Revisions 1 and 2 were each rejected by an adversarial read-only audit that opened every | ||
| anchor. Revision 1 had wrong line numbers; revision 2 had a deeper problem, and fixing it changed | ||
| what this document promises. | ||
|
|
||
| ## The framing correction that revision 3 is built on | ||
|
|
||
| Revision 2 wrote sentences like "spends the Claude subscription allowance" and cited a registry or | ||
| quota line. The audit's objection is correct and it matters: **those lines do not state vendor | ||
| billing terms.** `registry.ts` states an id, a transport, a base URL and an auth kind. | ||
| `quota.ts` states which usage windows OpenCodex reads. Neither knows what Anthropic or xAI charges. | ||
| Citing them as proof of a billing claim is exactly the kind of overreach a maintainer should reject, | ||
| and `quota.ts:1404`-`1409` says outright that the Anthropic endpoint reports no tier at all. | ||
|
|
||
| So the guide answers the question the reader is actually asking — *which account does this spend?* — | ||
| in terms the source can support: | ||
|
|
||
| - which credential the request carries, | ||
| - which account that credential authenticates as, | ||
| - which usage windows OpenCodex reads back, if any. | ||
|
|
||
| Whether that account is billed per token or against a plan is the vendor's term, not OpenCodex's. The | ||
| guide says so once, plainly, and does not pretend otherwise per provider. | ||
|
|
||
| ## Why the issue's own suggested wording could not be shipped verbatim | ||
|
|
||
| The issue proposes "an OAuth or subscription login consumes that subscription's allowance, an API key | ||
| bills that key's account per token, and OpenCodex does not convert one into the other." | ||
|
|
||
| **"Never converts" is refuted.** `orcarouter-oauth` is a browser-consent PKCE flow whose purpose is | ||
| to mint a durable user-owned `sk-orca-…` key and store it in the OAuth credential store | ||
| (`src/oauth/orcarouter.ts:12` for the prefix, `:95`-`99` for the long-lived-key comment; | ||
| `src/providers/registry.ts:1441`). The login *is* the key delivery mechanism. | ||
|
|
||
| **"Bills per token" is not universally true of a key.** Several key presets are subscriptions bought | ||
| as a key: `zai` GLM Coding Plan (`registry.ts:2544`), `zhipu-bigmodel-coding` whose comment says | ||
| "this endpoint is the subscription product" (`registry.ts:2619`), `kimi-code` "API-key form of the | ||
| same Kimi Code Plan transport" (`registry.ts:2971`). CodeBuddy is blunter: "Free/trial/promotional/ | ||
| subscription credits draw from the same official API-key pool" (`registry.ts:3234`). | ||
|
|
||
| **What is true, and is what gets written.** One request carries exactly one credential family. | ||
| `DispatchBinding` is either `{kind:"oauth"}` or `{kind:"api-key"}` (`src/server/responses/core.ts:3974`-`3976`). | ||
| When an OAuth credential cannot be resolved the request fails back to the caller — the catch returns | ||
| `401 authentication_error` and the same block returns 429, 409 and 400 on other paths | ||
| (`core.ts:4268`-`4393`) — and at no point does it reach for a stored key. Key-pool 429/401 failover | ||
| refuses oauth and forward outright (`src/providers/key-failover.ts:99`). *Audit correction: revision 2 | ||
| said 401 was the only outcome of that block.* | ||
|
|
||
| **The key-deletion claim needs a carve-out.** OAuth login deletes `apiKey` and `apiKeyPool` under | ||
| the comment "OAuth-only providers must never retain credentials for a different auth mechanism" | ||
| (`src/oauth/index.ts:1494`-`1497`). The next block restores them when the preset sets | ||
| `allowKeyAuthOverride`, and if the previous `authMode` was `key` or unset it sets `authMode` back to | ||
| `"key"` (`oauth/index.ts:1498`-`1519`). On `xai` and `github-copilot`, running `ocx login` over an | ||
| existing key configuration can leave the provider still billing the key. The guide says so. | ||
|
|
||
| ## Locked rule, per authentication mode | ||
|
|
||
| - `forward` — the ChatGPT/Codex login. The request carries your Codex credential, so it spends the | ||
| ChatGPT plan behind that login and reports that plan's Codex quota windows. **Which windows exist | ||
| is plan-dependent** — the five-hour window is not universal and some plans report weekly only | ||
| (`src/codex/quota.ts:441`-`442`). It never reads an API key: an `openai` provider row with no | ||
| `authMode` is backfilled to `forward` in `routedProviderConfig` (`src/router.ts:313`-`319`, `:429`), | ||
| and the forward branch of the adapter uses the Codex account override, with `provider.apiKey` only | ||
| on the else branch (`src/adapters/openai-responses.ts:2319`-`2367`, `:2363`). *Audit corrections: | ||
| revision 2 promised all three windows unconditionally and cited `router.ts:654`, which is a | ||
| namespace-check mirror rather than the backfill.* | ||
| - `oauth` — a subscription login. The request carries a stored access token, so it spends whatever | ||
| account you logged in as, and OpenCodex reports whichever usage windows that provider exposes. | ||
| - `key` — the request carries the key you supplied, so usage lands on the account that owns it, on | ||
| that key's own terms: metered usage for a pay-as-you-go API account, or a plan allowance when the | ||
| key *is* a coding-plan subscription. The guide says "per token" only where the source does, as in | ||
| `meta-model` — "every call is metered per token" (`registry.ts:1639`). | ||
| - A request never swaps families. | ||
|
|
||
| Two stated exceptions, because both are shipped behaviour a reader can hit: | ||
|
|
||
| - `xai` and `github-copilot` accept `authMode: "key"` on the same provider id | ||
| (`allowKeyAuthOverride`, `registry.ts:1274` and `registry.ts:3157`). Which account pays is a | ||
| persisted configuration choice, and per the carve-out above, logging in does not necessarily take | ||
| you out of key mode. | ||
| - `orcarouter-oauth` mints a user-owned API key from the consent flow | ||
| (`src/oauth/orcarouter.ts:12`, `:95`-`99`, `registry.ts:1441`). From then on the request carries a | ||
| key, so it falls under the `key` rule above: usage lands on the OrcaRouter account that owns it. | ||
|
|
||
| ## Locked per-provider lines | ||
|
|
||
| Each cell states which account the credential authenticates as, and nothing about vendor pricing that | ||
| the anchor does not carry. | ||
|
|
||
| | Provider | Subscription login | API key | | ||
| |---|---|---| | ||
| | OpenAI / ChatGPT | `openai` — Codex login, `authKind: "forward"`; spends the ChatGPT plan behind that login (`registry.ts:1222`) | `openai-apikey` — a separate preset; usage lands on the OpenAI Platform account that owns the key (`registry.ts:1573`) | | ||
| | Anthropic | `ocx login anthropic` — signs in as your Claude account (`registry.ts:1444`); OpenCodex reads its five-hour and seven-day usage windows, and that endpoint reports no tier (`quota.ts:1404`-`1409`) | `anthropic-apikey`, labelled "Direct Anthropic API billing — no Claude subscription" (`registry.ts:1472`) | | ||
| | xAI | `ocx login xai` — the Grok CLI subscription gateway; OpenCodex reads SuperGrok weekly credits, else the legacy monthly pool (`quota.ts:1285`-`1370`) | same `xai` id with `authMode: "key"` (`registry.ts:1274`); key mode targets `https://api.x.ai/v1` (`registry.ts:1272`), so usage lands on that API account | | ||
| | Kimi | `ocx login kimi` — "Log in with your Kimi account" (`registry.ts:1481`, note at `:1496`) | `kimi-code`, "API-key form of the same Kimi Code Plan transport" (`registry.ts:2971`) | | ||
| | Command Code | `ocx login command-code` — OpenCodex reads five-hour and weekly windows plus a credit balance (`quota.ts:2392`-`2447`) | `commandcode` on `/provider/v1` (`registry.ts:2262`) | | ||
| | GitHub Copilot | `ocx login github-copilot` — "Requires an active Copilot subscription" (`registry.ts:3183`) | same `github-copilot` id with `authMode: "key"` (`registry.ts:3157`) | | ||
| | OrcaRouter | `ocx login orcarouter-oauth` — consent mints a user-owned, long-lived `sk-orca-…` key (`src/oauth/orcarouter.ts:12`, `:95`); the request then carries a key, so usage lands on that key's account | `orcarouter` — the same key pasted by hand (`registry.ts:1924`) | | ||
| | Meta Muse | `ocx login meta-muse` (`registry.ts:1650`) imports the Muse Code CLI key. Meta scopes that credential to its own CLI, so this is an **unsupported** use: how the calls settle is not observable from the API and every call should be treated as billable against your account (`registry.ts:1666`) | `meta-model` is the supported path — "every call is metered per token", and a Muse Code subscription does **not** work there (`registry.ts:1639`) | | ||
| | Google Antigravity | `ocx login google-antigravity` — signs in with your Google account over the Cloud Code Assist wire (`registry.ts:1997`) | no key mode on this id. `google` is a separate AI Studio Gemini preset reached with its own key (`registry.ts:1978`), so it is a different product rather than a key mode for this login | | ||
|
|
||
| *Audit corrections folded here: Meta Muse retargeted from `:1673` (which is `umans`) to `:1650` and | ||
| `:1666`; Kimi, Antigravity and xAI reworded so the sentence matches what its anchor says. A third | ||
| audit pass then removed the last two vendor-billing words the anchors did not carry — OrcaRouter's | ||
| "balance" and Google's "billing".* | ||
|
|
||
| Login-only, no API-key equivalent: `cursor` (`registry.ts:1233`), `kiro` (`registry.ts:1513`), | ||
| `nous` (`registry.ts:1546`). Two audits searched the registry for a login+key twin this table might | ||
| have missed and found none. | ||
|
|
||
| ## Where the reader verifies their own setup | ||
|
|
||
| The issue asks the guide to say that "the account card in the dashboard shows which mode an account | ||
| is using". It does not. The per-account rows render alias, email, id, health, reauth and active | ||
| state, and carry no auth-mode badge (`ProviderAuthPanel.tsx:504`-`527`). | ||
|
|
||
| The mode is a **provider-level** field (`provider-routes.ts:737`) rendered as the Connection block's | ||
| **Authentication** row (`ProviderOverview.tsx:167`, label at `:168`). Its `authModeLabel` is `OAuth`, | ||
| `API key`, `ChatGPT passthrough` or `Local`, with a `No key needed` fallback for a preset that needs | ||
| no credential (`gui/src/i18n/en.ts:1136`-`1194`, `ProviderRail.tsx:34`). *Audit correction: revision 2 | ||
| listed only four labels.* | ||
|
|
||
| The guide does **not** claim the separate **AUTHENTICATION** block prints those labels — for forward | ||
| it prints "Codex passthrough" (`en.ts:1425`), and for oauth and key it prints login state and | ||
| "API key configured". | ||
|
|
||
| **Decision: the guide points at the surface that exists** and names the labels that row actually | ||
| renders, rather than repeating the issue's phrasing and sending readers hunting for a badge that was | ||
| never built. No locale key is added; every label quoted already ships. | ||
|
|
||
| ## Regression guard | ||
|
|
||
| `tests/ci-workflows/docs-provider-billing-claims.test.ts`, beside `docs-429-failover-claims.test.ts`. | ||
| It pins the claims that would silently rot: the per-mode rule is present, every dual-mode provider | ||
| above has a row naming both of its modes, and the guide does not reintroduce the refuted absolute | ||
| "never converts" phrasing. Registered in `scripts/test-layout/layout.json` and | ||
| `tests/fixtures/test-layout-expected.json`. NOT RUN locally; hosted CI is the proof. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Synchronize the packet revision.
Line 1 identifies this packet as revision 5, but Line 20 says revision 3. Update the stale revision value before this packet is used to drive the lane.
🤖 Prompt for AI Agents