diff --git a/bin/ocx.mjs b/bin/ocx.mjs index 2018c0bd0d0..4cd3380ae18 100755 --- a/bin/ocx.mjs +++ b/bin/ocx.mjs @@ -52,7 +52,7 @@ import { } from "../src/update/npm-cache-preflight.mjs"; import { handoffWindowsTrayForUpdate, planWindowsTrayUpdate } from "../src/update/tray-update-plan.mjs"; import { bootRestoreProbe, launcherUsableAfterNpmUpdate, transactionalNpmUpdate } from "../src/update/transactional-install.mjs"; -import { npmUpdateFailureGuidance } from "../src/update/update-failure-guidance.mjs"; +import { manualUpdateFailureGuidance, npmUpdateFailureGuidance } from "../src/update/update-failure-guidance.mjs"; import { CODEX_CLI_VERSION_MANAGER_ROOT_ENV_SLOTS, isCodexCliUpdateInspectionArgv, @@ -816,14 +816,11 @@ function runPackageManagerSelfUpdate(manager) { // legacy in-place install (which deletes live first) is exactly the wrong rescue — // it recreates the #1849 destruction path. Report and stop; the boot probe and the // recovery marker cover the swap-window states. - const manual = manager === "pnpm" - ? `pnpm add -g --allow-build=bun ${PKG}@${tag}` - : `npm install -g --allow-scripts=bun ${PKG}@${tag}`; // An unexpected exception leaves the active package path unproven for either manager. // Do not run service/tray/proxy recovery through a possibly half-swapped tree. postUpdateLauncherUsable = false; console.error(`opencodex: ${manager} update failed unexpectedly (${error?.message ?? error}). ` + - `The live install was not knowingly modified; run 'ocx update' again or reinstall with ${manual}.`); + `Run 'ocx update' again or follow the manual recovery steps below.`); res = { status: 1 }; } if (res.status !== 0) recoverStoppedRuntimeAfterFailure("update failed"); @@ -861,13 +858,17 @@ function runPackageManagerSelfUpdate(manager) { // Phase-specific next step (#5624): whether the previous version is still in place decides // between "retry" and "restore", and a bare reinstall must follow a stop (#5496). const guidance = npmUpdateFailureGuidance({ ...npmFailure, pkgName: PKG, version: latest || undefined, tag }); - console.error(`\nUpdate failed (npm ${npmFailure.phase}). ${guidance.lines.join(" ")}`); + console.error(`\nUpdate failed (npm ${npmFailure.phase}). ${guidance.lines.join("\n")}`); process.exit(1); } - const manual = manager === "pnpm" - ? `pnpm add -g --allow-build=bun ${PKG}@${tag}` - : `npm install -g --allow-scripts=bun ${PKG}@${tag}`; - console.error(`\nUpdate failed (${manager} exit ${res.status ?? "?"}). Try manually: ${manual}`); + const guidance = manualUpdateFailureGuidance({ + bin: manager, + args: manager === "pnpm" + ? ["add", "-g", "--allow-build=bun", `${PKG}@${latest || tag}`] + : ["install", "-g", "--allow-scripts=bun", `${PKG}@${latest || tag}`], + owner, + }); + console.error(`\nUpdate failed (${manager} exit ${res.status ?? "?"}). ${guidance.join("\n")}`); process.exit(1); } diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index bfb9a1352a7..7d114386757 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -2645,7 +2645,7 @@ dependencies = [ [[package]] name = "opencodex-desktop" -version = "2.77.0" +version = "2.78.0" dependencies = [ "base64 0.22.1", "dbus", diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index f162e1d8411..352cf48f85c 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "opencodex-desktop" -version = "2.77.0" +version = "2.78.0" description = "OpenCodex desktop shell" authors = ["OpenCodex contributors"] license = "MIT" diff --git a/desktop/src-tauri/tauri.conf.json b/desktop/src-tauri/tauri.conf.json index 2e5a7813710..0df3a21b925 100644 --- a/desktop/src-tauri/tauri.conf.json +++ b/desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "OpenCodex", - "version": "2.77.0", + "version": "2.78.0", "identifier": "com.opencodex.desktop", "build": { "frontendDist": "../ui", diff --git a/devlog/_fin/261003_cli_gui_parity/000_plan.md b/devlog/_fin/261003_cli_gui_parity/000_plan.md new file mode 100644 index 00000000000..37283191a2d --- /dev/null +++ b/devlog/_fin/261003_cli_gui_parity/000_plan.md @@ -0,0 +1,60 @@ +# Make dashboard workflows usable from the terminal + +OpenCodex already implements many dashboard operations in its CLI, but discovery omits working commands and several workflows expose only some GUI fields. This unit completes the eligible task gaps, makes existing commands discoverable, and gives the shipped ocx skill verified task recipes. Operators keep the same management validation and consent boundaries, with explicit local/live target selection where those effects differ. + +Reader: maintainers reviewing the six-layer manual PR stack; they need the scope, exact command contracts and proof for each layer. + +## Loop contract + +- Archetype: satisfy a fixed, source-audited task inventory, followed by residual accounting. +- Trigger: user requested near-GUI CLI parity, improved shipped skill, inherited-model subagents, repeated PABCD and a published stack. +- Goal: every eligible GUI task has a named command/sequence, useful help, safe output, documented target and behavioral evidence; genuine exclusions and duplicate views remain explicit. +- Non-goals: GUI redesign, provider routing redesign, new auth authority, raw API/config escape hatches, live-user operation, native GitHub stacks, merging, release or deployment. +- Verifier: numbered phase acceptance tests plus the baseline/QA strategy in 003_verification_strategy.md; a docs checker observes these exact plan files and task ownership before B. +- Stop: six open, reviewable PRs with successful current-head hosted CI; all task ledger rows closed as verified, alias or justified exclusion, with no unexplained UNKNOWN. +- Durable artifacts: this unit, 008_task_ledger.json and ignored command/QA receipts under this session. +- Outcomes: DONE requires implementation, synchronized skill/docs, independent reviews and CI. Real unsupported prerequisites/authority are reported under host blocked rules; workload or compaction is not completion. +- Escalation: main resolves source/architecture conflicts; ask the user only for genuinely new authority. Failed delegates follow the bounded retry/retirement owner; do not swap models silently. +- Resources: no user token/cost/time cap. Tool scope is repository editing, isolated local fixtures and GitHub PR/CI for this repository. No real accounts, credentials, running services, paid upstream calls or destructive user files are QA targets. + +## Baseline and source owners + +Baseline dev is 9f89b7265b754eb681215ad327fc9459af37b9e1. The initial inventories contain 176 task-entry rows, with two additional embedded dashboard setting groups subsequently found (memory models and compaction routing). Duplicate entry points are retained as aliases rather than silently dropped. Source-coverage labels in 004/005 are not passing runtime evidence. Executable 010..080 decisions supersede earlier source-join syntax proposals; the private audit record retains their history. + +The tree remains Bun TypeScript: src/cli owns domain handlers and pure help/capability data; existing server management modules own validation/live mutation; tests/cli plus domain server tests own proof; skills/ocx and docs-site own operating guidance; structure owns current contracts. Existing structure/manifest.json maps src/cli to runtime/config/integration/Desktop/release docs. Only affected owners are updated. + +## Dependency-ordered work phases and PR layers + +| Phase | Executable document | Dependency | Output / PR layer | +| --- | --- | --- | --- | +| wp0 | This roadmap, inventories and UX contract | none | Docs-only audit; locks the remaining designs before source edits | +| wp1 | 010_discovery_foundation.md | wp0 | Pure metadata and generated-document capacity; layer 1 | +| wp2 | 020_existing_workflow_discovery.md | wp1 | Accurate existing task discovery and skill routing; finish layer 1 | +| wp3 | 030_provider_management.md | wp2 | Bounded input and exact provider live workflows; layer 2 | +| wp4 | 040_models_routing.md | wp3 | Models, picker order, combos and routing profile edits; layer 3 | +| wp5 | 050_accounts_runtime_settings.md | wp4 | Account policies, explicit auth options, agent/settings/v2 parity; layer 4 | +| wp6 | 060_integrations_maintenance.md | wp5 | Live Desktop profile, integration recovery, storage and Hub reads; layer 5 | +| wp7 | 070_observation_api_tools.md | wp6 | Timeline/log fidelity, scoped usage and chosen-key/audio tools; layer 6 | +| wp8 | 080_acceptance_publication.md | wp7 | Residual task proof, final skill/docs and all exact-head PR receipts; finish layer 6 | + +The final source adjudication found bounded read gaps in log selection, model-row search and Tray-equivalent quota/filtered totals. [083_residual_read_workflows.md](083_residual_read_workflows.md) adds those repairs inside wp8's P amendment. [081](081_acceptance_revalidation.md) records the pinned-dev propagation and closure sequence; [082](082_acceptance_contract.md) defines the final evidence ledger. These additions preserve the fixed objective and all acceptance criteria. + +Every work phase runs a full P→A→B→C→D cycle. wp0 is code-free. Later P revalidates its existing decade doc and quotes the previous D conclusion; changes to decisions return to the same architect before A. Branch names: codex/cli-parity-foundation → codex/cli-parity-providers → codex/cli-parity-models → codex/cli-parity-accounts → codex/cli-parity-integrations → codex/cli-parity-observation. The bottom targets dev; each child targets its open parent. No native stack registration or merge is authorized. + +## Coverage and decisions + +- 002_terminal_ux.md defines terminal behavior and progressive disclosure. +- 003_verification_strategy.md records real baseline checks and verification scope. +- 004_settings_coverage.md and 005_operations_coverage.md preserve field-level source joins. +- 007_architecture_decisions.md records proposal dispositions, additive field chain and target boundaries. +- 008_task_ledger.json assigns every row to a phase; historical labels are retained while current status/evidence advance. + +Source comparison established that local provider/custom-model/v2/logout effects differ from live management receipts. --live is therefore explicit and never inferred from proxy availability or --json. Existing local behavior remains available. Browser-session identity actions stay outside automated parity; data-plane API testing is included with the same data-plane admission authority. + +## Alternatives rejected + +A generic API request command would expose routes without usable task contracts. Generic config writes would bypass existing live validation and completion receipts. A new command framework would duplicate the existing Capability/domain-handler architecture. Declaring routes that local commands never fetch would make coverage appear complete while remaining false. None is used. + +## Phase records + +wp0 roadmap was locked after independent audits passed. Inventory corrections already folded: existing v2 verbs are real local implementations, not absent agent verbs; model preset custom is disabled in the GUI; Lab local commands must not acquire fictitious HTTP coverage; memory-model and compaction-routing controls need explicit rows. Whole-plan architect reflection is ALIGNED at a095a4e514d6d8e5a37dbf05a66b2c9156ebaeeb28a8e0fb878bc76f931c27b1; the serializer and data-plane contract amendments were audited independently and passed. See 009_roadmap_lock.md. diff --git a/devlog/_fin/261003_cli_gui_parity/002_terminal_ux.md b/devlog/_fin/261003_cli_gui_parity/002_terminal_ux.md new file mode 100644 index 00000000000..20ed64696fe --- /dev/null +++ b/devlog/_fin/261003_cli_gui_parity/002_terminal_ux.md @@ -0,0 +1,29 @@ +# Terminal UX contract + +This is a repeated-use management tool for people and automation. The design preserves the compact root help and contextual family navigation delivered by the CLI help stack; it extends task coverage without turning the terminal into an endpoint debugger. + +Design variance: 2/10. Motion: 1/10 (no decorative animation). Density: D8 for the full reference, progressive disclosure for first use. Monospace, plain text and predictable line ordering carry meaning; color or symbols never determine correctness. Raster concepts do not help this utility surface and are intentionally omitted. + +## Task language + +- Extend existing nouns before adding new roots. Prefer list/show/status, set/update, enable/disable, apply/reset and their established domain equivalents. +- A family with no action offers a safe overview/help or an existing read-only default. It must never silently choose a write. +- Help is available without a live proxy and never runs a management action. Examples use registered grammar and placeholders, with a next read/verify command after a mutation. +- Short human output answers what changed, what remains unapplied and the next action. JSON preserves safe API DTO fidelity on stdout; diagnostics stay on stderr and failures keep documented exit codes. +- Empty lists distinguish no configuration, no matching rows and unavailable evidence. Do not render unavailable as zero or an empty successful inventory. +- Unknown/missing/duplicate arguments must fail before network writes. Boolean and numeric syntax reuse established parsers; structured inputs receive bounded, explicit JSON file/stdin forms only when the task's data shape needs them. +- IDs and names are encoded at path/query boundaries. No arbitrary method/URL command is counted as GUI parity. + +## Writes and recovery + +Existing management routes remain the source of validation, persistence, ownership and live application. A saved configuration is not proof that the client/runtime applied it; preserve refusal, partial application and restart-needed fields in output. + +Destructive operations retain explicit confirmation or preview as appropriate to the existing command family and server contract. Read, preview, apply and verify are distinguishable operations. No hidden retry of non-idempotent writes. New commands do not expand account identity, browser consent or secret-returning authority. + +No-running-proxy output names the recovery command. Invalid input names the argument and expected syntax without leaking its value when secret-bearing. Server errors preserve meaningful reason/hint fields and failure exit codes. Tests activate server refusal, not-found/conflict, malformed arguments and non-confirmed mutation paths. + +## Discoverability and skills + +The capability registry, help resolver, generated management reference and human recipes describe the same supported commands. Existing undeclared commands must be mapped before declaring a gap. Skill recipes start with readiness/version checks when operating a real proxy, then task-specific commands and verification. Human-only exclusions stay explicit and do not gain an API workaround. + +Each inventory row records GUI task, existing API contract, existing CLI route, implementation/discovery gap, final command and verification, or a reasoned exclusion. Coverage is measured against user tasks, not endpoint count or lines added. diff --git a/devlog/_fin/261003_cli_gui_parity/003_verification_strategy.md b/devlog/_fin/261003_cli_gui_parity/003_verification_strategy.md new file mode 100644 index 00000000000..a863a13ebb7 --- /dev/null +++ b/devlog/_fin/261003_cli_gui_parity/003_verification_strategy.md @@ -0,0 +1,30 @@ +# Verification strategy and observed baseline + +Baseline: `9f89b7265b754eb681215ad327fc9459af37b9e1`, the completed CLI-help stack on dev. This unit starts on `codex/cli-parity-foundation` in the existing managed worktree. + +Before implementation, the following ran successfully: + +| Command | Result | What it observes | +| --- | --- | --- | +| `bun run typecheck` | exit 0 | The repository TypeScript graph, including src/cli. | +| `bun run structure:check` | exit 0 | Structure manifest, declared source ownership, existing paths and invariant bindings; not prose correctness. | +| `bun run skill:surface:check` | exit 0 | Generated skill reference compared with src/cli/capabilities.ts. | +| `bun test tests/cli/cli-capabilities.test.ts tests/cli/cli-capabilities-arguments.test.ts tests/server/management-route-registry.test.ts tests/ci-workflows/skill-ocx.test.ts` | 62 pass, 0 fail, 1180 assertions | Capability parsing, leaf-module import boundary, management declaration reconciliation and shipped-skill command/consent boundaries. | + +Raw evidence is in the ignored `.tmp/cli-parity/baseline-*.log` files. The source-map command is unavailable in the installed plugin (it requires a codexclaw development checkout); bounded file inventories and exact source anchors replace it. + +## Per-unit proof + +Each implementation phase owns focused regression files with exact expected HTTP method/path/body, safe structured output, error/exit mapping and no-request assertions for rejected inputs. Tests use isolated homes and injected RuntimeApiDeps or a disposable loopback fixture; they do not mutate the operator's running proxy, credentials, client applications or accounts. + +Acceptance includes real CLI subprocess invocations with separated stdout/stderr, recorded exit codes and plain/pipe output. Help/version paths must remain offline and write-free. For mutations, run an equivalent isolated management-route contract scenario where appropriate; mocking a request records transport shape but does not by itself prove server acceptance. Input files/stdin, cancellation and confirmation paths receive explicit reachable scenarios. Each QA-owned temporary server/process has a teardown receipt. + +Existing global gates remain intact. New test files enter both test-layout maps. Source changes update their owning structure docs; public behavior updates CLI docs and the operating skill. Generated output is regenerated from its actual registry owner and checked for drift. Prose/UX semantics receive independent human-style review, not a test that only looks for a phrase. + +## Broad verification and publication + +Focused tests and type/static/document gates run locally. Full-suite and platform coverage use the current-head hosted PR CI for every layer; the repository's full suite has tens of thousands of tests and concurrent worktrees make repeated local full/changed runs disproportionate. This is the resource-scoped verification plan, not permission to ignore failing focused tests. Record exact commands and the coverage left to CI in each PR. + +The preceding CLI-help task recorded four local full-suite failures, all reproduced on its untouched baseline (three restart-lease failures and a pre-request directory snapshot EISDIR). Those records are history, not current-unit passing evidence. If this unit encounters a failure, inspect it and attribute or repair it with fresh source/command evidence. No skip, threshold relaxation, retry-as-fix or unrelated suite substitution is allowed. + +Each PR must retain its own current-head CI run, event, attempt, expected executed jobs, checkouts actually tested and open-review dispositions. Canceled/skipped/missing jobs are not passing tests. The stack remains open for review at completion; merge/release is not part of this request. diff --git a/devlog/_fin/261003_cli_gui_parity/004_settings_coverage.md b/devlog/_fin/261003_cli_gui_parity/004_settings_coverage.md new file mode 100644 index 00000000000..8b77672b8c2 --- /dev/null +++ b/devlog/_fin/261003_cli_gui_parity/004_settings_coverage.md @@ -0,0 +1,144 @@ +# Settings GUI-to-CLI field/workflow gap join + +Source-only completed join, 2026-10-03. One row for every one of the 67 tasks in the settings GUI/API inventory; IDs are prefixed `set-`. Joined the CLI source inventory to targeted provider/models/account/agent/combo/route/protocol/Lab handlers. No whole-CLI rescan, runtime/test/proxy/credential access, or source edits. Only this second-pass artifact is written. Exact source anchors establish implementation shape, not runtime success. + +Status meanings: + +- **COMPLETE**: named implementation covers the GUI fields/actions; no new operation identified. Runtime validation remains unrun. +- **DISCOVERY_ONLY**: GUI operation is implemented, but full leaf grammar is absent/incomplete in indexed capabilities/help; retain existing writer and improve discovery. This does not demand one capability entry per visual action. +- **IMPLEMENTATION_GAP**: at least one reachable field/action or material workflow behavior differs; existing supported variants are retained explicitly in the row. +- **EXCLUDED**: session-only consent or presentation boundary; no admin-token bypass should be proposed. +- **UNKNOWN**: field coverage exists but local/live convergence equivalence needs isolated proof. A generic config writer or HTTP passthrough is not parity. + +Commands are shown without repeated `ocx` prefixes for readability. Recommended syntax is a remediation proposal for the parent, not a claim that those flags already exist. `--json` is preserved where implemented; secrets use existing stdin contracts. Source scopes include the directly referenced handler helpers only. + +## Corrections and important joins + +1. Model preset `custom` is a disabled GUI state, not a selectable action (`gui/src/pages/Models.tsx:1550`). Existing preset/all CLI is sufficient; do not implement a custom flag solely because the server accepts the field. +2. Picker order and featured roster share `/api/subagent-models` but are distinct bodies. Existing `agent subagents set` does not implement picker ordering. +3. `--effort-mode` on combo sets `defaultEffortMode` (fallback/force), not GUI `reasoningEffortMode` (strict/adaptive). A similar name is not field parity. +4. Anthropic account threshold exists with `--account`; the same parser requires an account and does not expose the GUI pool-wide threshold/quota-window/enabled controls. Codex per-account threshold is also missing while pool-wide threshold exists. +5. Registry deferred claims for Lab public actions are stale per the CLI inventory. CompatibilityMatrix is read-only and the local Lab reads already exist; do not plan an HTTP-only rewrite. +6. GUI model-display-name mutation is absent from both dedicated CLI and current route declaration. Custom-model edit is not a substitute. Pricing reset, discovered metadata reset and context-cap variants are already implemented. +7. **Parent correction applied:** multi-agent mode/thread/keep-native/hint operations exist under the independent `ocx v2` root (`src/cli/dispatch.ts:539`, `src/cli/v2.ts:109`). M13/S04 are not missing writers; they are UNKNOWN for local/live equivalence with confirmed JSON/discovery/advisory differences. No duplicate `agent mode` taxonomy is proposed. +8. A11 reachability is now confirmed: picker and hard lock mount in `gui/src/pages/codex-set-multiauth.tsx:218` and `:221`. Client-settings ownership still needs sibling deduplication. + +## Complete task matrix + +| Task | Status | Existing command(s) | Exact missing variant / supported boundary | Recommended syntax / remediation | Source anchors (CLI and GUI/API) | +|---|---|---|---|---|---| +| set-P01 — Inspect configured providers, presets, authentication availability | DISCOVERY_ONLY | provider list/show/presets; inspect config; account list/current; status --json | Provider/catalog/authentication views exist across commands; static provider list is not the live config view. No separate raw OAuth-provider route command is needed if preset/auth inventory is preserved. | Index existing workflows and distinguish local provider list from inspect config; retain redaction. | src/cli/provider.ts:80; src/cli/provider-runtime.ts:235; src/cli/inspect.ts:209; src/cli/account.ts:44; gui/src/pages/use-providers-fetch.ts:34; src/server/management/provider-routes.ts:933 | +| set-P02 — Add a preset/custom provider; enable canonical OpenAI account provider | IMPLEMENTATION_GAP | provider add P [--adapter A --base-url U --api-key ... --default-model M --allow-private-network --force --set-default --sync]; provider edit openai --enabled on | Add parser has no responsesPath/authMode fields; account-mode is a later command. Local add returns needsSync; --json returns before --sync executes. Live validated POST parity and canonical enable are separate concerns. | Extend add with --responses-path PATH and --auth-mode key\|forward\|oauth; use server POST on explicitly live path, preserving existing offline mode. Make --json --sync report actual convergence instead of silently bypassing it. | src/cli/provider.ts:142; src/cli/provider.ts:156; src/cli/provider.ts:212; src/cli/provider.ts:279; src/cli/provider-runtime.ts:73; gui/src/components/AddProviderModal.tsx:203; src/server/management/provider-routes.ts:1161 | +| set-P03 — Edit provider transport, discovery, default model, note, network permission | IMPLEMENTATION_GAP | provider edit/update P --adapter A --base-url U --default-model M\|- --auth-mode MODE\|- --note TEXT\|- --api-key-transport MODE\|- --enabled on\|off --live-models on\|off --allow-private-network on\|off | GUI fields covered except Cursor upstreamHttpVersion. Empty note/default clearing exists; headers and context-tier CLI extras do not cover HTTP-version override. | Add --upstream-http-version http1.1\|- to provider edit; '-' sends null, omission leaves field unchanged; preserve server validation. | src/cli/provider-runtime.ts:59; src/cli/provider-runtime.ts:112; src/cli/provider-runtime.ts:312; gui/src/components/provider-workspace/ProviderSettings.tsx:233; src/server/management/provider-routes.ts:1412 | +| set-P04 — Configure and inspect request pacing | IMPLEMENTATION_GAP | inspect pacing --name P --json | Read exists; provider edit has no requestPacing writer, no provider/model RPM, delay or concurrency fields, no enabled/reset controls. | Add provider pacing P [--enabled on\|off] [--rpm N] [--min-interval-ms N] [--max-concurrent N] [--model M] [--clear] --json; a domain-specific --file rules.json can carry atomic full rules, not arbitrary API paths. Preserve untouched provider/model fields. | src/cli/inspect.ts:214; src/cli/provider-runtime.ts:65; src/cli/provider-runtime.ts:140; gui/src/components/provider-workspace/ProviderSettings.tsx:178; src/server/management/provider-routes.ts:917 | +| set-P05 — Enable/disable, choose default, remove provider | IMPLEMENTATION_GAP | provider edit P --enabled on\|off; provider set-default P; provider remove P | Enable is live PATCH. Set-default/remove are local paths; remove refuses current default while GUI reassigns an enabled replacement; local removal dependency check only examines targets. Thus CRUD names do not establish same live workflow. | Keep names, add/use live mutation path for set-default and remove with exact server receipt/dependency refusals. Explicit offline behavior must remain labeled. Do not emulate default deletion as two non-atomic requests. | src/cli/provider-runtime.ts:133; src/cli/provider.ts:345; src/cli/provider.ts:355; src/cli/provider.ts:459; gui/src/pages/use-providers-crud.ts:67; src/server/management/provider-routes.ts:1459 | +| set-P06 — Change OpenAI account mode | DISCOVERY_ONLY | provider account-mode pool\|direct --json | Standalone PATCH is covered, including server-side rebind/cache effects. Leaf lacks dedicated capability metadata per CLI inventory. | Document/index existing command; no new writer. | src/cli/provider-runtime.ts:252; gui/src/components/provider-workspace/ProviderSettings.tsx:289; src/server/management/provider-routes.ts:1421 | +| set-P07 — Atomically edit provider JSON | IMPLEMENTATION_GAP | config import exists but is not provider-editor CAS parity | No dedicated baseline/next provider DTO workflow; local full config import lacks server stale-baseline check, target scope and persisted receipt. | provider snapshot --json; provider apply --baseline FILE --file FILE --json. Snapshot emits {defaultProvider,providers}; apply sends exactly {baseline,next}, rejects stale 409 without auto-refresh/rebase; bounded files or stdin variant. No generic passthrough. | src/cli/provider-runtime.ts:312; src/server/management/provider-routes.ts:1055; gui/src/hooks/useJsonConfigEditor.ts:16; gui/src/hooks/useJsonConfigEditor.ts:55; src/server/management/provider-routes.ts:1055 | +| set-P08 — Check provider connectivity | DISCOVERY_ONLY | provider test P --json | Existing named probe distinguishes applicable:false. No missing GUI action. | Index command and retain not-applicable/configuration-only versus live connectivity distinctions. | src/cli/provider-runtime.ts:148; gui/src/components/provider-workspace/ProviderOverview.tsx:109; src/server/management/provider-routes.ts:1619 | +| set-P09 — Inspect provider quota/capacity/usage | COMPLETE | provider quota [--refresh] --json; account list P --quota [--refresh] --json; usage --range 30d --surface codex\|all --json | Operational quota and scoped usage reads exist. Generic telemetry ownership belongs to sibling; do not add a duplicate provider usage transport. | Reuse existing commands. Keep passive quota unavailable and partial usage signals. | src/cli/provider-runtime.ts:178; src/cli/account-api.ts:370; src/cli/observe.ts:164; gui/src/components/provider-workspace/ProviderWorkspaceShell.tsx:182; src/server/management/provider-routes.ts:904 | +| set-P10 — OAuth login, add another account, reauthenticate, submit callback, cancel, logout | IMPLEMENTATION_GAP | account login P [--reauth --id I] [--no-wait]; account reauth/code/cancel; login P (local); logout P --json (local) | Core flow exists, but account login always sends addAccount:true for non-reauth; no explicit openBrowser override. Initial local-import login and runtime logout equivalence remain UNKNOWN. Meta Muse consent cannot be bypassed. | Add account login --open-browser on\|off and --add-account on\|off with omission preserving server preference; add account logout P --json for exact runtime logout if live-equivalence fixture fails. Keep local login behavior documented separately and preserve provider consent gate. | src/cli/account-auth.ts:139; src/cli/account-auth.ts:260; src/cli/dispatch.ts:374; src/cli/dispatch.ts:425; gui/src/pages/use-providers-oauth.ts:119; src/server/management/oauth-account-routes.ts:236 | +| set-P11 — Kiro native device login | COMPLETE | account login kiro --method builder-id\|google\|github [--no-wait] --json; account cancel kiro --flow F --json | Native device method, flow polling and cancellation exist; add-only constraints match GUI. | Retain implementation; expose flags consistently in higher-level account help where omitted. | src/cli/account-auth.ts:150; src/cli/account-auth.ts:174; src/cli/account-auth.ts:327; gui/src/components/use-kiro-device-login.ts:46; src/server/management/oauth-account-routes.ts:250 | +| set-P12 — Manage OAuth account roster | DISCOVERY_ONLY | account list/current/use P I; account alias P I TEXT\|-; account pause/resume P I; account remove P I --yes; all support --json | Roster lifecycle and clear alias are covered through provider-aware endpoints. Alias and several lifecycle leaves are under-indexed. | Index existing grammar; no new endpoint. Preserve provider capability refusals and exact id/alias resolution. | src/cli/account.ts:44; src/cli/account-extended.ts:438; src/cli/account-extended.ts:803; src/cli/account-extended.ts:1064; gui/src/hooks/useProviderAccountPools.ts:210; src/server/management/oauth-account-routes.ts:398 | +| set-P13 — Import Antigravity accounts | DISCOVERY_ONLY | account import google-antigravity --format cockpit-tools --file F\|--stdin --json | Bounded import exists with redacted per-record receipt and nonzero for failed/unsupported records; no missing GUI action. | Index existing command and safe input forms. | src/cli/account-extended.ts:541; src/cli/account-extended.ts:595; src/cli/account-extended.ts:616; gui/src/components/provider-workspace/ProviderAuthPanel.tsx:358; src/server/management/oauth-account-routes.ts:835 | +| set-P14 — Manage API-key pool | DISCOVERY_ONLY | account list/use P I; account add-key P [--label L] (stdin); account alias P I TEXT\|-; account remove P I --yes --json | Key pool lifecycle is implemented. Secret stdin and null/blank alias semantics already exist. | Index full family; do not add key text to argv examples or create duplicate key pool commands. | src/cli/account-api.ts:419; src/cli/account-extended.ts:507; src/cli/account-extended.ts:1064; gui/src/hooks/useProviderAccountPools.ts:279; src/server/management/oauth-account-routes.ts:929 | +| set-P15 — Set account-pool strategy and thresholds | IMPLEMENTATION_GAP | account strategy P STRATEGY; account sticky P N; account auto-switch P on\|off\|threshold N\|status; account routes anthropic ... | Strategy/sticky cover common fields. No pool enabled writer or quotaWindow writer. Anthropic auto-switch delegates to account-only parser and requires --account, so GUI pool-level Anthropic threshold is missing. Generic/Codex thresholds exist. | Add account pool P [--enabled on\|off] [--threshold N] [--quota-window five-hour\|weekly\|max-utilization] --json, using unified pool/settings DTO supported fields. Can expose strategy/sticky in same command but retain existing aliases. Never map pool enable to threshold 0 or per-account override. | src/cli/account-extended.ts:355; src/cli/account-extended.ts:941; src/cli/account-extended.ts:974; src/cli/account-anthropic-threshold.ts:14; gui/src/pool-settings.ts:74; src/server/management/oauth-account-routes.ts:559 | +| set-P16 — Set per-Anthropic-account auto-switch override | COMPLETE | account auto-switch anthropic status\|on\|off\|inherit\|threshold N --account I --json | Per-account nullable override and inherited/effective threshold are implemented; inherit sends null. | Keep command; do not confuse P15 pool policy with this scoped override. | src/cli/account-anthropic-threshold.ts:4; gui/src/hooks/useProviderAccountPools.ts:443; src/server/management/oauth-account-routes.ts:507 | +| set-P17 — Control xAI Responses opt-in and model Fast variants | IMPLEMENTATION_GAP | provider edit xai --xai-chat on\|off --json | xaiResponsesOptIn is implemented via inverse --xai-chat (!value). No fastEnabled field/flag for provider Fast model variant rows. | Add provider edit P --fast on\|off mapping fastEnabled; document --xai-chat off means Responses opt-in true, without reversing existing flag meaning. | src/cli/provider-runtime.ts:76; src/cli/provider-runtime.ts:108; gui/src/components/provider-workspace/ProviderAuthPanel.tsx:75; src/server/management/provider-routes.ts:1412 | +| set-P18 — Inspect/redeem Grok reset coupons | COMPLETE | account grok-reset-coupons [I] [--consume --yes --token-id T --operation-id UUID] --json | Read, token selection, consent flag and stable operation-id retry exist. | No new writer. Preserve explicit user spend intent and exact operation UUID on retry. | src/cli/account-auth.ts:374; gui/src/hooks/useGrokResetCoupons.ts:133; src/server/management/grok-coupon-routes.ts:63 | +| set-P19 — Inspect/redeem Anthropic reset grants | IMPLEMENTATION_GAP | No Anthropic grant read command in account-auth dispatcher; Grok/Codex reset commands target different contracts | Read missing; consume is EXCLUDED because server requires gui-session. Do not extend reset-credit consume to Claude. | Add account anthropic-reset-grants [I] --json for GET only, with optional current-account semantics matching server. Explain session-only consume rather than attempting session minting. | src/cli/account-auth.ts:414; src/server/management/anthropic-reset-grant-routes.ts:109; src/server/management/anthropic-reset-grant-routes.ts:131; gui/src/hooks/useAnthropicResetGrants.ts:168; src/server/management/anthropic-reset-grant-routes.ts:131 | +| set-P20 — Choose whether OAuth launches browser on proxy host | IMPLEMENTATION_GAP | account login currently omits openBrowser; GUI local preference feeds request field | No one-shot browser-launch override; device flag changes authentication protocol and is not equivalent. | Add account login P --open-browser on\|off; omit field when flag absent. No new persistent settings write required for GUI-local preference. | src/cli/account-auth.ts:139; src/cli/account-auth.ts:198; src/cli/account-auth.ts:260; gui/src/components/open-browser-pref-toggle.tsx:19 | +| set-A01 — Inspect/refresh account readiness, quotas and active selection | COMPLETE | account list openai --quota [--refresh] --json; account current openai --json; account refresh openai --json | Read, quota refresh and POST validation refresh already exist. Login's old recovery prose pointing to GUI does not mean refresh command is absent. | Reuse command; update recovery guidance as discovery work only if in authorized scope. | src/cli/account-extended.ts:326; src/cli/account-extended.ts:347; src/cli/account-api.ts:281; gui/src/hooks/useCodexAccountPool.ts:317; src/codex/auth-api/routes.ts:35 | +| set-A02 — Add/re-authenticate pool account with browser or device flow | IMPLEMENTATION_GAP | account login openai --id I --reauth --device --no-wait --json; account code/cancel openai --flow F | Start/poll/manual code/cancel complete; openBrowser one-shot preference absent as P20. No need for a second Codex login command. | Extend existing login flag as P20, keep id and flowId distinct, preserve device flag and user verification. | src/cli/account-auth.ts:139; src/cli/account-auth.ts:197; src/cli/account-auth.ts:294; src/cli/account-auth.ts:327; gui/src/components/use-add-codex-account-oauth.ts:95; src/codex/auth-api/routes.ts:493 | +| set-A03 — Reauthenticate native main in place | COMPLETE | account main reauth --device [--no-wait] --json; account main reauth status\|cancel --flow F --json | Dedicated native-main no-body start, exact-flow polling/cancel implemented. | Keep native-main identity separate from account login openai. Sibling can own docs without duplicating operation. | src/cli/account-main.ts:192; gui/src/components/use-main-device-reauth.ts:99; src/codex/main-device-reauth-api.ts:49 | +| set-A04 — Select active account, rename, remove | DISCOVERY_ONLY | account current/use/clear openai; account alias openai I TEXT\|-; account remove openai I --yes --json | Selection, explicit auto clear, rename/reset alias and removal exist. Main versus auto has documented identity precedence. | Index leaves. Use account clear to always clear pin when a real id is named auto. | src/cli/account.ts:44; src/cli/account.ts:364; src/cli/account-extended.ts:438; src/cli/account-extended.ts:1064; gui/src/hooks/useCodexAccountPool.ts:486; src/codex/auth-api/routes.ts:52 | +| set-A05 — Pause/resume, pause exhausted accounts, set priority | COMPLETE | account pause/resume openai I; account pause-exhausted openai; account priority openai I N\|first\|earlier\|normal\|later\|last\|reset --json | Pause/resume/exhausted and priority null-reset are present. | No new commands; keep main restrictions and server-authoritative priority receipt. | src/cli/account-extended.ts:714; src/cli/account-extended.ts:803; src/cli/account-extended.ts:875; gui/src/hooks/useCodexAccountPool.ts:535; src/codex/auth-api/routes.ts:90 | +| set-A06 — Set per-account auto-switch threshold | IMPLEMENTATION_GAP | account auto-switch openai on\|off\|threshold N\|status is pool-wide only | Codex per-account id plus threshold:null override has no parser path; --account is parsed only by Anthropic handler. | Extend account auto-switch openai ... --account I, add inherit => {id,threshold:null}; pool action with no --account remains {threshold}. Resolve main and aliases explicitly. | src/cli/account-extended.ts:355; src/cli/account-extended.ts:394; src/cli/account-anthropic-threshold.ts:4; gui/src/hooks/useCodexAccountPool.ts:623; src/codex/auth-api/routes.ts:310 | +| set-A07 — Permit paid credits after quota exhaustion | IMPLEMENTATION_GAP | Only generic config fallback; no account-auth/extended credits policy handler | Both individual {id,creditsAfterLimit} and aggregate {all} variants are missing. | account credits openai I on\|off --json; account credits openai --all on\|off --json. Mutually exclusive selector/all; GET/read from account roster, PUT exact credits endpoint. Require explicit cost-spend opt-in intent; never infer from show-credits preference. | src/cli/account.ts:44; src/cli/account-auth.ts:414; src/codex/auth-api/routes.ts:122; gui/src/hooks/useCodexAccountPool.ts:662; src/codex/auth-api/routes.ts:122 | +| set-A08 — Inspect/redeem Codex reset credits | DISCOVERY_ONLY | account reset-credits I\|main [--consume --yes --operation-id UUID] --json | Read and redeem exist, with stronger optional retry identity than current GUI. Root account usage omits operation-id though leaf handler supports it. | Index leaf grammar and stable retry id; no missing operation. | src/cli/account-auth.ts:345; src/cli/account.ts:69; gui/src/components/CodexAccountPool.tsx:441; src/codex/auth-api/routes.ts:454 | +| set-A09 — Configure quota-window activation and credit display | IMPLEMENTATION_GAP | system settings --json reads settings; account refresh handles immediate refresh only | No codexQuotaAutoRefresh per-id/per-window writer and no showCodexCredits switch. Immediate refresh is not quota-window activation; show credits is not credits-after-limit. | account quota-activation openai I --window fiveHour\|weekly on\|off --json, exact {codexQuotaAutoRefresh:{id,window,enabled}}; system settings --show-codex-credits on\|off for presentation preference. Sibling/main decide ownership. | src/cli/system-command.ts:124; src/cli/account-extended.ts:326; gui/src/components/CodexAccountPool.tsx:374; src/server/management/config-routes.ts:564 | +| set-A10 — Native-main profile inventory, register, switch/recover | DISCOVERY_ONLY | account main list\|doctor\|register LABEL\|switch I --yes\|recover [--rollback --yes] --json | Native-profile workflows implemented, including staging add beyond GUI. CLI recover without rollback intentionally uses {rollback:false}; GUI's confirmedStopped true does not imply missing safe recovery action. | Index existing family; shared lifecycle owner should verify no-op recovery versus rollback fixture, not add a second writer. | src/cli/account-main.ts:269; src/cli/account-main.ts:280; src/cli/account-main.ts:359; src/cli/account-main.ts:376; gui/src/components/CodexAccountPool.tsx:564; src/codex/native-profile-api.ts:137 | +| set-A11 — Account-picker setting and main hard lock | IMPLEMENTATION_GAP | system settings --json reads values | No codexAccountPickerEnabled or codexMainAccountHardLock writer in dedicated settings handler. | Extend system settings --account-picker on\|off --main-account-hard-lock on\|off, exact settings fields and server receipt. Confirmed GUI mount at codex-set-multiauth:218/221; sibling-owned overlap, not unresolved reachability. | src/cli/system-command.ts:124; gui/src/pages/codex-set-multiauth.tsx:218; gui/src/components/CodexAccountPickerSetting.tsx:27; src/server/management/config-routes.ts:512 | +| set-A12 — Read prompt layer stack and effective prompt text | COMPLETE | inspect codex-prompt --json; inspect codex-prompt --text (mutually exclusive) | Stack and bounded effective-text read are implemented. | No new command; unavailable text remains unavailable, not empty successful content. | src/cli/inspect.ts:32; src/cli/inspect.ts:216; gui/src/pages/codex-set-prompt.tsx:147; src/server/management/codex-prompt-routes.ts:308 | +| set-A13 — Toggle/edit/order custom prompt layers and base variants | EXCLUDED | inspect codex-prompt is read-only | Prompt toggles, ordered custom layers, base select/edit/delete require GUI-session principal; no admin CLI write parity should be added. | Retain documented exclusion; user-directed GUI operation only unless parent separately redesigns product consent contract. | src/server/management/codex-prompt-routes.ts:299; src/server/management/route-registry.ts:198; gui/src/pages/codex-set-prompt.tsx:163; src/server/management/codex-prompt-routes.ts:332 | +| set-A14 — Adopt existing prompt configuration or repair prompt drift | EXCLUDED | inspect codex-prompt can expose drift | Adopt/repair preview and commit share session-only gate; no auto-repair from read. | Retain exclusion; do not create token-based adoption/repair command. | src/server/management/codex-prompt-routes.ts:299; src/server/management/codex-prompt-routes.ts:413; src/server/management/route-registry.ts:196; gui/src/pages/codex-set-prompt.tsx:304; src/server/management/codex-prompt-routes.ts:413 | +| set-A15 — Enable default-mode request-user-input | COMPLETE | agent request-user-input [on\|off] --json | Read and boolean write cover GUI; confirmed mounted in CodexSet. | No new command; sibling deduplicates advanced settings. | src/cli/inspect.ts:126; src/cli/agent.ts:432; gui/src/components/DefaultModeRequestUserInputSetting.tsx:7; src/server/management/agent-settings-routes.ts:509 | +| set-A16 — Toggle Ultra Fast tier | IMPLEMENTATION_GAP | system settings --json reads ultraFastTier | No --ultra-fast-tier mutation flag; provider fastEnabled and catalog fastRows are different settings. | Add system settings --ultra-fast-tier on\|off --json => {ultraFastTier}; preserve server default deletion semantics. | src/cli/system-command.ts:124; src/server/management/config-routes.ts:654; gui/src/components/UltraFastTierSetting.tsx:36; src/server/management/config-routes.ts:516 | +| set-M01 — Inspect catalog, exposed selection, context limits and provider metadata | DISCOVERY_ONLY | models live --provider P --json; models selected P; models context status; models preset show; models new-policy; alias list; inspect config/catalog | All catalog read families exist; plain models is explicitly static and cannot replace models live. | Index relevant leaves and communicate static versus live; no new catch-all catalog command required. | src/cli/models-runtime.ts:73; src/cli/models-runtime.ts:387; src/cli/models-runtime.ts:435; src/cli/models.ts:381; gui/src/pages/Models.tsx:395; src/server/management/model-routes.ts:213 | +| set-M02 — Show/hide selected models or all models of one provider | DISCOVERY_ONLY | models enable\|disable P/M [--native] --json; models provider P on\|off --json | Single-model and provider-all identity-aware writes exist. GUI has no separately required arbitrary multi-selection batch command beyond these workflows. | Index existing verbs; preserve native bit and provider membership, not raw disabled-list overwrite. | src/cli/models-runtime.ts:354; src/cli/models-runtime.ts:369; gui/src/model-visibility.ts:87; src/server/management/model-routes.ts:621 | +| set-M03 — Choose curated/provider model preset and default policy for newly discovered models | DISCOVERY_ONLY | models preset show [--provider P]; models preset apply P [--all]; models new-policy on\|off [--provider P]; models new-arrivals --json | GUI offers preset/all, both implemented. custom is a disabled state activated by model edits, not a missing GUI destination. Policy global/provider on/off covered. | No custom-preset flag required for GUI parity; index existing commands. Any server-only mode custom flag is optional API expansion. | src/cli/models-runtime.ts:435; src/cli/models-runtime.ts:474; gui/src/pages/Models.tsx:1529; gui/src/pages/Models.tsx:1550; gui/src/pages/Models.tsx:1170; src/server/management/model-routes.ts:233 | +| set-M04 — Edit provider/model aliases and default alias policy | DISCOVERY_ONLY | alias list; alias set P\|P/M ALIAS; alias rm P\|P/M; alias defaults on\|off [--provider P] --json | Provider/model aliases, explicit remove and global/provider defaults covered. | Index full family; do not conflate model alias with display label. | src/cli/alias.ts:16; gui/src/pages/models-alias-editing.ts:30; src/server/management/model-routes.ts:295 | +| set-M05 — Edit/reset model display label and cost override | IMPLEMENTATION_GAP | models price P/M; models set-price P/M --input N --output N [--cache-read N --cache-write N] or --auto; custom models edit --display-name only | Pricing including null reset complete. Discovered model display-name set/null clear absent; server route is also undeclared in registry. | Add models display-name P/M --set TEXT\|--clear --json; exact modelId/displayName body; preserve persistence receipt when HTTP reports catalog convergence failure. Do not repurpose custom model id endpoint. | src/cli/models-runtime.ts:106; src/cli/models-runtime.ts:202; src/cli/models-runtime.ts:566; src/server/management/model-routes.ts:447; gui/src/components/ModelPriceDialog.tsx:7; src/server/management/model-routes.ts:386 | +| set-M06 — Create/edit/delete custom model definitions | UNKNOWN | models add P M --display-name --context-window --modalities --reasoning-efforts --default-reasoning-effort; models list-custom --json; models edit ID ...; models remove ID\|P/M --yes | All GUI custom CRUD fields exist. Add/remove are local and call syncCustomModelsIfLive; add/remove lack --json, while edit is live HTTP. Need isolated same-target convergence proof before declaring complete or runtime gap. | Do not invent missing CRUD. If machine parity required, add --json with saved/convergence receipt to local add/remove; optionally route live operations through custom-models API while retaining explicit offline mode. | src/cli/models.ts:185; src/cli/models.ts:197; src/cli/models.ts:280; src/cli/models.ts:359; src/cli/models-runtime.ts:195; gui/src/pages/Models.tsx:1310; src/server/management/model-routes.ts:743 | +| set-M07 — Override/restore discovered-model capability metadata | COMPLETE | models set P/M --context-window N\|0\|- --modalities CSV\|- --reasoning-efforts CSV\|""\|- --default-reasoning-effort LEVEL\|-; --reset --json | All GUI metadata overrides and full null-reset exist; empty reasoning array versus inherited null is preserved. Catalog refresh failures have saved-state guidance. | Retain existing writer and schema; no generic JSON body option needed. | src/cli/models-runtime.ts:262; gui/src/components/ModelSettingsDialog.tsx:153; src/server/management/model-routes.ts:764 | +| set-M08 — Override provider/per-model advertised context window | IMPLEMENTATION_GAP | models set P/M --context-window N\|0\|- covers per-model override | Provider-level contextWindow default and one patch containing touched modelContextWindows lack flags. Per-model individual override is already covered; do not duplicate it. | Extend provider edit P --context-window N\|- and repeatable --model-context-window MODEL=N\|-; null clears, untouched keys remain absent. Atomic multi-model patch is optional if parent accepts equivalent individual tasks; provider default is required. | src/cli/provider-runtime.ts:59; src/cli/models-runtime.ts:281; gui/src/pages/Models.tsx:854; gui/src/pages/Models.tsx:825; src/server/management/provider-routes.ts:1412 | +| set-M09 — Configure context caps: per-provider, global default, apply/remove all | DISCOVERY_ONLY | models context status; models context value N [--set-all]; models context provider P on\|off [--value N]; models context all on\|off --json | All three cap shapes covered, including per-provider explicit value and apply/remove-all. No implementation gap. | Improve discovery only; preserve distinction between global default and applying to every provider. | src/cli/models-runtime.ts:500; gui/src/pages/Models.tsx:939; src/server/management/provider-routes.ts:1881 | +| set-M10 — Configure picker order manually or by ranking policy | IMPLEMENTATION_GAP | agent subagents status shows picker fields; agent subagents set writes featured models only | No pickerOrder/pickerOrderMode writer. Manual order, default reset, alphabetical/provider/most-used modes all missing. | models order status; models order set --models CSV; models order set --mode default\|alphabetical\|provider\|most-used; models order reset --json. Manual/reset => mode null; ranking computes complete order from visible identities and usage where needed. Never write models for this task. | src/cli/agent.ts:163; src/cli/models-runtime.ts:566; gui/src/pages/Models.tsx:1904; gui/src/components/ModelPickerOrderEditor.tsx:83; src/server/management/subagent-model-routes.ts:19 | +| set-M11 — Show generated Fast catalog rows | IMPLEMENTATION_GAP | system settings --json reads fastRows | No global fastRows flag; not covered by provider fastEnabled or ultraFastTier. | Add models fast-rows on\|off --json or system settings --fast-rows on\|off, choose one canonical verb and document alias if needed; PUT {fastRows}. | src/cli/system-command.ts:124; src/cli/models-runtime.ts:566; gui/src/components/ModelCatalogSettingsPanels.tsx:11; src/server/management/config-routes.ts:519 | +| set-M12 — Configure shadow-call interception target | DISCOVERY_ONLY | models shadow status; models shadow set M\|- --enabled on\|off --json | Enable/disable target and empty-string clear covered. | Index existing command; retain validation on enable and target clear. | src/cli/models-runtime.ts:544; gui/src/pages/Models.tsx:480; src/server/management/config-routes.ts:1159 | +| set-M13 — Configure multi-agent surface and per-session thread limit | UNKNOWN | v2 status; v2 mode v1\|default\|v2; v2 keep-native-v1 on\|off; v2 threads N (local, prose output) | All GUI effects have existing local commands. Mode/keep-native save config and resync; threads uses shared transition helper. No structured --json contract or GUI advisory-ack write in cmdV2. Same live-target and receipt equivalence remains unproven; these are NOT absent operations. | Keep ocx v2 taxonomy. Add strict --json parsing/receipts if machine parity is required; do not create agent mode. Parent decides whether advisory is GUI-only or needs an explicit v2 acknowledgment option. Verify local/shared helpers versus live management path before choosing transport changes. | src/cli/v2.ts:109; src/cli/v2.ts:177; src/cli/v2.ts:191; src/cli/v2.ts:220; src/cli/dispatch.ts:539; src/server/management/agent-settings-routes.ts:415; gui/src/pages/Models.tsx:1101 | +| set-C01 — Inspect combos, target inventory, quota and catalog exposure | DISCOVERY_ONLY | combo list/show I; models live; inspect config; provider quota --json | All combo inventory/target/quota inputs present; GUI grouping doesn't require a separate command. | Index combo/route combo family; no new transport. | src/cli/combo.ts:54; src/cli/combo.ts:65; src/cli/dispatch.ts:849; gui/src/pages/Combos.tsx:138; src/server/management/combo-routes.ts:122 | +| set-C02 — Create/edit/rename/remove combo and target policy | IMPLEMENTATION_GAP | combo set\|create\|update I --targets P/M[:W],... --strategy S --sticky N\|- --effort E\|- --alias A\|- --native-alias --display-name TEXT\|- --decision-provider P\|- --decision-model M\|- --decision-timeout N\|- --rename-from OLD; remove I --yes | Core CRUD/rename/JEV resets exist. Missing imageInput auto/disabled, reasoningEffortMode strict/adaptive, target reasoningEfforts/modelProfile; native-alias cannot explicitly switch off while keeping a native-shaped alias. --effort-mode is defaultEffortMode, NOT reasoningEffortMode. Replacing targets loses metadata without expressive parser. | Extend combo set with --image-input auto\|disabled --reasoning-effort-mode strict\|adaptive --native-alias on\|off (backward-compatible bare flag); --targets-file FILE for typed ordered targets incl optional reasoningEfforts/modelProfile, or repeatable structured --target flags. Preserve omitted fields and explicit clears. | src/cli/combo.ts:32; src/cli/combo.ts:80; src/cli/combo.ts:137; src/cli/combo.ts:163; gui/src/pages/Combos.tsx:301; src/server/management/combo-routes.ts:134 | +| set-C03 — Probe selected JEV decision backend | COMPLETE | combo test [--combo I] [--decision-provider P\|--decision-model M] [--decision-timeout N] --json | Existing probe resolves saved selector client-side when --combo; explicit args probe chosen draft. Discovery command also exists. | No gap; keep potential model-call cost visible and do not treat successful configuration-only response as probe success. | src/cli/combo.ts:186; src/cli/combo.ts:215; gui/src/components/combo-workspace-jev-decision.tsx:107; src/server/management/decision-routes.ts:53 | +| set-C04 — Explain a saved combo's protocol behavior | COMPLETE | api protocols --json; api explain --model combo/I --inbound responses\|chat\|messages --feature F[,F] ... --json | Saved combo plan and features covered by semantic protocol command. | No combo-specific duplicate required; document usage with public alias. | src/cli/api-protocols.ts:121; src/cli/api-protocols.ts:131; gui/src/components/combo-workspace-detail-panel.tsx:416; src/server/management/protocol-routes.ts:45 | +| set-C05 — Inspect JEV combo decision outcomes, tokens and evidence coverage | IMPLEMENTATION_GAP | usage --range ... --model combo/I exists but returns ordinary usage | No jev=1/comboId report selector in usage parser; ordinary model-filtered usage is not the JEV decisions/token/backends report. | Add combo stats I --range 7d\|30d\|all --json, exact GET usage?jev=1&comboId=I&range=; sibling usage owner can share transport/formatting. The current GUI/API DTO has no monetary cost or savings baseline; report actual token/coverage facts and incomplete evidence. | src/cli/observe.ts:164; src/cli/combo.ts:251; gui/src/components/jev-stats-panel.tsx:90; gui/src/components/jev-stats-panel.tsx:90; src/server/management/logs-usage-routes.ts:188 | +| set-R01 — Inspect routing profiles, candidates, scoring and compatibility suite choices | DISCOVERY_ONLY | route policy list/show I --json; inspect routing-analytics --json; models live; lab catalog --json | Existing commands expose profile/revision, scoring context, inventory and Lab suite choices. | Index leaves; local Lab query is valid transport for local target. | src/cli/route-policy.ts:27; src/cli/route-policy.ts:43; src/cli/inspect.ts:211; src/cli/lab.ts:87; gui/src/pages/RoutingProfiles.tsx:351; src/server/management/routing-profile-routes.ts:226 | +| set-R02 — Create/update/remove routing profile | IMPLEMENTATION_GAP | route policy only list/show/dry-run/evaluate | No create/update/delete. Missing all profile-write fields and expectedRevision conflict workflow. | route policy create I --file PROFILE.json --json; route policy update I --file PROFILE.json --expected-revision REV --json; route policy remove I --yes --json. Domain profile schema is exact GUI profile object, not arbitrary endpoint JSON. Omitted profile branches are removed according to server full-profile semantics. | src/cli/route-policy.ts:85; src/server/management/routing-profile-routes.ts:233; gui/src/routing-profile-editor-data.ts:280; gui/src/pages/RoutingProfiles.tsx:458; src/server/management/routing-profile-routes.ts:233 | +| set-R03 — Dry-run a saved route against requirements | DISCOVERY_ONLY | route policy dry-run\|evaluate I --model-context N --tools --image --structured-output --json | All four GUI evidence inputs supported. Advanced encrypted/reasoning/tier/candidate evidence are server-only and not GUI parity debt. | Index command; optional broader API support should be separate scope. | src/cli/route-policy.ts:55; gui/src/pages/RoutingProfiles.tsx:559; src/server/management/routing-profile-routes.ts:364 | +| set-S01 — Choose/reorder featured subagent roster | DISCOVERY_ONLY | agent subagents\|roster status\|set CSV\|clear --json | Featured ordered roster, five-item max and empty-list clear covered; this does not cover M10 picker ordering. | Index leaf actions without creating a second roster writer. | src/cli/agent.ts:163; src/cli/agent.ts:426; gui/src/pages/Subagents.tsx:213; src/server/management/subagent-model-routes.ts:54 | +| set-S02 — Force subagent model and configure fallback chain | COMPLETE | agent subagents force M\|-; agent fallback status\|set [CSV] [--poll-ms 5000..600000]\|clear --json | Force null and fallback empty-list clear/poll range match GUI. Server-only poll null reset not exposed in GUI, so not a GUI gap. | Retain implementation; document identity and clear semantics. | src/cli/agent.ts:173; src/cli/agent.ts:195; gui/src/components/subagents-workspace/SubagentForceControl.tsx:67; src/server/management/subagent-model-routes.ts:101 | +| set-S03 — Configure delegation guidance/default model/effort and synchronize defaults | IMPLEMENTATION_GAP | agent injection\|guidance set --model M\|- --effort E\|- --guidance on\|off; status --json | Model, effort and guidance covered; syncCodexSubagentDefaults missing. Existing set outputs write result without GUI's normalized reread. | Add --sync-codex-defaults on\|off, exact field. Read authoritative injection DTO after successful save if write doesn't return normalized state; retain null clears. | src/cli/agent.ts:111; src/cli/agent.ts:137; src/server/management/agent-settings-routes.ts:608; gui/src/pages/use-subagent-delegation.ts:15; src/server/management/agent-settings-routes.ts:564 | +| set-S04 — Set delegation mode and editable proactive hint | UNKNOWN | v2 mode v1\|default\|v2; v2 mode-hint TEXT; v2 mode-hint --clear; v2 status (local) | Mode and hint set/null-reset already exist. mode-hint uses same setMultiAgentModeHintText helper as management route; blank text rejected. Root registry omits mode-hint and cmdV2 lacks JSON/strict trailing-argument receipt handling. Shared mode live-equivalence/advisory issue is M13, not missing hint writer. | Document/index v2 mode-hint and --clear; extend existing v2 machine contract, not agent mode. A hint-file convenience is optional, not GUI parity debt. Test local scope/new-session behavior and shared writer failures before classifying local/live coverage complete. | src/cli/v2.ts:144; src/cli/v2.ts:167; src/cli/v2.ts:191; src/cli/registry.ts:575; src/server/management/agent-settings-routes.ts:394; gui/src/pages/Subagents.tsx:131 | +| set-S05 — Ask for delegation model recommendation, then separately apply | COMPLETE | agent injection suggest WORK [--model SIZING_MODEL] [--apply] --json; agent injection set --model M --effort E | Suggestion, no-write default and explicit apply cover GUI propose/accept; includes optional sizing override beyond GUI. | No new command; preserve proposal failure/unsized and explicit apply semantics. | src/cli/agent.ts:73; gui/src/components/subagents-workspace/DelegationSuggest.tsx:49; src/server/management/agent-settings-routes.ts:578 | +| set-K01 — Inspect/filter/paginate compatibility verdicts and subjects | DISCOVERY_ONLY | lab status; lab verdicts --subject --layer --suite --verdict --limit --cursor; lab subjects --limit --cursor --json | Filters/pagination and status read via local projection query already exist. Remote target equivalence is not proven and outside this local scope. | Index local read family; avoid new HTTP-only duplicates. | src/cli/lab.ts:76; src/cli/lab.ts:365; src/cli/lab.ts:385; gui/src/pages/compatibility-matrix-api.ts:35; src/server/management/lab-routes.ts:354 | +| set-K02 — Inspect one verdict's evidence lineage and production context | DISCOVERY_ONLY | lab subject I; observations --subject I --layer L --suite T --limit --cursor; event I; artifact D; production-signals --subject I --limit --json | All lineage resources exposed; GUI aggregation is composition over named reads. No need to mimic bounded visual panel in CLI; preserve not_verification marker. | Document an evidence-lineage recipe and local target caveat; optional consolidated explain command is convenience, not confirmed missing data. | src/cli/lab.ts:76; src/cli/lab.ts:418; src/cli/lab.ts:428; src/cli/lab.ts:468; gui/src/pages/compatibility-matrix-api.ts:150; src/server/management/lab-routes.ts:358 | +| set-K03 — Filter protocol pairs and view community trust context | DISCOVERY_ONLY | lab subject I --json; lab public community --json | Community trust DTO and subject protocol pairs exposed. Pair dropdown filters are client-side presentation over subject details; no new protocol-pair endpoint. | Index command/recipe; no mutation gap. If parent wants server-side pair filtering, treat it as separate enhancement. | src/cli/lab.ts:92; src/cli/lab.ts:297; src/cli/lab.ts:418; gui/src/pages/compatibility-protocol-pairs.ts:14; src/server/management/lab-routes.ts:287 | +| set-K04 — Inspect provider upstream wire/protocol and conversion plan | COMPLETE | api protocols [--provider P] --json; api explain --model M --inbound PROTOCOL [--feature F] --json | Provider wire, global policy revision and plan features covered. | Retain semantic CLI commands; not generic passthrough. | src/cli/api-protocols.ts:121; src/cli/api-protocols.ts:131; gui/src/components/provider-workspace/ProviderProtocolPanel.tsx:1; src/server/management/protocol-routes.ts:67 | +| set-K05 — Control protocol exposure/rollout | COMPLETE | api policy --messages on\|off --unrepresentable legacy\|reject --rollout SWITCH=on\|off ... --json | Messages toggle and all server rollout fields already supported. Actual GUI control is on API-surface cards, a sibling boundary. | No gap. Preserve strict feature/rollout validation; sibling can index overlapping entrypoint. | src/cli/api-protocols.ts:151; gui/src/pages/api-surface-cards.tsx:57; src/server/management/protocol-routes.ts:143 | + +## Priority remediation specifications and next proof + +These are bounded implementation candidates for the parent; several matrix rows share a single change. The 24 IMPLEMENTATION_GAP rows are **not** 24 independent command families. M13/S04 are now UNKNOWN for local/live equivalence, with existing effects confirmed. + +| Priority / rows | Exact implementation contract | Required isolated proof (not run here) | +|---|---|---| +| First: set-M05 display names | `models display-name P/M --set TEXT` or `--clear` sends PUT `/api/providers/P/model-display-names` `{modelId:M,displayName:TEXT\|null}`. Add regex route declaration. Keep custom model edit unchanged. Server can save and then return failed convergence; CLI must retain saved state/error receipt instead of saying no change happened. | Set, clear, missing model, encoded provider/model identifiers, saved-but-refresh-failed response; prove correct resource and that retry does not invent a custom definition. Source `src/server/management/model-routes.ts:447`; GUI receipt comment `gui/src/pages/Models.tsx:647`. | +| First: set-R02 routing profile writes | Create `{mode:"create",id,profile}`; update `{mode:"update",id,expectedRevision,profile}`; remove query id with explicit destructive intent. Profile file is bounded/validated domain input. Exact fields: alias,candidates[{provider,model}],require{minContextWindow,minQuotaHeadroom,tools,imageInput,structuredOutput,reasoningEffort,serviceTier,localOnly,remoteAllowed,encryptedCodexTasks},optimize{latency,health,cost,quota},limits{maxEstimatedCostUsd,onUnknownCost},unknownEvidence,compatibility{requiredSuites,minStatus,maxEvidenceAgeMs,unknownEvidence,degradedEvidence}. | Create collision; update missing/stale revision leaves existing profile unchanged; delete dependent/in-use refusal; optional branch removal semantics and unknown field validation. Sources `src/cli/route-policy.ts:85`, `src/server/management/routing-profile-routes.ts:233`, `gui/src/routing-profile-editor-data.ts:280`. | +| First: set-P07 provider snapshot/apply | Snapshot projected `{defaultProvider,providers}` only; apply exact `{baseline,next}` through PUT providers. No port/system/secret dump; strip derived GUI fields same as DTO projection. Never auto-retry stale baseline with fresh state because that would authorize overwriting intervening edits. | Stale-baseline 409; invalid endpoint/row; removal with dependencies; baseline read includes only permitted projection; no write after preview refusal; confirmed saved/catalog receipt. Sources `gui/src/hooks/useJsonConfigEditor.ts:16`, `src/server/management/provider-routes.ts:1055`. | +| First: set-A07/set-A06/set-P15/set-P19 account gaps | Credit policy: `{id,creditsAfterLimit:boolean}` OR `{all:boolean}`; reject ambiguous selector/all. Codex override `{id,threshold:number\|null}` with inherit -> null; pool default remains `{threshold}`. Unified pool PATCH/PUT fields provider,enabled,strategy,stickyLimit,autoSwitchThreshold,quotaWindow; quotaWindow enum five-hour/weekly/max-utilization is Anthropic-only. Anthropic grants command is GET-only; consume session gate remains excluded. | One versus all, on/off, explicit main identity, unknown id, inheritance versus zero, invalid quota window, unsupported provider fields, no secret output, consume refusal without user GUI session. Sources `src/codex/auth-api/routes.ts:122`, `src/codex/auth-api/routes.ts:310`, `src/server/management/oauth-account-routes.ts:559`, `src/server/management/anthropic-reset-grant-routes.ts:131`. | +| Next: set-M10 picker order | `models order` owns pickerOrder and pickerOrderMode only; never changes featured models or force. Manual ordered ids + null mode; default/reset -> empty/default order + null mode; alphabetical/provider/most-used computes order using current visible identities, preserves supported model namespaces, sends matching mode. Most-used refuses incomplete usage evidence rather than sorting a partial sample. | Capture exact body; roster/force unchanged; native/routed id collision; duplicate/invisible ids; null/default reset; most-used incomplete usage refusal. Sources `src/server/management/subagent-model-routes.ts:74`, `gui/src/pages/Models.tsx:1904`. | +| Next: set-S03 injection field; set-S04/set-M13 existing v2 contract | Extend injection set only with syncCodexSubagentDefaults boolean and normalized reread. Existing `ocx v2 mode`, `keep-native-v1`, `threads`, `mode-hint TEXT` and `mode-hint --clear` already implement the other GUI effects locally. Add strict JSON parsing/receipt support in the existing family if required; advisory acknowledgment remains a parent product-contract decision. No duplicate agent-mode writer. | Injection effort normalization; local/live same-home/target and sync outcome; structured v2 output without swallowed trailing flags; hint blank rejection/null reset; advisory not auto-acknowledged. Sources `src/cli/agent.ts:125`, `src/cli/v2.ts:109`, `src/cli/v2.ts:144`, `src/cli/v2.ts:177`, `src/cli/v2.ts:191`, `src/cli/v2.ts:220`, `src/server/management/agent-settings-routes.ts:415`. | +| Next: set-C02 combo fields | Typed targets input includes ordered provider/model, optional weight, reasoningEfforts, modelProfile. Add imageInput auto/disabled and reasoningEffortMode strict/adaptive. Do not confuse defaultEffortMode with reasoningEffortMode. Explicit nativeAlias false needed to disable while alias remains native-shaped. Existing renameFrom/JEV null selector/timeouts retained. | Metadata survives unrelated edit and target reorder; auto/strict explicitly revert disabled/adaptive; remove a target override intentionally; nativeAlias on/off; JEV provider/model mutual exclusion and null resets; rename retains dependencies. Sources `src/cli/combo.ts:32`, `src/cli/combo.ts:137`, `gui/src/combo-workspace-data.ts:507`. | +| Next: set-P03/set-P04/set-P17/set-M08 provider fields | HTTP override null clears. Provider contextWindow null clears; modelContextWindows merges touched keys with per-key null deletes. requestPacing is whole-object replacement, not server merge: flags must either read/merge existing provider state or require a full typed pacing file; `--clear` sends requestPacing:null. fastEnabled boolean has separate meaning from global fastRows. | Exact PATCH body includes only intended fields; per-model pacing edit retains other rules; clear removes object; context null removes only selected key; Cursor protocol validation; provider fast toggle doesn't affect global fastRows. Sources `src/server/management/provider-routes.ts:479`, `src/server/management/provider-routes.ts:493`, `src/server/management/provider-routes.ts:513`, `src/server/management/provider-routes.ts:542`. | +| Parent/sibling merge: set-A09/set-A11/set-A16/set-M11/set-C05 | Settings flags write showCodexCredits,codexAccountPickerEnabled,codexMainAccountHardLock,ultraFastTier,fastRows. Quota activation writes `{codexQuotaAutoRefresh:{id,window:"fiveHour"\|"weekly",enabled}}`; do not confuse this enum with pool quotaWindow. Combo stats reads `usage?jev=1&comboId=I&range=7d\|30d\|all`, not ordinary usage model filter. | Independent field bodies; true/false defaults; unavailable activation window returns 409; JEV-specific response/partial data; no duplicate ownership with operations sibling. Sources `src/server/management/config-routes.ts:564`, `src/cli/system-command.ts:124`, `src/cli/observe.ts:164`. | +| Requires behavior proof: set-P02/set-P05/set-M06/P10 local-live portions | Preserve working offline/local commands; do not claim live management equivalence from local save alone. Add with --json currently returns before --sync; remove default differs from GUI and local dependency checks differ. Custom add/remove invoke live sync helper but need same-target fixture before deciding on a transport rewrite. | Isolated local config + fake live transport: same target selected, save/sync order and outcome, default deletion semantics, dependent combo refusal, JSON mode still applies requested sync, logout live cache convergence. Sources `src/cli/provider.ts:64`, `src/cli/provider.ts:279`, `src/cli/provider.ts:345`, `src/cli/models.ts:185`, `src/cli/dispatch.ts:425`. | + +## Explicit scope limits and exclusions + +- No changes to the settings GUI/API inventory in this pass. Corrections above are recorded here so parent can reconcile without concurrent edits. +- Prompt mutation rows set-A13/set-A14 remain EXCLUDED; Anthropic grant consumption within set-P19 is EXCLUDED even though its read is an implementation gap. Human consent is not a flag to silently auto-answer. +- Browser tabs/dialogs/search/collapse/clipboard/SSE invalidation are presentation mechanisms, not missing command operations. Protocol pair filtering over existing subject data belongs to this category; preserving unknown/trust evidence is still required. +- Existing API protocol commands are complete, context-cap shapes are complete, model-settings full null reset is complete, and native-main reauth is complete. Do not duplicate these under new nouns. +- Lab local projection readers are implementation, not missing HTTP calls. A future remote-target contract needs separate evidence; current source inventory doesn't authorize that expansion. +- Main owns final command naming, roadmap/FSM, fixtures and implementation. This report recommends syntax but does not execute commands or claim runtime parity. + +## Final correction audit and bounded remaining unknowns + +Root-family cross-check followed `src/cli/registry.ts:277` provider, `:311` account, `:333` alias, `:337` models/model, `:369` combo/route, `:380` effort, `:393` agent, `:404` inspect, `:413` usage/observe, `:575` v2, plus already-inspected system/settings, API protocols and Lab handlers. Targeted checks of v2, effort, access and config-command found no dedicated writer for the remaining claimed pacing/fast/picker/credit/quota-activation fields. Generic config remains explicitly outside task-parity proof. No whole-CLI rescan was performed. + +Bounded unknowns: + +- **set-M13 / set-S04:** all operations exist under v2. Compare local config/CODEX_HOME, catalog sync result, existing shared transition/hint helpers, and GUI live receipt/advisory semantics with isolated fixtures. Missing JSON/strict argument contract and unindexed mode-hint are confirmed; whether GUI advisory acknowledgment should become a CLI field is a parent decision. +- **set-M06:** custom model CRUD fields exist; local add/remove and live edit need isolated same-target save/convergence proof. Missing JSON on add/remove is confirmed; no claim of absent CRUD. +- **Partial uncertainty within known-gap rows:** provider add/remove/set-default and OAuth local logout need live equivalence proof; their concrete missing fields or behavior differences are documented separately, so UNKNOWN transport does not erase those gaps. +- **Sibling boundaries:** quota activation/settings and JEV stats recommendations need deduplication with operations/client-settings owner. Their GUI mounts and missing dedicated field parsers were inspected; ownership, not existence, is unresolved. + +Final matrix totals: 67 rows = 16 COMPLETE + 22 DISCOVERY_ONLY + 24 IMPLEMENTATION_GAP + 2 EXCLUDED + 3 UNKNOWN. Source-only evidence; no tests or live operations executed. + +## Dashboard-mounted panels added in roadmap reconciliation + +| ID | Task and source | Current CLI coverage | Work phase | +|---|---|---|---| +| set-S06 | Memory extraction/consolidation model settings; `gui/src/pages/dashboard-overview-panels.tsx`, `gui/src/components/MemoryModelsPanel.tsx` | No dedicated live block writer; the existing memory family does not prove these GUI fields | wp5 | +| set-S07 | Compaction route model, effort, triggers and source scope; `gui/src/pages/dashboard-overview-panels.tsx`, `gui/src/components/CompactionRoutingPanel.tsx` | No dedicated live block writer | wp5 | + +These two source-backed implementation gaps supplement the historical 67-row settings inventory above. The task ledger is authoritative for current cross-domain aliases and completion evidence. diff --git a/devlog/_fin/261003_cli_gui_parity/005_operations_coverage.md b/devlog/_fin/261003_cli_gui_parity/005_operations_coverage.md new file mode 100644 index 00000000000..26480746bc1 --- /dev/null +++ b/devlog/_fin/261003_cli_gui_parity/005_operations_coverage.md @@ -0,0 +1,158 @@ +# Operational task parity join + +Source-only bounded second pass, 2026-10-03. Root: `the task checkout`. Joined the operations GUI/API inventory with the CLI source inventory and targeted handlers only. No runtime requests, credentials, source edits, tests or Git writes. Source presence is not runtime proof. Main owns command conventions and the implementation roadmap. + +Status meanings: COMPLETE = existing domain command or documented sequence supports the task; DISCOVERY_ONLY = effect exists but task/leaf/route metadata is incomplete; IMPLEMENTATION_GAP = a necessary action/field/workflow cannot be expressed by the inspected handler; EXCLUDED = current deliberate session/consent or pure presentation boundary; UNKNOWN = target scope or sibling ownership prevents an honest equivalence claim. COMPLETE does not certify global JSON error/exit contracts; those are shared CLI audit work. New syntax below is a recommendation, never a claim of an existing command. + +## Highest-value confirmed gaps + +- `ops-K03`: key rename. Existing `access key set` only scopes providers/models (`src/cli/access.ts:168`); no name operand, despite sharing the GUI PATCH endpoint. Recommend `ocx access key rename [--json]`. +- `ops-T07`: cleanup threshold and reduction target. Policy parser (`src/cli/storage.ts:162`) has enabled/percent/mode/schedule only; GUI submits trigger.archivedBytesOver and target.reduceToBytes (`gui/src/pages/Storage.tsx:709`). Recommend mutually exclusive `--remove-oldest-percent` / `--reduce-to-bytes`, plus `--archived-bytes-over`. +- `ops-D15`, `ops-D16`: sidecar fields. Actual parser (`src/cli/agent.ts:256`) omits webSearch.streamRoutedModelOutput and vision.timeoutMs, both writable in GUI. Recommend `agent sidecar web --stream-routed-output on|off`, `agent sidecar vision --timeout-ms N`. +- `ops-O13`, `ops-I15`, `ops-I16`: timeline and Cursor reads are advertised by capabilities but absent from real handlers. Recommend `companion timeline` and `integration native cursor status|local-installer` (read-only). +- `ops-I10`/profile part of `ops-I13`, `ops-R01`/`ops-R07`: declared journal retirement and Hub read debt. Preserve registry owners; executor-local remote-workspace status is not Hub status. +- `ops-I06`, `ops-L07`: Droid reasoning-default payload and link force-removal option absent from existing writer/parser. +- `ops-K08`: `access test` exists, but does not accept the newly created data key and uses runtimeRequest management headers (`src/cli/access.ts:255`, `src/cli/runtime-api.ts:142`). It is not proof that the chosen key works. Recommend a separate `--api-key-stdin` path with explicit inference authorization and no secret echo; do not use the admin-header helper for data-plane key verification. + +Local/native counterexamples: startup repair invokes `ocx service repair`; shim repair invokes `ocx codex-shim install` (`src/server/startup-action-control.ts:109`); Windows tray writes invoke `ocx tray --json` (`src/server/windows-tray-control.ts:9`). None is a missing effect merely because an HTTP route has no literal CLI caller. + +## Per-task join + +Commands below omit the common `ocx` prefix for readability. Each ID maps to the operations inventory. Proposed flags are recommendations, not existing features. + +| Task and GUI anchor | Status | Existing commands | Missing field/workflow or recommended syntax | Targeted proof | +|---|---|---|---|---| +| **ops-D01** — Read live health/version/uptime and provider overview; `gui/src/pages/dashboard-core-poll.ts:275` | UNKNOWN | status --json; health --json; system status --json | Liveness exists; system status bundles settings/startup/memory, not system/health. Exact remote version/uptime/spendLedger projection requires target comparison. Proposed system status extension only if needed. | src/cli/system-command.ts:35; src/cli/dispatch.ts:787; src/cli/index.ts:1677 | +| **ops-D02** — Read model catalog and 30d usage; `gui/src/pages/dashboard-core-poll.ts:135`, `:142` | DISCOVERY_ONLY | models live --json; usage --range 30d --json | Catalog and usage reads exist; publish model live leaf. Provider/account projection details remain sibling-owned. | src/cli/models-runtime.ts:73; src/cli/observe.ts:209 | +| **ops-D03** — Read runtime/client preferences; `gui/src/pages/dashboard-core-poll.ts:218` | DISCOVERY_ONLY | system settings --json | No field gap; complete settings leaf metadata. | src/cli/system-command.ts:123 | +| **ops-D04** — Set Codex autostart; `gui/src/pages/use-dashboard-data.ts:753`, `:777` | DISCOVERY_ONLY | system settings --auto-start on/off --json | Boolean supported; no new setter needed. | src/cli/system-command.ts:127 | +| **ops-D05** — Set Desktop authless / client compaction; `gui/src/pages/use-dashboard-data.ts:768`, `:778` | DISCOVERY_ONLY | system settings --desktop-authless on/off --client-compaction on/off --json; then system sync --json | Stored/effective/file-apply result exists. GUI auto-sync is expressible as sequence; document partial apply and explicit sync. | src/cli/system-command.ts:61; src/cli/system-command.ts:139; src/cli/system-command.ts:206 | +| **ops-D06** — Sync client catalogs/configuration; `gui/src/pages/use-dashboard-data.ts:790`, `:796` | DISCOVERY_ONLY | system sync --json | Same POST/result; register leaf. Connected machine uses S05, not shared-hub target. | src/cli/system-command.ts:206 | +| **ops-D07** — Read project-config warnings; `gui/src/pages/dashboard-core-poll.ts:126` | DISCOVERY_ONLY | system diagnostics --json | No missing query; register child metadata. | src/cli/system-command.ts:204 | +| **ops-D08** — Read startup protection; `gui/src/pages/dashboard-core-poll.ts:104`; `gui/src/pages/Startup.tsx:140` | DISCOVERY_ONLY | system startup health/status --json | Startup protection readable. Existing hidden __startup-health is not a proposed public task. | src/cli/system-command.ts:150; src/cli/dispatch.ts:760 | +| **ops-D09** — Read memory/storage pressure and active turns; `gui/src/components/MemoryObservabilityCard.tsx:243` | DISCOVERY_ONLY | observe memory --json; memory --json | Full metrics/active-turn payload readable; register diagnostic leaf. | src/cli/observe.ts:280 | +| **ops-D10** — Drain and restart proxy; `gui/src/components/MemoryObservabilityCard.tsx:359` | COMPLETE | restart | Live drain/replacement exists; fallback ensure if stopped. Do not replace with ordinary stop/start. JSON/error-contract work is shared audit. | src/cli/dispatch.ts:777; src/cli/system-restart-client.ts:1 | +| **ops-D11** — Check latest/preview package update; `gui/src/pages/use-dashboard-data.ts:824` | DISCOVERY_ONLY | system update check --channel latest/preview --json | Both GUI channels supported; register leaf. | src/cli/system-command.ts:166 | +| **ops-D12** — Run package update, optionally restart; `gui/src/pages/use-dashboard-data.ts:909` | DISCOVERY_ONLY | system update run --channel latest/preview --restart on/off --yes --json | All payload fields exist. Accepted job is not completed update; use D13. | src/cli/system-command.ts:184 | +| **ops-D13** — Observe update job; `gui/src/pages/use-dashboard-data.ts:493` | DISCOVERY_ONLY | system update status JOB-ID --json | Polling can be composed; --wait is optional convenience, not absent backend capability. | src/cli/system-command.ts:169 | +| **ops-D14** — Desktop-shell update navigation; `gui/src/pages/use-dashboard-data.ts:6` (openDesktopUpdatePage dependency), `gui/src/pages/dashboard-overview-sections.tsx:220` | EXCLUDED | Desktop update UI panel | Shell navigation/native updater display differs from package job; preserve desktop-internal snapshot exemption. | src/server/management/route-registry.ts:376; gui/src/pages/use-dashboard-data.ts:6 | +| **ops-D15** — Web search sidecar off/on/model/stream; `gui/src/pages/dashboard-overview-sections.tsx:585`, `:606`; save `gui/src/pages/use-dashboard-data.ts:563` | IMPLEMENTATION_GAP | agent sidecar web --model ID --backend BACKEND --enabled on/off --json | Missing webSearch.streamRoutedModelOutput. Recommend --stream-routed-output on/off. Selecting a model after Off needs --enabled on, already supported. | src/cli/agent.ts:256; gui/src/pages/dashboard-overview-sections.tsx:606 | +| **ops-D16** — Vision sidecar off/on/model/reasoning/limits/timeout; `gui/src/pages/dashboard-overview-sections.tsx:467`, `:480`, `:628`, `:648` | IMPLEMENTATION_GAP | agent sidecar vision --model ID --backend BACKEND --reasoning LEVEL --max-descriptions N --enabled on/off --json | Missing vision.timeoutMs. Recommend --timeout-ms N; preserve backend/model coherence and sibling fields. | src/cli/agent.ts:256; gui/src/pages/dashboard-overview-sections.tsx:480 | +| **ops-D17** — Shadow-call interception toggle/model; `gui/src/pages/dashboard-overview-sections.tsx:716`, `:728`; save `gui/src/pages/use-dashboard-data.ts:610` | DISCOVERY_ONLY | models shadow status --json; models shadow set MODEL --enabled on/off --json | Enabled/model supported, '-' clears model. Publish leaf, no new writer. | src/cli/models-runtime.ts:546 | +| **ops-D18** — Multi-agent mode/advisory, injection/guidance/defaults, effort caps; `gui/src/pages/use-dashboard-data.ts:640`, `:679`, `:727`; `gui/src/pages/dashboard-overview-sections.tsx:78`, `:103` | UNKNOWN | v2; agent injection/effort/subagents/roles | Sibling owns multi-agent/advisory/injection/compaction/memory-model field join. No duplicate completeness claim here. | gui/src/pages/use-dashboard-data.ts:640; gui/src/pages/use-dashboard-data.ts:679; src/cli/agent.ts:48 | +| **ops-S01** — Install service / repair service / install shim; `gui/src/pages/Startup.tsx:290`, `:294` | COMPLETE | service install; service repair; codex-shim install; system startup install-service/install-shim --json | GUI launcher directly maps to these commands. system startup has no --repair, but direct service repair is equivalent; shim repair is install. | src/server/startup-action-control.ts:109; src/service/cli.ts:233; src/cli/dispatch.ts:679 | +| **ops-S02** — Windows tray status; `gui/src/pages/Startup.tsx:176` | COMPLETE | tray status --json; inspect windows-tray --json | Local command and management read both exist; platform support reported. | src/tray/windows.ts:784; src/cli/inspect.ts:213 | +| **ops-S03** — Windows tray install/start/stop/uninstall; `gui/src/pages/Startup.tsx:268` | COMPLETE | tray install/start/stop/uninstall --json | GUI backend invokes this actual CLI; no missing route-effect gap. | src/server/windows-tray-control.ts:9; src/tray/windows.ts:784 | +| **ops-S04** — Connected-machine shim status/install/repair/uninstall; `gui/src/pages/Startup.tsx:97`, `:104` | COMPLETE | codex-shim status/install/uninstall | Same diagnose/install/uninstall functions; repair maps to install in machine owner. Must run on client machine, not hub. Structured status is output enhancement only. | src/client/machine-api.ts:113; src/client/machine-api.ts:125; src/cli/dispatch.ts:695 | +| **ops-S05** — Read connected-machine selected clients / sync them; `gui/src/pages/Integrations.tsx:62`, `:72` | UNKNOWN | sync on connected machine | Mutation exists; selectedClients+journalOwner display and machine-target receipt need client-state owner join. Keep domain sync syntax, not generic machine HTTP. | src/client/machine-api.ts:87; src/cli/dispatch.ts:508 | +| **ops-C01** — Read Claude Code effective state/model choices; `gui/src/pages/ClaudeCode.tsx:88` | COMPLETE | claude config status --json | Same effective-state GET and fields. | src/cli/integrations.ts:64 | +| **ops-C02** — Enable/disable Claude connection immediately; `gui/src/pages/ClaudeCode.tsx:166`; `gui/src/pages/use-claude-connection.ts:2` | COMPLETE | integration native claude on/off --json | Same immediate native mutation; local config edit alone would not count. | src/cli/inspect.ts:85; src/cli/inspect.ts:113 | +| **ops-C03** — Enable/disable CLI first-party proxy environment; `gui/src/pages/ClaudeCode.tsx:178` | COMPLETE | claude config set --first-party on/off --json | cliFirstParty sent alone; shared-proxy-retained warning preserved. | src/cli/integrations.ts:78; src/cli/integrations.ts:121 | +| **ops-C04** — Save Code behavior/auth/env/model mappings/helpers; `gui/src/pages/ClaudeCode.tsx:217` | COMPLETE | claude config set --enabled --auth-mode --system-env --fast-mode --auto-context --compact-window --inject-agents --small-fast-model --model-map --web-model --web-backend --vision-model --vision-backend | All GUI fields supported. Restore helper inheritance with both --web-model - --web-backend - (vision analog); backend removes empty override. Use typed values shown by handler usage. | src/cli/integrations.ts:16; src/cli/integrations.ts:78; src/server/management/agent-settings-routes.ts:1642 | +| **ops-C05** — Start configured Claude intercept; `gui/src/components/ClaudeInterceptStart.tsx:18` | COMPLETE | claude intercept start --json | Exact configured-proxy start exists; no trust-install bypass. | src/cli/integrations.ts:372 | +| **ops-C06** — Read Desktop editable profile/catalog and applied health; `gui/src/pages/ClaudeDesktop.tsx:270`, `:354` | UNKNOWN | claude desktop status --json; claude desktop show --json | status is live, show builds local desired profile; connected GUI editor reads hub. Consider explicit runtime-scope profile read after main target design. | src/cli/claude-desktop.ts:742; src/cli/claude-desktop.ts:751; src/cli/claude-desktop.ts:790 | +| **ops-C07** — Edit Desktop assignment family/alias/default and save; `gui/src/pages/ClaudeDesktop.tsx:24`, `:418`, `:806`, `:826` | UNKNOWN | claude desktop move ROUTE FAMILY [--default]; default FAMILY ROUTE-or-none; import FILE | Local assignments/defaults/import exist; hub desired-profile target differs. Alias can be imported as profile JSON. Parent owns live-vs-local contract, no generic config workaround. | src/cli/claude-desktop.ts:806; src/cli/claude-desktop.ts:815; src/cli/claude-desktop.ts:833 | +| **ops-C08** — Save then apply Desktop first-party/gateway; `gui/src/pages/ClaudeDesktop.tsx:433` | COMPLETE | claude desktop apply --first-party; apply --gateway | Mode-aware apply exists and preserves applied-marker warning. import --apply is gateway-only; separate apply selects first-party. Local/client paths guarded. | src/cli/claude-desktop.ts:676; src/cli/claude-desktop.ts:393; src/cli/claude-desktop.ts:479 | +| **ops-C09** — Bind/unbind native picker ID to route; `gui/src/components/ClaudeFirstPartyBindings.tsx:59` | COMPLETE | claude desktop bind PICKER-ID ROUTE; unbind PICKER-ID | Exact set/remove live binding payloads exist. | src/cli/claude-desktop.ts:766 | +| **ops-C10** — Enable/disable Desktop picker proxy; `gui/src/components/ClaudeDesktopPicker.tsx:97` | COMPLETE | claude desktop picker status/on/off | GET/PUT and trust workflow exist. Preserve trusted-local and interactive requirements; no raw persist/trust bypass. | src/cli/claude-desktop.ts:554; src/cli/claude-desktop.ts:623; src/cli/claude-desktop.ts:746 | +| **ops-C11** — Export Desktop profile JSON; `gui/src/pages/ClaudeDesktop.tsx:468` | COMPLETE | claude desktop export PATH-or-dash | Artifact export exists. GUI can export unsaved draft; CLI has no GUI draft and exports persisted local profile. Draft-state visualization is not absent export capability. | src/cli/claude-desktop.ts:824; gui/src/pages/ClaudeDesktop.tsx:468 | +| **ops-C12** — Import Desktop profile JSON; `gui/src/pages/ClaudeDesktop.tsx:480` | UNKNOWN | claude desktop import FILE [--apply] | CLI local import immediately persists; GUI imports draft then saves selected runtime. Connected/hub target ambiguity remains C07; reuse parser rather than inventing second one. | src/cli/claude-desktop.ts:833; gui/src/pages/ClaudeDesktop.tsx:480 | +| **ops-G01** — Read Grok fence/catalog/status; `gui/src/pages/Grok.tsx:81` | DISCOVERY_ONLY | grok status/show --json | Same status implemented; incomplete route/leaf declaration in CLI inventory. | src/cli/integrations.ts:142 | +| **ops-G02** — Save included/excluded Grok model selection; `gui/src/pages/Grok.tsx:151` | DISCOVERY_ONLY | grok set/include/exclude CSV --json; grok clear --json | Excluded-list replacement/add/remove/clear supported; publish precise grammar. | src/cli/integrations.ts:160 | +| **ops-G03** — Save and apply Grok fence; `gui/src/pages/Grok.tsx:171` | DISCOVERY_ONLY | grok set CSV --json; then grok apply --json | Save then apply expressible; changed/skippedReason preserved in JSON. Register leaves; no duplicated writer. | src/cli/integrations.ts:153; src/cli/integrations.ts:174 | +| **ops-I01** — List native integration desired/observed state; `gui/src/pages/integrations/native-api.ts:144`; overview `IntegrationsOverview.tsx:605` | COMPLETE | integration native list --json | Same desired/observed four-client collection. | src/cli/inspect.ts:90 | +| **ops-I02** — Toggle native Grok / Codex / Claude Desktop; `gui/src/pages/integrations/native-api.ts:155`; overview `IntegrationsOverview.tsx:604` | COMPLETE | integration native grok/codex/claude-desktop on/off --json | All three mutations exist; backend disable refusal remains authoritative. | src/cli/inspect.ts:49; src/cli/inspect.ts:113 | +| **ops-I03** — List file-client state / inspect one; `gui/src/pages/integrations/integration-api.ts:435`, `:441` | DISCOVERY_ONLY | integration client status/list/show [--client ID] --json | Collection and one-client reads exist. Capability focuses Aside; declare ordinary client routes too. | src/cli/integrations.ts:232; src/cli/integrations.ts:49 | +| **ops-I04** — Preview apply / overwrite / disable; `gui/src/pages/integrations/FileIntegrationPage.tsx:219`; `integration-api.ts:475` | EXCLUDED | Existing apply/disable at I05 | Interactive-preview exemption intentional. Optional future integration client preview --client ID --operation apply/overwrite/disable requires parent policy change and bound follow-up mutation. | src/server/management/route-registry.ts:232; src/cli/integrations.ts:299 | +| **ops-I05** — Apply / overwrite conflicting config / disable owned block; `gui/src/pages/integrations/FileIntegrationPage.tsx:251`; `integration-api.ts:512` | DISCOVERY_ONLY | integration client enable/disable --client ID [--overwrite-conflict] --json | Effects exist; no planFingerprint under current interactive-preview exemption. Preserve explicit overwrite waiver. Droid map is separate I06 gap. | src/cli/integrations.ts:299; src/cli/integrations.ts:319 | +| **ops-I06** — Set/clear per-model Droid reasoning defaults; `gui/src/pages/integrations/DroidReasoningDefaultsPanel.tsx:27`, `:38` | IMPLEMENTATION_GAP | integration client enable --client droid | No droidReasoningDefaults input. Recommend repeatable --reasoning-default MODEL=EFFORT and explicit --clear-reasoning-defaults; if preview adopted, same map must bind apply. | src/cli/integrations.ts:299; gui/src/pages/integrations/integration-api.ts:483; gui/src/pages/integrations/integration-api.ts:520 | +| **ops-I07** — Read rollback journal; `gui/src/pages/integrations/integration-api.ts:454` | DISCOVERY_ONLY | integration client history/journal [--client ID] --json | Journal read exists; publish non-Aside route/leaf and retain snapshot-expired status. | src/cli/integrations.ts:258 | +| **ops-I08** — Preview rollback, including drift-confirmed retry; `gui/src/pages/integrations/RestoreDialog.tsx:143`; `integration-api.ts:496` | EXCLUDED | integration client restore --op ID [--confirm-drift] | Preview intentionally exempt. Optional restore --op ID --preview needs explicit bound confirmation policy, not new generic POST access. | src/server/management/route-registry.ts:233; src/cli/integrations.ts:278 | +| **ops-I09** — Restore rollback snapshot; `gui/src/pages/integrations/RestoreDialog.tsx:191`; `integration-api.ts:543` | DISCOVERY_ONLY | integration client restore --op ID [--confirm-drift] --json | Restore and drift waiver implemented. Fingerprint not current scripted contract; don't label entire rollback missing. | src/cli/integrations.ts:278 | +| **ops-I10** — Retire older rollback entry/snapshot; `gui/src/pages/integrations/FileIntegrationPage.tsx:446`; overview `IntegrationsOverview.tsx:851`; `integration-api.ts:571` | IMPLEMENTATION_GAP | history and restore only | No journal DELETE task. Recommend integration client history remove --op ID --yes --json; preserve newest-row refusal and snapshotRemoved:false receipt. | src/cli/integrations.ts:258; src/cli/integrations.ts:299; src/server/management/route-registry.ts:230 | +| **ops-I11** — Disable all eligible file integrations; `gui/src/pages/integrations/IntegrationsOverview.tsx:443`, `:460`, `:512` | COMPLETE | Repeat integration client disable --client ID for explicit set | Existing sequential commands can disable the explicitly selected eligible set; this is a real write workflow, not presentation. GUI confirmation previews remain exempt. No atomic bulk endpoint; do not silently introduce wildcard/default-all scope. | gui/src/pages/integrations/IntegrationsOverview.tsx:443; gui/src/pages/integrations/IntegrationsOverview.tsx:512 | +| **ops-I12** — List Aside profiles / read one / toggle selected profile; `gui/src/pages/integrations/AsideProfilesPage.tsx:54`, `:89`, `:116`; `integration-api.ts:424` | COMPLETE | integration client status/enable/disable --client aside --profile N --json | Canonical per-profile selection exists; omitted profile intentionally aggregate. Preview remains I04 exemption. | src/cli/integrations.ts:40; src/cli/integrations.ts:49; src/cli/integrations.ts:230 | +| **ops-I13** — Aside profile rollback list/preview/restore/retire; `gui/src/pages/integrations/integration-api.ts:465`, `:503`, `:550`, `:585` | IMPLEMENTATION_GAP | history --client aside --profile N; restore --client aside --profile N --op ID [--confirm-drift] | Only retirement absent; preview exempt. Recommend history remove --client aside --profile N --op ID --yes --json with profile-response binding. | src/cli/integrations.ts:258; src/cli/integrations.ts:278; src/server/management/route-registry.ts:191 | +| **ops-I14** — Refresh all server-selected Aside profiles; `gui/src/pages/integrations/aside-profile-api.ts:38` | UNKNOWN | sync reaches refreshAsideProfilesThroughServer | Refresh effect exists, but broad sync also synchronizes catalogs/other clients. Parent decide if broader scope is acceptable or add integration client sync --client aside reusing helper. | src/cli/aside-profiles.ts:9; src/cli/aside-profiles.ts:69; src/cli/dispatch.ts:526 | +| **ops-I15** — Inspect Cursor installed builds/gateway/last-seen state; `gui/src/pages/integrations/CursorIntegrationPage.tsx:113`; `cursor-api.ts:34` | IMPLEMENTATION_GAP | integration native list/on/off; Cursor advertised only | Actual allowlist has no Cursor. Recommend integration native cursor status --json; read-only build/gateway/last-seen status. | src/cli/inspect.ts:49; src/cli/inspect.ts:99; src/cli/capabilities.ts:1015 | +| **ops-I16** — Check advertised Cursor local installer; `gui/src/pages/integrations/CursorIntegrationPage.tsx:81`; `cursor-api.ts:59` | IMPLEMENTATION_GAP | No Cursor installer-read command | Recommend integration native cursor local-installer --json. Explicit update-channel query only; URL/version output never triggers install. | src/cli/inspect.ts:85; gui/src/pages/integrations/cursor-api.ts:59; src/server/management/cursor-integration-routes.ts:152 | +| **ops-O01** — Read debug flags/env/runtime overrides; `gui/src/pages/Debug.tsx:48` | DISCOVERY_ONLY | observe debug --json; debug SCOPE status | Flags/env/overrides readable; publish scope grammar. | src/cli/observe.ts:281; src/cli/debug.ts:144 | +| **ops-O02** — Toggle provider/usage/injection/Claude inbound capture; `gui/src/pages/Debug.tsx:180`, `:210` | DISCOVERY_ONLY | debug provider/usage/injection/claude on/off | All four toggles supported. Legacy JSON mutation output absence is shared machine-output work, not missing action. | src/cli/debug.ts:144; src/cli/debug.ts:150 | +| **ops-O03** — Reset all capture overrides to env; `gui/src/pages/Debug.tsx:213` | COMPLETE | debug provider reset; debug usage reset; debug injection reset; debug claude reset | Four commands reach same final reset state; GUI atomic reset:true has no one-shot alias. Atomic reset-all optional unless parent contract requires it. | src/cli/debug.ts:163; gui/src/pages/Debug.tsx:213 | +| **ops-O04** — Read/follow provider, usage, injection debug buffers; `gui/src/pages/Debug.tsx:97`, `:115` | IMPLEMENTATION_GAP | debug provider logs -f; debug usage logs -f; observe injection --json | Injection snapshot exists, continuous follow/after cursor does not. debug injection logs refuses. Recommend observe injection --follow --jsonl with bounded cursor polling. | src/cli/debug.ts:171; src/cli/observe.ts:283; gui/src/pages/Debug.tsx:115; gui/src/pages/Debug.tsx:168 | +| **ops-O05** — Read Claude inbound metadata captures; `gui/src/pages/Debug.tsx:65` | DISCOVERY_ONLY | observe claude-inbound --json | Complete metadata snapshot read. Expose leaf with honest limit semantics. | src/cli/observe.ts:282 | +| **ops-O06** — Read/retry/incrementally poll request log; `gui/src/pages/Logs.tsx:614` | COMPLETE | logs --limit 2000 --json; logs --follow --jsonl | Read/follow exists. GUI cursor amendments differ from CLI new-ID dedupe; an in-flight-to-terminal fixture is needed before claiming identical update visibility. No new API necessary. | src/cli/observe.ts:79; src/cli/observe.ts:100; gui/src/pages/Logs.tsx:614 | +| **ops-O07** — Filter log snapshot by surface/model/provider/status/time/speed/interception/conversation/protocol; `gui/src/pages/Logs.tsx:728`; `gui/src/pages/logs-filter.ts:16`, `:103` | EXCLUDED | logs has provider/model/status/conversation/account filters | GUI local speed/intercept/protocol/time/view projections are presentation, not missing management mutations. More CLI read filters optional product choice. | gui/src/pages/Logs.tsx:728; gui/src/pages/logs-filter.ts:16; src/cli/observe.ts:85 | +| **ops-O08** — Read usage by preset/custom window, surface and local-machine attribution; `gui/src/pages/Usage.tsx:1110` | UNKNOWN | usage --range --surface --since --until --provider --model --json | Custom bounds exist. Connected CLI uses own-key Hub report; GUI chooses machine vs whole-Hub via apiKeyId. Need target/principal join before proposing --scope machine/hub; don't infer missing from absent apiKeyId flag alone. | src/cli/observe.ts:164; src/cli/observe.ts:188; gui/src/pages/Usage.tsx:1110 | +| **ops-O09** — Model text search, expand rows, tab and chart display; `gui/src/pages/Usage.tsx:1093`; chart components | EXCLUDED | No CLI task needed | Local search, expansion, tabs/graph styling. Server bounds remain O08. | gui/src/pages/Usage.tsx:1093 | +| **ops-O10** — Read companion preferences/presence; `gui/src/pages/usage-companion-panel.tsx:215`; Tray `gui/src/pages/Tray.tsx:100` | COMPLETE | companion show --json | Settings/defaults/presence/corrupt payload available. | src/cli/companion.ts:13 | +| **ops-O11** — Persist companion model/provider visibility and chart/menu appearance; `gui/src/pages/usage-companion-panel.tsx:302`, `:503` | COMPLETE | companion set KEY=VALUE ... --json | Domain settings map covers all GUI fields including JSON arrays, models=null and booleans. This is not generic API passthrough. | src/cli/companion.ts:20; gui/src/pages/usage-companion-utils.ts:9 | +| **ops-O12** — Restore companion defaults (including corrupt-state recovery); `gui/src/pages/usage-companion-panel.tsx:350` | COMPLETE | companion reset --json | Same reset:true and corrupt-settings recovery. | src/cli/companion.ts:36 | +| **ops-O13** — Read usage timeline for companion/chart/tray; `gui/src/pages/usage-companion-panel.tsx:257`; `gui/src/pages/Tray.tsx:118` | IMPLEMENTATION_GAP | companion show/set/reset only | Recommend companion timeline --hours N --bucket-minutes N --metric total/input/output/cached --aggregation sum/average/max --grouping model/modelAccount [--model ID repeated] [--hide-provider ID repeated] --json. Preserve truncated/missingMeasurements. | src/cli/companion.ts:46; gui/src/pages/usage-companion-utils.ts:52; src/server/management/usage-timeline-routes.ts:11 | +| **ops-O14** — Open companion view in system browser; `gui/src/pages/usage-companion-panel.tsx:367` | EXCLUDED | No standalone operator equivalent required | Browser navigation is declared session-only. No forged dashboard principal/open-url passthrough. | src/server/management/route-registry.ts:268; src/server/management/companion-routes.ts:30 | +| **ops-O15** — Tray refresh totals/accounts/quota; `gui/src/pages/Tray.tsx:79`, `:86`, `:104`; `gui/src/pages/tray-data.ts:27` | UNKNOWN | usage/companion show plus account read families | Sibling certifies account/quota fields; Tray display alone is excluded. | gui/src/pages/Tray.tsx:79; gui/src/pages/tray-data.ts:27 | +| **ops-O16** — Tray switch current account/key; `gui/src/pages/Tray.tsx:39`; `gui/src/pages/tray-data.ts:36` | UNKNOWN | Account selection families in inventory-cli.md | Sibling owns exact Codex/OAuth/provider-key switch join. Prefer account command, not Tray-specific verb. | gui/src/pages/Tray.tsx:39; gui/src/pages/tray-data.ts:36 | +| **ops-T01** — Inspect storage sizes/categories/Log Guard state; `gui/src/pages/Storage.tsx:1358` | COMPLETE | storage report --json | Same live storage/log-guard scan payload. | src/cli/storage.ts:233; src/server/management/storage-log-guard-routes.ts:216 | +| **ops-T02** — Preview oldest archived-session cleanup percentage; `gui/src/pages/Storage.tsx:177` | COMPLETE | storage cleanup --percent N --json | Default preview emits digest/candidates; registry mutates declaration doesn't change handler's preview semantics. | src/cli/storage.ts:73; src/cli/storage.ts:88 | +| **ops-T03** — Quarantine / permanently delete previewed archives; `gui/src/pages/Storage.tsx:201` | COMPLETE | storage cleanup --percent N --mode quarantine/permanent --yes --json | Fresh preview then exact returned digest. Standalone prior preview is recomputed, not reused; immutable preview receipt UX is optional, guard preserved. | src/cli/storage.ts:88; src/cli/storage.ts:105 | +| **ops-T04** — List quarantine batches; `gui/src/pages/Storage.tsx:392` | COMPLETE | storage trash list --json | Full quarantine batch list. | src/cli/storage.ts:117 | +| **ops-T05** — Restore a quarantine batch; `gui/src/pages/Storage.tsx:431` | COMPLETE | storage trash restore ENTRY-ID --yes --json | ID mutation with explicit confirmation exists; partial outcomes preserved in JSON/error. | src/cli/storage.ts:126; src/cli/storage.ts:141 | +| **ops-T06** — Read policy/job; `gui/src/pages/Storage.tsx:672`, `:865` | COMPLETE | storage policy show --json | Policy/job state available. | src/cli/storage.ts:153 | +| **ops-T07** — Enable/disable recurring cleanup / save threshold, target, schedule, mode; `gui/src/pages/Storage.tsx:709`, `:738` | IMPLEMENTATION_GAP | storage policy set --enabled --percent --mode --schedule --json | Missing trigger.archivedBytesOver and target.reduceToBytes. Recommend --archived-bytes-over BYTES plus exclusive --reduce-to-bytes BYTES / --remove-oldest-percent N, retaining --percent alias if chosen. Never implicitly enable. | src/cli/storage.ts:162; gui/src/pages/Storage.tsx:709 | +| **ops-T08** — Run cleanup policy now; `gui/src/pages/Storage.tsx:790`, `:809` | COMPLETE | storage policy set EXISTING-FIELDS; policy run --yes --json; policy show --json | Run + poll expressible; disabled stays disabled. T07 separately limits draft fields. Optional --wait must preserve skipped/deferred/failed terminal outcomes. | src/cli/storage.ts:207; src/cli/storage.ts:214; src/cli/storage.ts:153 | +| **ops-T09** — Inspect log protection fresh; `gui/src/components/storage-workspace/StorageWorkspace.tsx:515` | COMPLETE | storage codex-logs status --json; observe storage codex-logs status --json | Existing alias dispatch reaches actual read. | src/cli/storage.ts:223; src/cli/observe.ts:245 | +| **ops-T10** — Protect logs compat / quiet; `gui/src/components/storage-workspace/StorageWorkspace.tsx:251`, `:261`, `:460` | COMPLETE | storage codex-logs protect --mode compat/quiet --json | Exact protection payload. | src/cli/observe.ts:248 | +| **ops-T11** — Remove log protection / repair protection / compact logs; `gui/src/components/storage-workspace/StorageWorkspace.tsx:440`, `:463` | COMPLETE | storage codex-logs unprotect/repair/compact --json | Dynamic suffix dispatch reaches all three; no missing literal-route gap. | src/cli/observe.ts:258 | +| **ops-K01** — List access keys/usage/pending rotations, endpoints, auth matrix, API/audio surfaces; `gui/src/pages/ApiKeys.tsx:155` | COMPLETE | access key list --json; access endpoints --json | Full list JSON includes auth/audio/usage/pending rotation; endpoint-only helper projects fewer fields. Discover existing complete payload, don't add duplicate read. | src/cli/access.ts:136; src/cli/access.ts:241 | +| **ops-K02** — Create key with name; `gui/src/pages/ApiKeys.tsx:268` | COMPLETE | access key create NAME --json | Secret returned once. Preserve operating-skill restriction on agent receipt of raw keys; no credentials generated here. | src/cli/access.ts:143 | +| **ops-K03** — Rename key; `gui/src/pages/ApiKeys.tsx:327` | IMPLEMENTATION_GAP | access key set edits scopes only | Recommend access key rename ID-or-NAME NEW-NAME --json; reuse unambiguous findKeyRow, PATCH only {id,name}, leave scope unchanged. | src/cli/access.ts:168; src/cli/access.ts:109; gui/src/pages/ApiKeys.tsx:327 | +| **ops-K04** — Revoke key; `gui/src/pages/ApiKeys.tsx:302` | COMPLETE | access key remove ID --yes --json | Explicit ID revocation and confirmation exist. | src/cli/access.ts:224 | +| **ops-K05** — Start rotation; `gui/src/pages/ApiKeys.tsx:349` | COMPLETE | access key rotate ID --json | Start returns key and rotationId. Actual grammar is rotate ID, not rotate start ID. | src/cli/access.ts:197 | +| **ops-K06** — Commit rotation / abort rotation; `gui/src/pages/ApiKeys.tsx:371` | COMPLETE | access key rotate commit ID ROTATION-ID --json; rotate abort ID ROTATION-ID --json | Both methods and fields supported. | src/cli/access.ts:198; src/cli/access.ts:217 | +| **ops-K07** — Read public external-model catalog; `gui/src/pages/ApiKeys.tsx:192` | DISCOVERY_ONLY | access models --json | Public /v1/models read exists, distinct from management models live; publish leaf. | src/cli/access.ts:246; src/cli/access.ts:279 | +| **ops-K08** — Test newly created key on Responses / Chat / Messages for selected model; `gui/src/pages/ApiKeys.tsx:443`, `:456` | IMPLEMENTATION_GAP | access test MODEL --protocol responses/chat/messages --json | Existing inference test has no selected-key input and uses management headers; cannot prove newly created key works. Recommend --api-key-stdin with no secret echo/argv/env and explicit inference authorization. Ping text/token limit differences alone not the gap. | src/cli/access.ts:255; src/cli/runtime-api.ts:142; gui/src/pages/ApiKeys.tsx:456 | +| **ops-K09** — Read generated client configuration / copy snippet; `gui/src/components/apikeys-workspace/ClientConfigRow.tsx:53` | COMPLETE | inspect client-config --client ID --json | Same generated config envelope. Export artifact and integration apply remain separate tasks. | src/cli/inspect.ts:157 | +| **ops-K10** — Toggle public Messages API; `gui/src/pages/api-surface-cards.tsx:52` | COMPLETE | api policy --messages on/off --json | Exact messagesEnabled field; backend also updates legacy Claude inbound on closing. | src/cli/api-protocols.ts:152; src/server/management/protocol-settings-patch.ts:132 | +| **ops-K11** — Preview model's protocol path/features; `gui/src/components/protocols/ProtocolPlanPanel.tsx` → `gui/src/protocol-api.ts:91` | COMPLETE | api explain --model ID --inbound responses/chat/messages --feature FEATURE [repeated] --json | Same preview/features/policy revision. Never call preview a successful live model probe. | src/cli/api-protocols.ts:130 | +| **ops-K12** — Dictation file upload and cancel; `gui/src/components/apikeys-workspace/AudioApiPanel.tsx:2`; `gui/src/audio-api-client.ts:13` | UNKNOWN | No scoped audio CLI identified in supplied CLI inventory | Real data-plane upload, parent inclusion decision. If included: access audio transcribe FILE --model ID --api-key-stdin --json, bounded upload/cancel. Do not silently create new audio product. | gui/src/audio-api-client.ts:13; src/server/index/serve-options.ts:1594 | +| **ops-K13** — Live voice connect/disconnect probe; `gui/src/audio-api-client.ts:88` | UNKNOWN | No scoped live-audio probe identified in supplied CLI inventory | Parent inclusion decision. If included: access audio live-check --model ID --api-key-stdin --json with timeout/session.close; connection-only, no microphone/delegation execution claim. | gui/src/audio-api-client.ts:88; src/server/live.ts:348 | +| **ops-K14** — Download generated client configuration; `gui/src/components/apikeys-workspace/ClientConfigPanel.tsx:78` | COMPLETE | export --client ID --out PATH [--force] [--json] | Existing artifact task, explicit destination/non-clobber default. Native format default; --json requests JSON representation, not apply. | src/cli/export-command.ts:158; gui/src/components/apikeys-workspace/ClientConfigPanel.tsx:78 | +| **ops-R01** — Hub devices/runtime availability/session status; `gui/src/pages/RemoteWorkspace.tsx:93` | IMPLEMENTATION_GAP | remote-workspace status is executor-local | Recommend remote-workspace hub status --json aggregating devices/runtimes/sessions via runtime management auth/target, not executor file store. | src/cli/remote-workspace.ts:80; src/server/management/route-registry.ts:397; src/server/management/remote-workspace-routes.ts:61 | +| **ops-R02** — Create device enrollment code; `gui/src/pages/RemoteWorkspace.tsx:172` | EXCLUDED | remote-workspace pair consumes enrollment code | Grant creation is paired-dashboard consent; no --yes CLI minting/session impersonation. Preserve create-vs-consume roles. | src/server/management/route-registry.ts:400; src/server/management/remote-workspace-routes.ts:12 | +| **ops-R03** — Start hub model session on selected remote root; `gui/src/pages/RemoteWorkspace.tsx:184` | EXCLUDED | No CLI proposed | Hub-authenticated model session start and access mode remain dashboard consent. | src/server/management/route-registry.ts:401 | +| **ops-R04** — Submit prompt to bound session; `gui/src/pages/RemoteWorkspace.tsx:208` | EXCLUDED | No CLI proposed | Prompt may execute remote tools; do not use task parity to bypass session principal. | src/server/management/route-registry.ts:402 | +| **ops-R05** — Stop session; `gui/src/pages/RemoteWorkspace.tsx:243` | EXCLUDED | No CLI proposed | Interactive remote session stop remains paired-session action. | src/server/management/route-registry.ts:404 | +| **ops-R06** — Revoke device and stop its sessions; `gui/src/pages/RemoteWorkspace.tsx:256` | EXCLUDED | No CLI proposed | Device identity revocation and related session stops remain paired-session actions. | src/server/management/route-registry.ts:403 | +| **ops-R07** — Read individual Hub runtimes/sessions endpoints | IMPLEMENTATION_GAP | No Hub runtime/session list command | Recommend remote-workspace hub runtimes --json and hub sessions --json. Declared debt; GUI currently reads both through aggregate R01 rather than distinct buttons. | src/server/management/route-registry.ts:398; src/server/management/route-registry.ts:399 | +| **ops-L01** — Read link topology/status; `gui/src/pages/RemoteLink.tsx:186` | COMPLETE | link status --json | Admin topology DTO intentionally omits dashboard joinAvailable; do not forge consent field to match presentation. | src/cli/link.ts:256; src/server/management/link-routes.ts:580 | +| **ops-L02** — Discover/rescan SSH aliases; `gui/src/pages/RemoteLink.tsx:262` | EXCLUDED | No CLI proposed | SSH candidates discovery remains dashboard-session-only contract. | src/server/management/route-registry.ts:389 | +| **ops-L03** — Probe host key; `gui/src/pages/RemoteLink.tsx:282` | EXCLUDED | No CLI proposed | Host-key probe is interactive fingerprint-consent flow. | src/server/management/route-registry.ts:390 | +| **ops-L04** — Accept shown fingerprint and validate remote OCX; `gui/src/pages/RemoteLink.tsx:298` | EXCLUDED | No CLI proposed | Persisting fingerprint requires observed pending key and consent; never auto-confirm from metadata. | src/server/management/route-registry.ts:391 | +| **ops-L05** — Home adds child; `gui/src/pages/RemoteLink.tsx:313` | EXCLUDED | link issue is existing lower-level admin primitive | Not equivalent to Home-adds-Child transaction; retain session-only boundary rather than generic wrapper. | src/server/management/route-registry.ts:393; src/cli/link.ts:234 | +| **ops-L06** — Standalone child joins Home and restarts; `gui/src/pages/RemoteLink.tsx:333` | EXCLUDED | No CLI proposed | Child join/restart consent contract unchanged; issue/port primitives alone not workflow parity. | src/server/management/route-registry.ts:392 | +| **ops-L07** — Disconnect link / force-remove after remote cleanup failure; `gui/src/pages/RemoteLink.tsx:371`, `:422` | IMPLEMENTATION_GAP | link revoke --link-id ID --json | Normal revoke exists; parser rejects --force and sends no body. Recommend --force --yes for explicit forced local removal with residual remote-cleanup warning, normal defaults unchanged. | src/cli/link.ts:260; src/server/management/link-routes.ts:510; gui/src/pages/RemoteLink.tsx:371 | + +## Declared debt and intentional exclusions + +- Journal retirement: registry `src/server/management/route-registry.ts:230`, `:188`, `:191`; owner `260904_priority65_closeout WP7`, tracked source `devlog/_fin/260904_priority65_closeout/060_wp7_rollback_journal_crud.md`. Global/profile mutations are GUI reachable; aggregate Aside DELETE is declared but current helper does not use that aggregate spelling. +- Remote Workspace Hub reads: registry `src/server/management/route-registry.ts:397`, `:398`, `:399`; owner `remote-workspace-cli-followup`, tracked source `docs-site/src/content/docs/reference/management-api.md:214`. Existing executor-local status is not a replacement. +- Interactive plans: registry `src/server/management/route-registry.ts:232`, `:233`, `:190`. Excluded under current declared contract, although useful plan-first CLI workflows are a product option. Do not omit planFingerprint if adopting bound confirmation; stale plans require renewed confirmation. +- Link GUI admissions and Remote Workspace writes retain registry session-only classifications. GitHub star POST must never gain an agent action (`src/server/management/route-registry.ts:380`); current inspect star is read-only (`src/cli/inspect.ts:192`). +- App session logout (`src/server/management/route-registry.ts:372`), desktop update snapshot (`:376`), companion browser navigation (`:268`), old Aside aliases (`:180`, `:181`), disabled config PUT (`:223`) and storage test streams (`:261`, `:263`) do not create new operational CLI debt. + +## Proof to request during implementation + +No code or tests were executed in this pass. Use isolated HTTP fixtures with RuntimeApiDeps (`src/cli/runtime-api.ts:136`) and controlled homes; shared auth helpers otherwise read normal user state. Assert parser → exact method/path/body/query → projection/exit status; no live proxy or real keys needed. + +1. Timeline/Cursor/Hub reads: route/flag encoding, read-only behavior, malformed response and meaningful empty state. Validate actual handler dispatch, not only capabilities route membership. +2. Rename: PATCH exactly id/name, preserve provider/model scopes, reject ambiguous selector without write. Sidecar additions: partial patch retains siblings, booleans and timeout bounds preserved. +3. Cleanup fields: mutually exclusive target forms and trigger threshold; absent enabled preserved. Droid defaults: same submitted map reaches chosen client/profile and any plan binding. +4. Journal retire: global and Aside-profile route, newest-row 409, missing-row 404, snapshotRemoved:false visible. Link force: explicit confirmation, only requested force:true body, residual remote cleanup stays visible. +5. Key test: fake sentinel secret from controlled stdin, dedicated data header only, no management token injection, no secret in stdout/stderr/errors. Prove selected-key rejection cannot pass via loopback bypass. +6. Follow injection: after cursor advances, empty poll/no duplicates, interruption, malformed/error response. Log row amendment visibility is separately unresolved in O06. +7. Target-dependent UNKNOWNs: verify Desktop local versus Hub profile, connected usage own-key versus Hub scope, machine sync selected-client projection, and narrow Aside-only refresh before promoting to COMPLETE or a gap. + +Parent boundary: command spelling/flags above are proposals. Main owns final convention, receipt/error/exit design, authorization policy and roadmap sequencing. Audio probe inclusion and sibling account/subagent joins remain explicitly UNKNOWN. No generic API passthrough or generic config editing is counted as parity. + + +Static artifact receipt: 109 unique ops task rows; all 344 explicit source file/line anchors resolve; every operations-inventory ID has exactly one join row; Markdown column counts checked. Status totals: UNKNOWN=12, DISCOVERY_ONLY=23, COMPLETE=43, EXCLUDED=16, IMPLEMENTATION_GAP=15. No application execution or runtime success inferred. diff --git a/devlog/_fin/261003_cli_gui_parity/006_target_contracts.md b/devlog/_fin/261003_cli_gui_parity/006_target_contracts.md new file mode 100644 index 00000000000..d45b836874e --- /dev/null +++ b/devlog/_fin/261003_cli_gui_parity/006_target_contracts.md @@ -0,0 +1,219 @@ +# wp0 target crux: local versus live settings workflows + +2026-10-03, source-only bounded decision record. Resolves set-M06/M13/S04, provider P02/P05 and logout P10 for roadmap selection. No tracked files, prior artifacts, user config, credentials, proxy, upstream or tests were changed/run. Source establishes the transport/side-effect differences sufficiently to choose implementation; it does not establish runtime parity. Existing isolated tests were inspected, not executed. + +## Concrete roadmap choice + +**Retain existing roots and local behavior. Add an explicit `--live` option to the existing commands for exact management-target GUI behavior, with structured receipts. Do not auto-fallback between local and live; do not create `agent mode` or a generic API passthrough.** The only new API surface work needed by these cruxes is CLI transport/options, not new server routes. `--json` selects output, never target. + +| Tasks | Accepted implementation choice for roadmap | Smallest before → after | +|---|---|---| +| set-M06 | Existing `models add/remove` gain `--live --json`; keep existing local add/remove default and existing live `models edit`. Add proper local JSON save/sync disposition as output enhancement. | Before: add/remove modify local config and optionally perform broad Codex sync, emit prose; edit is live. After: explicit live add/remove use the same server custom-model handlers as GUI, return its identity and catalog receipt; local remains available and is labeled local. | +| set-M13 | Reuse `v2 status`, `mode`, `keep-native-v1`, `threads`, plus existing on/off. Add `--live --json` and explicit advisory acknowledgment for live mode changes. Keep current local implementation and add strict JSON output/argument parsing. | Before: local files/Codex helpers, mode/keep sync, prose, no advisory-version write; trailing flags are not systematically rejected. After: live option operates on the selected management target and returns server post-write state/advisory/catalogRefresh; no-live remains existing local functionality. | +| set-S04 | Reuse `v2 mode-hint TEXT` and `v2 mode-hint --clear`; route through the same narrow v2 live option when requested. Local set/null writer already exists and needs only machine output/discovery. | Before: shared local hint helper already implements set/null and rejects blanks. After: no duplicate hint implementation; optional live path receives the target runtime's capability/refusal and receipt. | +| set-P02 | Existing `provider add` gains explicit live path; preserve offline default. Add missing typed fields (responses path/auth mode) to the same domain parser. | Before: local seed/save, `--json` bypasses `--sync`; target runtime not the mutation owner. After: live preset read/POST provider and receipt belong to one resolved server; local `--sync --json` actually executes the requested sync and reports disposition. | +| set-P05 | Existing `provider remove` and `provider set-default` gain `--live`. Existing `provider edit --enabled` already uses management and does not need a second endpoint. | Before: local remove refuses current default and only deletes local provider/custom-model config; live GUI can reassign default and removes OAuth account set/caps. After: explicit live operations use exactly those server validations and effects, with destructive confirmation for removal. | +| set-P10 logout | Existing top-level `logout P` gains `--live --json`; keep local logout default and its current removed/not-found semantics. | Before: shared auth-store removal occurs locally, but live caches/login state aren't cleared by this CLI branch. After: explicit live logout invokes `/api/oauth/logout?provider=P` and returns its actual success receipt; no unproved `removed:true` claim. | + +“Accepted” here is the explorer's concrete recommended lock for the parent's roadmap, not a claim that implementation or user-state mutation has occurred. Main retains naming/final phase authority. This choice is intentionally narrower than changing all existing command defaults. + +## Why machine-output enhancement alone is insufficient for live GUI parity + +### Shared persistence is not a shared target or completion receipt + +- `saveConfig` writes local config under a mutation lock and bumps a generation; its body is persistence, not a management request: `src/config.ts:370`. +- Server handlers mutate their retained live `config`, use preserving/rebase-aware persistence (`src/config/live-reconcile.ts:443`), and perform explicit live-store/cache/convergence work. Their GET config returns that retained config: `src/server/management/config-routes.ts:306`. +- `runtimeBaseUrl` selects the identity-checked live proxy or rejects absent/client-role management; it does not promise the dashboard's remote-relay target: `src/cli/runtime-api.ts:70`. Therefore `--live` means **the existing CLI management target**, not arbitrary GUI apiBase/remote equivalence. No new raw `--url` capability is proposed. +- Resolve base URL **once per multi-request workflow**, then pass a pinned `RuntimeApiDeps.baseUrl` to every request. Otherwise list/resolve/delete can re-resolve the listener between calls (`src/cli/runtime-api.ts:136`). This is a per-command target fix, not a new transport framework. +- Do not invoke the local handler before deciding `--live`; a refused or unreachable live request must never have already saved local config. Do not fall back after a transport error, timeout, 409 or partial receipt. + +### set-M06: custom model CRUD + +Local add writes `config.customModels`, calls `saveConfig`, then `syncCustomModelsIfLive`: `src/cli/models.ts:197`, `src/cli/models.ts:250`. Local remove follows the same save/sync family: `src/cli/models.ts:280`, `src/cli/models.ts:331`. The sync helper catches failures and emits warning text rather than returning a durable save/catalog receipt (`src/cli/models.ts:185`). + +`syncModelsToCodex(port, config=loadConfig())` checks local desired integration/service-home authority; depending on path it can refresh catalog/cache **and inject config** (`src/codex/sync.ts:99`, `src/codex/sync.ts:209`, `src/codex/sync.ts:293`). It is not equivalent to the GUI handler's catalog-only convergence. + +Live POST builds the same core `OcxCustomModel` fields, validates provider/collisions and mutates retained live config, then returns 201 `{...entry,catalogRefresh}` (`src/server/management/model-routes.ts:961`, `src/server/management/model-routes.ts:1001`). Live DELETE accepts UUID, updates live config, and returns `{ok:true,catalogRefresh}` (`src/server/management/model-routes.ts:1079`). Management convergence is bound to retained config and catalog-only scope (`src/server/management-api.ts:255`, `src/codex/management-convergence.ts:144`). + +**Resolved:** CRUD effects already exist, but a new live option is needed to claim the same target/management receipt. JSON-only changes cannot supply that. Existing local source remains a supported offline workflow. + +Exact live grammar: + +- `ocx models add P M [existing metadata flags] --live --json` → POST `/api/custom-models` with provider/modelId and provided displayName/contextWindow/inputModalities/reasoningEfforts/defaultReasoningEffort. No local `loadConfig()` preflight; target server validates provider. +- `ocx models remove UUID --yes --live --json` → DELETE `/api/custom-models/UUID`. +- Retain `P/M` removal convenience by GET `/api/custom-models` on the **same pinned target**, resolve exact-or-refuse using existing collision/slug rules, then DELETE UUID. Never resolve from local `customModels` before live deletion. +- Preserve empty reasoning `[]` versus inherit omission/null. Existing live edit parser already handles clear variants (`src/cli/models-runtime.ts:195`). + +### set-M13 / set-S04: reuse v2 + +Existing implementations are substantive, not aliases: `src/cli/v2.ts:109` status, `:144` hint set/clear, `:177` thread transition, `:191` mode save/sync, `:220` keep-native save/sync. Dispatch wires the root directly (`src/cli/dispatch.ts:539`). + +Server and local paths use the same `transitionMultiAgentV2` and `setMultiAgentModeHintText` helpers (`src/server/management/agent-settings-routes.ts:391`, `:415`, `:464`). Source establishes **local scalar/transition equivalence**, including blank hint rejection and null clear. It does not establish equivalence of surrounding target and convergence behavior: CLI sync is broad and local, while server returns post-write readings plus `catalogRefresh` (`src/server/management/agent-settings-routes.ts:484`). Local hint and threads paths do not issue management requests. + +Advisory semantics are now resolved: the GUI modal records explicit choice (`gui/src/pages/Models.tsx:2026`); the server validates acknowledgment is boolean and only true writes the advisory version **after** mode has landed (`src/server/management/agent-settings-routes.ts:372`, `:444`). There is no session-only principal gate or mandatory pre-write acknowledgment in `/api/v2`. Do not invent such a security guard. Do not auto-acknowledge just because a script requests a mode. + +Exact live grammar/mapping: + +| Existing root + proposed option | Exact live operation | +|---|---| +| `v2 status --live --json` | GET `/api/v2` | +| `v2 mode v1\|default\|v2 --live [--acknowledge-surface-advisory] --json` | PUT `{multiAgentMode, multiAgentSurfaceAdvisoryAcknowledged?:true}`; omission preserves unacknowledged state; default remains string `default`, not null. | +| `v2 keep-native-v1 on\|off --live --json` | PUT `{keepNativeChatGptOnV1:boolean}` | +| `v2 threads N --live --json` | PUT `{maxConcurrentThreadsPerSession:N}`, integer >=1 | +| `v2 mode-hint TEXT --live --json` | PUT `{multiAgentModeHintText:TEXT}`; nonblank raw text retained. | +| `v2 mode-hint --clear --live --json` | PUT `{multiAgentModeHintText:null}` | +| `v2 on\|off --live --json` | PUT `{enabled:boolean}`; retain server hybrid conflict refusal. | + +Use the same strict parser for local/live flags. `--json` must be consumed and unknown/trailing args rejected before local writers run; it must not be silently ignored. Local JSON must report `transport:local`, actual helper changed/no-op state and sync outcome without manufacturing server advisory or catalog receipts. Live JSON should preserve server returned state/warnings/catalogRefresh. If catalog refresh fails after persistence, do not claim rollback. + +### set-P02 / set-P05: provider lifecycle + +Local add parser seeds from registry/custom flags and writes local config (`src/cli/provider.ts:144`, `:192`, `:212`, `:274`). JSON mode returns before `--sync` handling (`:279`, `:292`). Existing test explicitly pins `needsSync:true` despite its misleading title (`tests/cli/cli-provider.test.ts:680`); fixing JSON-plus-sync is a deliberate behavior correction, not an untested assumption. + +Live POST validates the candidate, mutates retained live provider config, reconciles state stores, clears model cache and returns `{success:true,name,catalogRefresh}` (`src/server/management/provider-routes.ts:1372`, `:1400`). Live default selection rejects a disabled row and persists/reconciles a standalone `{setDefault:true}` (`:1459`). Local set-default only verifies existence before saving (`src/cli/provider.ts:439`, `:459`). + +Live remove is materially broader: default replacement and combo dependency checks (`src/server/management/provider-routes.ts:1824`), custom-model removal, cap cleanup, OAuth account-set removal, live-store reconciliation, model-cache invalidation and catalog receipt (`:1854`). Local remove refuses current default and deletes provider/custom-model config (`src/cli/provider.ts:345`, `:355`, `:364`). **Do not silently substitute the live delete in the existing unflagged command**: it would add credential deletion/default reassignment side effects. + +Exact live grammar: + +- `provider add P [existing fields plus --auth-mode MODE --responses-path PATH] --live --json` → POST `/api/providers` `{name,provider,setDefault?}`. Fetch target presets for canonical reserved provider seed; no local config mutation. Retain explicit `--force` overwrite intent. Existing POST is upsert: a read-before-post duplicate guard is not atomic create-only CAS; document that limit, do not claim a nonexistent server precondition. +- `provider set-default P --live --json` → standalone PATCH `{setDefault:true}`. +- `provider remove P --live --yes --json` → DELETE providers by name; server performs atomic default selection within its mutation path. Include removed credential scope in help/confirmation, and preserve dependentShadowIntercept/droppedCustomModels/catalogRefresh fields. +- `provider edit P --enabled on|off --json` is already management-backed; no new implementation needed for enable/disable. Do not overload adding `--live` on this already-live branch into a new transport selector. +- Reject `--live --sync`: live lifecycle endpoint already converges the catalog; broad local sync is a different action. Fix local `--sync --json` by running the requested local sync and reporting its actual returned/refused/skipped status; do not force it to `needsSync:false`. + +### set-P10: logout + +Both paths call `removeCredential`, which removes the active credential and chooses the next usable account under the store mutation lock (`src/oauth/store.ts:1093`). Local dispatch preserves useful removed/not-found semantics (`src/cli/dispatch.ts:425`). Store mutation persists and publishes account selection (`src/oauth/store.ts:828`), but publication uses in-process listeners (`src/lib/account-selection-events.ts:25`, `:36`); it is not a management cache-cleanup receipt. + +Server logout additionally clears login state, model/inflight catalog caches, provider/account quota caches and Devin direct caches (`src/server/management/oauth-account-routes.ts:373`). This is a concrete source difference; no timing probe against user state is needed to choose the live path. + +- `ocx logout P --live --json` → POST `/api/oauth/logout?provider=P`; no local credential read/remove first. +- Keep local `logout P --json` existing exit 4/removed receipt semantics. The live route returns `{success:true}` and is effectively idempotent for a missing credential; do not synthesize `removed:true` or local exit 4 from it. No server response extension is necessary for minimum GUI parity. +- Do not broaden login work in this crux. Browser preference/initial-login variants remain the separate P10/P20 parser gap already recorded. + +## Exact file plan + +New filenames below were checked absent at inspection time. Source files named existing must be extended narrowly; no shared transport framework is needed. + +| Work slice | Existing files to edit | New files proposed | Smallest responsibilities | +|---|---|---|---| +| Custom live add/remove + local receipts | `src/cli/models.ts`, `src/cli/models-runtime-subcommands.ts` only if dispatcher contract needs it | `src/cli/models-custom-runtime.ts`; `tests/cli/cli-models-custom-runtime.test.ts` | Route explicit live add/remove before local config access; typed bodies/same-target ID resolution; JSON local outcome separation. Reuse existing `src/cli/models-runtime.ts` edit implementation. | +| v2 target/JSON contract | `src/cli/v2.ts`, `src/cli/dispatch.ts`, `src/cli/registry.ts` | `src/cli/v2-runtime.ts`; `tests/cli/cli-v2-runtime.test.ts` | Parse common flags once, reuse existing local functions, route live operations to `/api/v2`, project truthful receipts. Add mode-hint to root help. No agent.ts mode taxonomy. | +| Provider live lifecycle | `src/cli/provider.ts`, `src/cli/provider-runtime.ts` only for shared parser/dispatch seam, `src/cli/registry.ts` | `src/cli/provider-lifecycle-runtime.ts`; `tests/cli/cli-provider-lifecycle-runtime.test.ts` | Explicit live add/default/remove; fetch target preset; reject local sync combination; preserve offline behavior. Existing local JSON-plus-sync test updated in `tests/cli/cli-provider.test.ts`. | +| Live logout | `src/cli/dispatch.ts`, `src/cli/registry.ts` | `src/cli/logout-command.ts`; `tests/cli/cli-logout-runtime.test.ts` | Extract or wrap existing validated root grammar, add explicit live branch and injectable RuntimeApiDeps, keep local behavior. No new account logout alias required. | +| Discovery/contracts/docs | `src/cli/capabilities.ts`; `skills/ocx/references/03_recipes.md`; `structure/runtime.md`, `structure/config.md`, `structure/clients/chatgpt-desktop.md`, `structure/ops/docs-and-release.md` as affected by each slice | none required for transport | Declare existing/new leaf options and distinguish local/live. Main owns canonical output/error envelope work. | +| Test layout | `scripts/test-layout/layout.json`, `tests/fixtures/test-layout-expected.json` | none | Register every new test file in both maps. Avoid enlarging ratcheted `tests/codex-integration/codex-v2-gate.test.ts`; use focused new siblings. | + +Verified ownership mapping is `structure/INDEX.md:124`: CLI is jointly owned by runtime/config/client-integration/desktop/docs-and-release documents. The file plan names the applicable existing owners; main should update only the affected invariants, following their nested instructions. No server source change is necessary to expose these existing routes. A future atomic create-only provider contract or richer logout removed receipt would be separate scope, not hidden in this roadmap. + +## Source evidence versus required implementation proof + +Existing test source confirms that these are real existing features, not names inferred from declarations: `tests/codex-integration/codex-v2-gate.test.ts:1641` covers hint write/clear; `:1661` covers nonblank whitespace preservation and missing/blank rejection; `:1742` covers v1/v2 thread-slot transitions. `tests/cli/cli-provider.test.ts:473` deliberately expects local default-provider removal refusal; `:680` pins JSON skip-sync. None were run in this pass. + +The plan can be locked from source: a local sync and an HTTP catalog-only receipt have demonstrably different code paths; local versus live logout cleanup is explicit; provider removal side effects differ. Running those existing local tests would not establish cross-target live parity, so no scratch probe or test run was warranted merely to choose the roadmap. + +Implementation gate (for main, not claimed complete): + +1. Fake `RuntimeApiDeps.fetchImpl/baseUrl/findLiveProxy`, temporary home/auth context only, with every unexpected network call failing. Test target pinning across read/resolve/write and no local read/write on live refusal. +2. Custom add/remove: exact metadata/null/empty variants; UUID versus slug collision; created entry and saved/catalog failure receipts; unflagged local path unchanged. +3. V2: all seven live mappings above; strict unknown/trailing args before any mutation; hint null/blank cases; advisory omitted by default and true only by explicit flag; mixed live target local home remains untouched; server partial persistence/convergence outcome stays visible. +4. Provider: live default delete reassigns through one DELETE, offline continues to refuse; dependent combos and credential cleanup are handler authority; missing provider and disabled default errors; live --sync rejected; local --sync --json calls sync and keeps truthful skipped/refused outcomes. Never assert read-before-create is CAS. +5. Logout: live request never calls local removeCredential; server receipt preserved; local removed/not-found JSON/exit unchanged; mocked handler tests demonstrate cleanup functions invoked, not upstream calls. +6. Use existing nonzero/error-body machinery; parent-owned machine-envelope work must retain RuntimeApiError.body on partial/failed-convergence responses. A tool exit or 2xx alone is not proof that persisted state and live/client state all converged. + +Remaining bounded limits: the existing CLI management target intentionally refuses a connected-client listener; dashboard remote relay equivalence is outside this change. Runtime receipts, target isolation and failure behavior remain to be tested during implementation. The transport choice and file plan no longer depend on an unexplained UNKNOWN. + + +# Operational parity target decisions + +2026-10-03, bounded explorer follow-up. Repository root `the task checkout`; all source anchors are relative to that root. This artifact supersedes the target-dependent labels in `gaps-operations.md` for the rows below. Main owns roadmap and command conventions. No tracked files, live proxy, real credentials, upstream traffic, branch state or orchestration were touched. Two focused test files ran with pure/mock inputs; details below distinguish executed proof from inspected tests. + +## Decisions ready for the roadmap + +| Existing task IDs | Resolved classification | Decision and smallest command choice | Implementation boundary / source evidence | +|---|---|---|---| +| ops-C06 | IMPLEMENTATION_GAP for live desired-profile read; existing live status COMPLETE | Keep `ocx claude desktop show --json` explicitly local. Add **`ocx claude desktop profile show --json`**, a fixed GET `/api/claude-desktop` on the existing local management runtime. Invoke it on the hub host to inspect the Hub profile. Existing `desktop status --json` remains the applied-health read. | GUI receives sharedBase at `gui/src/App.tsx:584`, GET at `gui/src/pages/ClaudeDesktop.tsx:270`. CLI show builds disk state at `src/cli/claude-desktop.ts:790`; status uses live route at `:751`. `src/cli/runtime-api.ts:71` rejects a client-role listener and does not automatically authenticate to a Hub. Do not introduce `--url`, remote admin-token storage, or automatic relay login. | +| ops-C07, ops-C12 | IMPLEMENTATION_GAP for live save/import; existing local editing retained | Add **`ocx claude desktop profile import FILE --json`**, validates one bounded DesktopProfile and sends `{profile}` to PUT `/api/claude-desktop`. It saves only; use existing `ocx claude desktop apply --gateway` or `--first-party` separately. Existing local `move`, `default`, `import`, `export` retain current meaning. A file-based profile operation is a domain operation, not generic config editing. | `src/cli/claude-desktop.ts:203` writes only persisted local profile under connection/CAS guards; it does not adopt live server config. GUI PUT `src/server/management/agent-settings-routes.ts:907` validates routes, protects applied markers, performs conflict check and explicitly adopts live config at `:959`. Existing connected import --apply is refused at `src/cli/claude-desktop.ts:736`. New handler: small `src/cli/claude-desktop-profile.ts`, dispatch through existing `src/cli/claude-desktop.ts:676`; do not grow a second parser or change auth. | +| ops-S05 | COMPLETE for actual GUI task; discovery/output clarification only | **`ocx status --json`** already exposes `connection.selectedClients`; **`ocx sync`** on connected machine already runs the same domain sync. No `machine clients` command or journal inspection task is needed to match this GUI. | `gui/src/pages/Integrations.tsx:64` consumes only selectedClients; renders at `:137`. Although API also returns journalOwner/shim, that page does not display them. CLI projection `src/cli/status.ts:918` includes selectedClients. GUI machine API calls deps.sync (`src/client/machine-api.ts:94`), whose default is syncConnectedClient at `:30`; connected CLI uses syncConnectedClient at `src/cli/dispatch.ts:450`. No new remote transport. | +| ops-O08 | Existing client-self + Hub-total COMPLETE in their authorized execution contexts; IMPLEMENTATION_GAP for administrator selecting one key on Hub | Keep **`ocx usage ... --json`** on a connected machine as own-key data-plane usage, and the same command on the Hub host as whole-Hub management usage. Add only **`--api-key-id ID`** to `ocx usage` for the existing non-client management branch. Reject this option on connected client before reading secret/transport; do not forward it to `/v1/usage`. | GUI machine button adds apiKeyId; Hub button omits it (`gui/src/pages/Usage.tsx:1113`, `:1275`). Management API accepts filter (`src/server/management/logs-usage-routes.ts:243`). CLI branch `src/cli/observe.ts:188` uses stored enrolled data key + `/v1/usage` for client; local Hub uses `/api/usage` at `:209`. `/v1/usage` authenticates exact configured key, projects `scope:'client'`, rejects caller-selected identity and strips accounts (`src/server/hub-usage.ts:15`, `:25`; `src/remote/hub-usage.ts:8`). This intentionally does NOT promise remote Hub-total access using a client data key. | +| ops-I14 | IMPLEMENTATION_GAP for narrow invocation, not missing backend/helper | Add **`ocx integration client sync --client aside --json`**. No profile selector: GUI refreshes server-selected enabled profiles. Call existing **refreshAsideProfilesThroughServer**, preserving its direct-local attestation capability. Emit per-profile outcomes, and nonzero on any unsuccessful outcome; do not turn partial 207 into blanket success. | GUI uses exact POST `/api/client-integrations/aside/sync {}` at `gui/src/pages/integrations/aside-profile-api.ts:38`; owner `src/server/management/aside-profile-routes.ts:308`. Helper already exists in `src/cli/aside-profiles.ts:9`; capability transport at `:24`, `:50`; controlled baseUrl seam at `:69`. Current `ocx sync` first syncs Codex and other file clients, then calls helper at `src/cli/dispatch.ts:526`, so it is an effect superset with warning-only failure handling, not the narrow task. Add branch in `src/cli/integrations.ts:222` with lazy helper import; no new backend owner. | +| ops-O06 | Snapshot read COMPLETE; follow amendment semantics IMPLEMENTATION_GAP | Keep command **`ocx logs --follow --jsonl`**. Make this existing follow workflow cursor/reset aware and surface changed already-seen rows; don't invent a history endpoint. Minimal compatible JSONL choice: re-emit a row when its canonical payload changes, with same id; document consumers should upsert. Preserve duplicate IDs when server explicitly returns them, resets and removals; see exact output decision below. | CLI `src/cli/observe.ts:100` never sends cursor and suppresses any seen ID at `:108`. Server content hash deliberately sends reset for same-ID content changes (`src/server/request-log-cursor.ts:50`, `:57`, `:77`); GUI replaces snapshot on reset (`gui/src/pages/Logs.tsx:621`) and merges only valid deltas. Probe below proves the mismatch with actual cursor/parser modules and the exact source-equivalent dedupe predicate. | +| ops-D01 | IMPLEMENTATION_GAP for authenticated runtime-health projection | Add **`ocx system health --json`**, fixed GET `/api/system/health` via existing runtimeRequest, run on the serving runtime's host. Keep existing `ocx health` liveness contract and `system status` aggregate unchanged. | Endpoint returns status/service/version/uptime/pid/spendLedger at `src/server/management/system-routes.ts:66`. `ocx health --json` emits only ok/pid/port (`src/cli/dispatch.ts:787`); `status --json` emits health ok/url/message plus other machine state (`src/cli/status.ts:873`), not this projection; `system status` fetches only settings/startup/memory (`src/cli/system-command.ts:35`). New leaf in `src/cli/system-command.ts:193`; no new API or remote credential scheme. | + +The Desktop additions are deliberately scoped to the currently serving **local management runtime**, which may be a Hub. Running them on a connected client should preserve the existing client-role refusal and guidance to run on the Hub. The connected browser's independently authorized shared dashboard session is not a CLI credential. Existing connected Desktop **apply** intentionally downloads the Hub's resolved Desktop model snapshot and writes the connected machine's gateway config (`src/cli/claude-desktop.ts:238`, `:260`); that is a separate target from editing the Hub's desired profile. + +The GUI profile import stages a draft only (`gui/src/pages/ClaudeDesktop.tsx:480`) whereas a terminal import command naturally commits when explicitly invoked. This does not require implementing a persistent CLI draft editor: file inspection followed by explicit `profile import` and separate `apply` preserves the meaningful save/apply boundary. Reuse `parseDesktopProfile`; retain server's unavailable-model validation and trusted applied-marker handling. No automatic mode switch or apply is added to import. + +## Exact follow-output choice + +`ops-O06` is a workflow gap, not merely inefficient repeated polling. With a stable request id, CLI seen-ID dedupe loses later status/token/pricing updates even though `/api/logs` exposes them. + +Smallest bounded change is to extract cursor/snapshot state into **`src/cli/log-follow.ts`** and call it from **`src/cli/observe.ts`**. It should: + +1. Parse cursor/reset envelopes defensively. Legacy array/snapshot responses remain accepted and never manufacture a cursor. +2. Replace the local bounded window on reset; append suffixes otherwise. The server explicitly permits repeated IDs, so do not use an id-only set to represent the window. +3. Existing text/row-JSONL follow can re-emit changed rows; an explicit removed/reset event would be a different output contract. If deletion/reset visibility is required for exact machine reconstruction, add **`--events`** to follow and emit `{type:'snapshot',rows,cursor}`, `{type:'append',rows,cursor}` rather than silently breaking the existing row JSONL stream. This is the smallest additive exact-state option. No `--api-path` or new backend route. +4. Keep `--json` one-shot; `--follow --jsonl` streaming. Bound window/cursor sizes, handle interruption, and never claim an accepted stale snapshot is new data. + +Recommended roadmap split: fix row amendments and cursor transport in existing follow; add `--events` only in the same reviewed output-contract unit if exact reset/removal projection is an acceptance requirement. GUI-to-CLI read parity must at least stop losing amendments. Full event schema is main's shared output-contract decision, not a new unresolved target/auth question. + +## Duplicate settings tasks: use existing IDs + +These are aliases for existing owner tasks, **not additional UNKNOWN roadmap rows**. + +| Operations row | Canonical settings IDs | Why | +|---|---|---| +| ops-D18 multi-agent mode/thread/native behavior and advisory | **set-M13**, **set-S04** | Existing v2 root and advisory/local-live join belong to these rows; do not introduce another agent-mode taxonomy. | +| ops-D18 injection model/effort/guidance/default synchronization | **set-S03** | Same injection/default contract. Featured roster and fallback references, where rendered, map to **set-S01**, **set-S02**. Effort-cap metadata should be reconciled with this settings-owned unit, not counted again. | +| ops-O15 account/quota/active-selection reads in Tray | **set-A01**, **set-P09**, **set-P12**, **set-P14** | Codex readiness/quota plus generic OAuth/API-key roster. Usage/companion reads stay ops-O08/O10/O13. | +| ops-O16 Tray switches | **set-A04** (Codex), **set-P12** (OAuth), **set-P14** (provider API-key pool) | `gui/src/pages/tray-data.ts:36` selects these exact three route families. No tray-specific command. | + +Canonical titles/IDs verified from `.tmp/cli-parity/gaps-settings.md`. These references are stable task identities; that artifact may continue gaining source evidence while main assembles the roadmap. + +**Coverage expansion requiring main allocation, not a duplicate UNKNOWN:** the first inventory's D18 footnote also mentioned `MemoryModelsPanel` and `CompactionRoutingPanel`, but neither has a matching dedicated `set-*` row in the current 67-row settings join. They write `memoryModels` and `compactionRouting` through PUT `/api/settings` (`gui/src/components/MemoryModelsPanel.tsx:111`, `gui/src/components/CompactionRoutingPanel.tsx:180`); they are not injection or v2 fields and must not be falsely mapped to set-S03/M13. Parent should allocate these two field groups to its runtime/model settings unit before calling all dashboard fields closed. Exact values: memoryModels `{extract?:{...phase},consolidation?:{...phase}}|null`; compactionRouting `{model,reasoningEffort?,triggers?,sourceModels?}|null`. Existing system settings parser only handles autostart/stream-mode/desktop-authless/client-compaction (`src/cli/system-command.ts:123`), so generic `config set` would not establish live PUT parity. This pass does not create new set IDs or edit sibling artifacts. + +## Accepted C4 data-plane unit + +Parent explicitly accepted terminal model test, transcription and connection-only voice probe as tasks. **ops-K08/K12/K13 are included IMPLEMENTATION_GAPs**, not pending product scope. Browser microphone capture, playback widgets, copy buttons and waveform UI remain excluded. Authorization authority is unchanged. + +| ID | Smallest command | Code/contract and proof requirements | +|---|---|---| +| ops-K08 | **`ocx access test MODEL --protocol responses\|chat\|messages --api-key-stdin --json`** | Keep existing access test grammar, add explicit chosen-key input. Current `src/cli/access.ts:255` uses runtimeRequest, whose headers start with management auth (`src/cli/runtime-api.ts:142`); that is not GUI newly-created-key proof. Use bounded `readSecretBytes` (`src/cli/runtime-api.ts:398`) with a 4096-byte key limit, strict UTF-8/single-line decoding, explicit TTY refusal, input deadline and returned-buffer cleanup and a dedicated fixed data-plane transport that sets only the chosen `x-opencodex-api-key`, never management bearer/header defaults. Existing no-flag test must not newly claim selected-key validation. | +| ops-K12 | **`ocx access audio transcribe FILE --model ID --api-key-stdin --json`** | Multipart file/model/response_format=json to fixed `/v1/audio/transcriptions`, bounded file size/deadline/body, abort on signal. GUI contract at `gui/src/audio-api-client.ts:13`; actual admitted route `src/server/index/serve-options.ts:1594`. Output transcript only by explicit task result, not incidental debug logging; no background retries that spend usage again. | +| ops-K13 | **`ocx access audio live-check --model ID --api-key-stdin --json`** | Fixed WebSocket `/v1/live?model=...`, same data-key subprotocol scheme and session.update/client-delegation settings as GUI (`gui/src/audio-api-client.ts:71`, `:88`). Wait for server session id/status, then session.close; bounded readiness/session lifetime. No microphone, audio uploads, delegation execution or auto-reconnect. Return connection outcome, not a claim of usable voice roundtrip. | + +Recommended file boundaries: **`src/cli/access-data-plane.ts`** for fixed allowlisted HTTP test/transcription origin+headers+bounded response helpers, **`src/cli/access-audio.ts`** for the two audio verbs and WebSocket lifecycle; wire through **`src/cli/access.ts:274`**. These are domain-specific internal helpers, not an operator-visible arbitrary-URL request command. Reuse existing stdin/timeout/error primitives where safe; never call runtimeRequest for the chosen-key wire request. + +Target resolution reuses current topology, not a new credential store: standalone/Hub use the existing local serving origin; connected client uses its already-enrolled normalized serverUrl, analogous to `fetchHubUsage` (`src/client/hub-client.ts:484`). The explicit key is still provided for this task through stdin; never implicitly substitute the enrolled key or admin token. Revalidate connection identity around an async read on connected clients and retain HTTPS-or-loopback, redirect refusal, safe headers, and origin policy. Do not add `--base-url`, token argv/env flags, token file caches, or a management-relay credential exchange in this unit. + +For review, require fixture evidence for wrong chosen key, missing key, all three protocol shapes, explicit refusal when the target does not enforce the chosen key, no admin-header leakage, no redirects, no stderr/stdout secret echo, bounded file/body/session sizes, timeout/interruption and correct session.close. Existing auth/scope policy remains authoritative; adding a CLI cannot make a data key a management principal. All synthetic keys must stay fixture-only. The final 070 observational guard supersedes any unconditional chosen-key-enforcement assumption in this source proposal. + +## Focused proof and proposed test placement + +Executed, using only mocked/pure inputs: + +- From repository root: `bun test gui/tests/log-poll.test.ts tests/clients/client-hub-usage.test.ts` — Bun selected **only the client file**, 16 pass / 0 fail / 40 assertions. Do not claim this invocation ran GUI tests. +- From `gui/`: `bun test tests/log-poll.test.ts` — 4 pass / 0 fail / 22 assertions. Covers legacy snapshots, reset metadata, malformed cursor rejection and repeated-ID preservation. +- A fileless `bun -e` probe imported actual `src/server/request-log-cursor.ts` and `gui/src/pages/log-poll.ts`, fed two snapshots `{id:'fixture-same-id',status:200,totalTokens:1}` then tokens=9, and applied the exact source-equivalent CLI `seen.has(String(row.id))` predicate. Output: **`{"serverReset":true,"guiAmendedTokens":9,"cliDedupeEmitted":0}`**. This is proof of cursor/parser behavior plus a source-level CLI predicate reproduction, **not execution of the whole CLI follow loop**. + +Inspected only, not executed: + +- `tests/claude-integration/claude-desktop-cli.test.ts:402` asserts connected show/export/move/default remain local and warn; `:280` asserts connected import --apply refuses before writing; `:501` asserts apply delegates profile to live owner. +- `tests/clients/aside-profile-sync-owner.test.ts:211` covers stale CLI refreshing only live-server-enabled Aside profiles; existing helper is the owner to reuse. +- `tests/server/hub-usage.test.ts:66` rejects absent/environment/admin keys for data-plane usage; `:76` rejects caller-selected identity; `:85` retains authenticated scope with bounds/filters. Executed client-side mocks additionally verify dedicated data key/no Authorization and dropping management-only DTO fields. + +New focused files proposed (no edits made): + +| Change | Proposed focused test file | Essential assertions | +|---|---|---| +| Desktop live profile show/import | `tests/cli/cli-claude-desktop-profile.test.ts` | Fixed GET/PUT shape, bounded parse-before-write, missing/invalid file zero write, 409 does not rebase, live adoption result, client-role refusal, existing local verbs untouched. | +| Runtime health read | `tests/cli/cli-system-health.test.ts` | Current runtime GET, version/uptime/pid/spendLedger preserved, remote/client refusal, no invented liveness success. | +| Usage admin filter | `tests/cli/cli-usage-scope.test.ts` | Hub query includes apiKeyId only when explicit; connected rejection before secret/transport; default connected self read unchanged; invalid connection fails closed. | +| Narrow Aside sync | `tests/cli/cli-aside-sync.test.ts` | Calls existing helper only, no Codex/other-client sync, no profile flag, partial outcomes nonzero, attestation refusal no fallback. Keep stale-owner test as server proof. | +| Log amendments/events | `tests/cli/cli-log-follow.test.ts` | Changed same ID, repeated IDs, valid suffix, reset/removal, legacy snapshots, malformed cursor, bounded state, interruption. GUI parser tests are not CLI coverage. | +| Data-plane chosen key/audio | `tests/cli/cli-access-data-plane.test.ts`, `tests/cli/cli-access-audio.test.ts` | Injected HTTP/WebSocket and fake stdin only; auth/secret/redirect/failure/cancel matrix above. No provider traffic. | + +Any new test files must be registered in both repository layout maps under the existing AGENTS rule. Prefer these siblings over extending already-large legacy parity/desktop files. Exact-head integration/test execution and final receipt design remain main-owned. + +Static receipt: 47 explicit source file/line anchors resolve; all 10 referenced set-* task IDs exist in the current settings join. Proposed new implementation/test paths are explicitly proposals and were not created. diff --git a/devlog/_fin/261003_cli_gui_parity/007_architecture_decisions.md b/devlog/_fin/261003_cli_gui_parity/007_architecture_decisions.md new file mode 100644 index 00000000000..978685d754b --- /dev/null +++ b/devlog/_fin/261003_cli_gui_parity/007_architecture_decisions.md @@ -0,0 +1,42 @@ +# Architecture decisions + +The reader is a maintainer reviewing named terminal workflows. Existing domain handlers and the existing management client remain the execution authority; the capability index becomes an accurate, scalable discovery layer. + +| Decision | Main disposition | Consequence | +| --- | --- | --- | +| CP-ARCH-01 | Accept fixed named metadata with handwritten domain handlers | No arbitrary method/URL API client, no generic config fallback counted as parity. | +| CP-ARCH-02 | Amend: reuse Capability, without NamedCommandMeta/DomainCommand interfaces | Split type/data files only for size and pure-import boundaries. Handler signatures remain compatible. | +| CP-ARCH-03 | Accept existing server validation/persistence/ownership | CLI parses grammar and bounded input; no second persistence engine or live-to-local fallback. | +| CP-ARCH-04 | Accept additive optional Capability.usage | Exact operand grammar is available for verified leaves; old metadata output stays compatible when absent. | +| CP-UX-01/02 | Accept fixed grammar, explicit JSON sources and scoped confirmation | Ordinary reversible changes do not gain gratuitous confirmation. Destructive actions preserve --yes and identity checks. | +| CP-UX-03 | Amend: preserve legacy stderr prose and exit codes | No global JSON error-envelope migration. New diagnostics are safe and retain reason/hint/status. Partial receipts remain observable. | +| CP-UX-04 | Scope observational claims | Help remains offline/write-free. Do not promise all legacy reads bypass shim preflight; new strict observational paths need their own proof. | +| CP-AUDIT-01/02 | Accept task ledger plus separate API debt | Counts are not endpoint-prefix coverage. Local/native equivalence, duplicate GUI entry points and consent exclusions are explicit. | +| CP-SIZE-01 | Accept capacity work before metadata expansion | Split generated reference into flat domain chapters; add a CLI structure owner by moving existing contracts, never raising line caps. | +| CP-TEST-01 | Accept handler-wire, real route, CLI and negative evidence | No live user state/upstream traffic. Source declarations are not runtime proof. | +| CP-DATA-01/02 | Accept bounded credentialless malformed-body observation before a chosen-key model probe | Fail unavailable on unproved enforcement, preserve existing server policy, and state the cross-request identity/policy limits; exact mechanism and real-route tests are in 070. | +| CP-DELIVERY-01 | Accept dependency slices with sequential branch ownership | Main alone switches branches; implementation leaves have disjoint file scopes. Six manual PR layers, each with its own CI. | + +## Local and live are explicit targets + +Preserve offline/local commands. Add --live to provider add/remove/set-default, custom models add/remove, v2 and logout where the source comparison proves different retained runtime state or receipts. Decide the target before touching local config. A multi-request workflow resolves its management base once and reuses it. A refusal never falls back to local writes. --json is output selection, never target selection. + +For Desktop, use the existing namespace with profile show/import for the live desired profile; existing show/move/default/import remain local and apply remains separate. Management commands run on the serving runtime host, including a Hub; a connected-client management refusal remains a refusal. No remote admin credential scheme is introduced. + +## Capability usage field chain + +Creation: optional usage literals in pure capability domain files; type in src/cli/capability-types.ts. Serialization: capabilities-command.ts uses an explicit field projection; it must add usage conditionally while retaining the exact legacy shape for declarations without it. A real runCapabilities --json fixture verifies the field is not silently dropped. Deserialization: N/A, no persisted/external capability ingestion is introduced. Consumers: declared help renderer and generated reference use usage beneath canonical headings; command matching/recovery and capabilityInvocation remain based on command tokens. Tests cover absent-field compatibility, exact grammar, alias resolution and pure import closure. + +## Input and result boundaries + +New JSON file/stdin input is explicit, bounded to the management JSON contract (4 MiB), rejects interactive waiting, invalid UTF-8/JSON and wrong top-level shapes, and never echoes payloads. Domain files validate keys/shapes and reuse pure existing validators where appropriate. Composite requests are checked against the whole serialized body limit. Never read stdin twice in one invocation. + +Server errors retain their normal exit classification. A new safe nested-error projection in runtime-api preserves an existing nested code/message without dumping the raw body. Domain writers distinguish saved, applied, deferred, conflict and unknown completion; no blind write retry. Human text uses terminal escaping. JSON success remains the safe existing DTO, not a universal new envelope. + +## Explicit scope for data-plane tools + +The API Keys page's selected-key test, audio transcription and connection-only live probe are included as terminal tasks. They use a dedicated fixed data-plane transport with an explicit stdin key, never management auth or arbitrary URLs. Browser microphone capture/playback and waveform controls are presentation exclusions. No secret-returning creation/pairing operation is executed by the development agents. + +## Consultation provenance + +Architect: Epicurus, returned handle 01a1021d-2d94-7d20-95c1-a18da31958a8. Proposal and decision amendment are retained in task scratch. The architect accepted optional usage with exact legacy fallbacks and accepted retaining existing v2 roots after main corrected an inventory false-negative. Whole-plan reflection by the same architect is ALIGNED for SHA256 a095a4e514d6d8e5a37dbf05a66b2c9156ebaeeb28a8e0fb878bc76f931c27b1: all 178 IDs, unit assignments and acyclic aliases were checked, with no essential mismatch. This is separate from the earlier concept agreement. Reflection artifact is retained in task scratch; its decision mapping covers every accepted CP decision. Independent A remains a separate gate. diff --git a/devlog/_fin/261003_cli_gui_parity/008_task_ledger.json b/devlog/_fin/261003_cli_gui_parity/008_task_ledger.json new file mode 100644 index 00000000000..916b6f76458 --- /dev/null +++ b/devlog/_fin/261003_cli_gui_parity/008_task_ledger.json @@ -0,0 +1,3119 @@ +{ + "baseline": "9f89b7265b754eb681215ad327fc9459af37b9e1", + "candidateRows": 178, + "scopeNote": "176 initial rows plus two confirmed embedded settings groups; aliases are retained and excluded from unique eligible denominator, not silently dropped.", + "rows": [ + { + "id": "set-P01", + "domain": "settings", + "baseline": "DISCOVERY_ONLY", + "status": "complete", + "unit": "wp2", + "evidence": [ + "022_discovery_verification.md", + "src/cli/capabilities.ts", + "skills/ocx/references/01_management_surface.md" + ], + "task": "set-P01 — Inspect configured providers, presets, authentication availability", + "existingCli": "provider list/show/presets; inspect config; account list/current; status --json", + "sourceInventory": "004_settings_coverage.md", + "final": {"disposition":"verified","phase":"wp2","pr":"https://github.com/lidge-jun/opencodex/pull/6526","workflows":[{"command":"ocx provider list","invocation":"ocx provider list [--json|--jsonl]","target":"local","authority":"scoped-automation","help":"ocx provider list --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P01","wp2-final-suite"],"routes":[],"limitations":["Local config and registry read, not GET /api/providers. --json and --jsonl are mutually exclusive."]},{"command":"ocx provider show","invocation":"ocx provider show [--json]","target":"local","authority":"scoped-automation","help":"ocx provider show --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P01","wp2-final-suite"],"routes":[],"limitations":["Reads local config; masks API-key and key-pool fields. This is not a live provider read or a general secret-export interface."]},{"command":"ocx provider presets","invocation":"ocx provider presets [--json]","target":"live-management","authority":"scoped-automation","help":"ocx provider presets --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P01","wp2-final-suite"],"routes":[{"method":"GET","path":"/api/provider-presets"}],"limitations":[]},{"command":"ocx inspect config","invocation":"ocx inspect config [--json]","target":"live-management","authority":"scoped-automation","help":"ocx inspect config --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P01","wp2-final-suite"],"routes":[{"method":"GET","path":"/api/config"}],"limitations":[]},{"command":"ocx account list","invocation":"ocx account list [provider] [--all] [--quota [--refresh]] [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account list --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P01","wp2-final-suite"],"routes":[{"method":"GET","path":"/api/oauth/providers","condition":"only list without a provider; discover admitted OAuth families"},{"method":"GET","path":"/api/codex-auth/accounts","query":"refresh=1 only with --quota --refresh","condition":"only Codex account family"},{"method":"GET","path":"/api/codex-auth/active","condition":"only Codex account family"},{"method":"GET","path":"/api/oauth/accounts","query":"provider=; quota=1 only with --quota; refresh=1 only with both quota and refresh","condition":"only OAuth account family"},{"method":"GET","path":"/api/providers/keys","query":"name=; quota=1 only with --quota; refresh=1 only with both quota and refresh","condition":"only API-key account family"}],"limitations":["Omitting provider discovers OAuth providers and includes configured families. JSON is {accounts,notes}, not a raw server account array.","Default listing is observational. --quota can probe provider quotas; --refresh is only acted on together with --quota. A refresh observation is not pending-account validation consent."]},{"command":"ocx account current","invocation":"ocx account current [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account current --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P01","wp2-final-suite"],"routes":[{"method":"GET","path":"/api/codex-auth/accounts","query":"","condition":"only Codex account family"},{"method":"GET","path":"/api/codex-auth/active","condition":"only Codex account family","query":""},{"method":"GET","path":"/api/oauth/accounts","query":"provider=","condition":"only OAuth account family"},{"method":"GET","path":"/api/providers/keys","query":"name=","condition":"only API-key account family"}],"limitations":["Uses the live route for the configured account family; this does not report the physical native-main profile."]},{"command":"ocx status","invocation":"ocx status --json","target":"local","authority":"scoped-automation","help":"ocx status --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P01","wp2-final-suite"],"routes":[{"method":"GET","path":"/healthz","condition":"local proxy identity probe"},{"method":"GET","path":"/api/startup-health","condition":"only a verified live local proxy"},{"method":"GET","path":"/v1/hub-state","condition":"only an enrolled connected-client Hub observation"}],"limitations":["Supplementary local diagnostics; includes identity-probed /healthz, bound startup-health when a daemon is live and remote Hub state when connected. Not a GET /api/config replacement."]}],"sourceRefs":[{"path":"gui/src/pages/use-providers-fetch.ts","line":34,"snapshot":"WP8-ledger-source-snapshot","sha256":"db77ae0f2fa5d8686f68c3404ea5fa2403c62bea227cac529f04c64fdd77449e","anchor":"const res = await fetch(`${apiBase}/api/config`);","role":"gui","claim":"Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/pages/use-providers-fetch.ts","line":51,"snapshot":"WP8-ledger-source-snapshot","sha256":"db77ae0f2fa5d8686f68c3404ea5fa2403c62bea227cac529f04c64fdd77449e","anchor":"const provRes = await fetch(`${apiBase}/api/oauth/providers`);","role":"gui","claim":"Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":89,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"function handleList(args: string[]): void {","role":"cli","claim":"ocx provider list [--json|--jsonl] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":99,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"const config = loadConfig();","role":"cli","claim":"ocx provider list [--json|--jsonl] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/config.ts","line":212,"snapshot":"WP8-ledger-source-snapshot","sha256":"2540d8851dbc204cc78fd570f73818bd6a4103a55441de1225730780b5c1fd87","anchor":"export function loadConfig(): OcxConfig {","role":"common-owner","claim":"Local loadConfig is the owner; this supplemental inventory does not certify the selected live configuration.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":233,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"async function presets(argv: string[], deps: RuntimeApiDeps): Promise {","role":"cli","claim":"ocx provider presets [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":237,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"const result = await runtimeRequest<{ providers?: unknown[] } | unknown[]>(\"/api/provider-presets\", {}, deps);","role":"cli","claim":"ocx provider presets [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1991,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"return jsonResponse({ providers: deriveProviderPresets() });","role":"common-owner","claim":"GET /api/provider-presets — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/inspect.ts","line":210,"snapshot":"WP8-ledger-source-snapshot","sha256":"562bcd9a74505cae5031b3113127b04c8d7001689743b80ba58eb65866f43335","anchor":"if (sub === \"config\") await read(\"/api/config\", rest, deps);","role":"cli","claim":"ocx inspect config [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/inspect.ts","line":46,"snapshot":"WP8-ledger-source-snapshot","sha256":"562bcd9a74505cae5031b3113127b04c8d7001689743b80ba58eb65866f43335","anchor":"const result = await runtimeRequest(path, {}, deps);","role":"cli","claim":"ocx inspect config [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/config-routes.ts","line":307,"snapshot":"WP8-ledger-source-snapshot","sha256":"a8e37c1e2816f1d6082cddb3d21d9d526f395bab78ecdbc1f0073c0375f57eba","anchor":"return jsonResponse(withProviderCatalogCapabilityDTO(safeConfigDTO(config), config));","role":"common-owner","claim":"GET /api/config — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account.ts","line":179,"snapshot":"WP8-ledger-source-snapshot","sha256":"7f35085b33227f080dfe88c40824435923336d15f28a05869226c49bf5c1f0b8","anchor":"async function cmdList(rest: string[], deps: AccountDeps): Promise {","role":"cli","claim":"ocx account list [provider] [--all] [--quota [--refresh]] [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account.ts","line":229,"snapshot":"WP8-ledger-source-snapshot","sha256":"7f35085b33227f080dfe88c40824435923336d15f28a05869226c49bf5c1f0b8","anchor":"const r = await fetchRows(deps, baseUrl, t.name, t.type, wantsQuota ? { refresh: refreshQuota } : undefined);","role":"cli","claim":"ocx account list [provider] [--all] [--quota [--refresh]] [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":456,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"if (type === \"codex\") return fetchCodexRows(deps, baseUrl, Boolean(quota?.refresh), quota !== undefined);","role":"cli","claim":"ocx account list [provider] [--all] [--quota [--refresh]] [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":293,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"const accountsPath = `/api/codex-auth/accounts${refreshAction ? \"/refresh\" : forceRefresh ? \"?refresh=1\" : \"\"}`;","role":"cli","claim":"ocx account list [provider] [--all] [--quota [--refresh]] [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":379,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"const query = quota","role":"cli","claim":"ocx account list [provider] [--all] [--quota [--refresh]] [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":424,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"const query = `?name=${encodeURIComponent(name)}${quota ? `"a=1${quota.refresh ? \"&refresh=1\" : \"\"}` : \"\"}`;","role":"cli","claim":"ocx account list [provider] [--all] [--quota [--refresh]] [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":225,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"return jsonResponse({ providers: listOAuthProviders().filter(provider => canStartManagementOAuth(provider, principal)) });","role":"common-owner","claim":"GET /api/oauth/providers — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/codex/auth-api/routes.ts","line":37,"snapshot":"WP8-ledger-source-snapshot","sha256":"e1cf949c54f9c63141b5a5316d7f5cc9e14a23df45d03ac5ac0898eead022466","anchor":"return jsonResponse({ accounts: await listCodexAuthAccounts(config, forceRefresh) });","role":"common-owner","claim":"GET /api/codex-auth/accounts — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/codex/auth-api/routes.ts","line":292,"snapshot":"WP8-ledger-source-snapshot","sha256":"e1cf949c54f9c63141b5a5316d7f5cc9e14a23df45d03ac5ac0898eead022466","anchor":"if (url.pathname === \"/api/codex-auth/active\" && req.method === \"GET\") {","role":"common-owner","claim":"GET /api/codex-auth/active — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":398,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/oauth/accounts\" && req.method === \"GET\") {","role":"common-owner","claim":"GET /api/oauth/accounts — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":929,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/providers/keys\" && req.method === \"GET\") {","role":"common-owner","claim":"GET /api/providers/keys — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account.ts","line":271,"snapshot":"WP8-ledger-source-snapshot","sha256":"7f35085b33227f080dfe88c40824435923336d15f28a05869226c49bf5c1f0b8","anchor":"async function cmdCurrent(rest: string[], deps: AccountDeps): Promise {","role":"cli","claim":"ocx account current [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account.ts","line":288,"snapshot":"WP8-ledger-source-snapshot","sha256":"7f35085b33227f080dfe88c40824435923336d15f28a05869226c49bf5c1f0b8","anchor":"const r = await fetchRows(deps, baseUrl, name, c.type);","role":"cli","claim":"ocx account current [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":293,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"const accountsPath = `/api/codex-auth/accounts${refreshAction ? \"/refresh\" : forceRefresh ? \"?refresh=1\" : \"\"}`;","role":"cli","claim":"ocx account current [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":379,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"const query = quota","role":"cli","claim":"ocx account current [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":424,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"const query = `?name=${encodeURIComponent(name)}${quota ? `"a=1${quota.refresh ? \"&refresh=1\" : \"\"}` : \"\"}`;","role":"cli","claim":"ocx account current [--json] — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/status.ts","line":682,"snapshot":"WP8-ledger-source-snapshot","sha256":"422a1b231ab77cd73ba0001bc900db499a7c464b27c07a2032365f412258a146","anchor":"export async function collectStatus(options: { mainAccountPolicy?: boolean } = {}): Promise {","role":"cli","claim":"ocx status --json — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/status.ts","line":717,"snapshot":"WP8-ledger-source-snapshot","sha256":"422a1b231ab77cd73ba0001bc900db499a7c464b27c07a2032365f412258a146","anchor":"const live = await findLiveProxy({","role":"cli","claim":"ocx status --json — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/status.ts","line":253,"snapshot":"WP8-ledger-source-snapshot","sha256":"422a1b231ab77cd73ba0001bc900db499a7c464b27c07a2032365f412258a146","anchor":"const result = await fetchBoundLocalManagementRead(","role":"cli","claim":"ocx status --json — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/status.ts","line":466,"snapshot":"WP8-ledger-source-snapshot","sha256":"422a1b231ab77cd73ba0001bc900db499a7c464b27c07a2032365f412258a146","anchor":"const resolved = await resolveHubState({","role":"cli","claim":"ocx status --json — Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/config.ts","line":212,"snapshot":"WP8-ledger-source-snapshot","sha256":"2540d8851dbc204cc78fd570f73818bd6a4103a55441de1225730780b5c1fd87","anchor":"export function loadConfig(): OcxConfig {","role":"common-owner","claim":"Local diagnostic configuration owner.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/lib/local-management-capability.ts","line":19,"snapshot":"WP8-ledger-source-snapshot","sha256":"98d0e2badc2e67eb0320d201f266fb7b0edd0955896f46ac1d982b9f8ee30f0e","anchor":"startupHealth: \"/api/startup-health\",","role":"common-owner","claim":"Bound local startup-health capability path.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/client/hub-client.ts","line":534,"snapshot":"WP8-ledger-source-snapshot","sha256":"39125a7b92f9bf33200a2003ed95f246ceb3dd33c6ba59cfb4c32d1115c7d3fc","anchor":"const response = await fetchBounded(options.fetchImpl ?? fetch, `${origin}/v1/hub-state`, {","role":"common-owner","claim":"Connected status fetchHubState reads the Hub data endpoint, not local configuration.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/components/AddProviderModal.tsx","line":90,"snapshot":"WP8-ledger-source-snapshot","sha256":"058ee863fee6b8cfc3b567542bf3b1b48c56eea6d1cc7a6df2ffa6ace9937e9b","anchor":"const res = await fetch(`${apiBase}/api/provider-presets`, { signal });","role":"gui","claim":"Preset selector reads the same /api/provider-presets DTO as provider presets.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/hooks/useProviderAccountPools.ts","line":210,"snapshot":"WP8-ledger-source-snapshot","sha256":"aad9433a78ea30dc8426b227cc80b0b6bf2687688ab6b8d30c9b041f3ffa554f","anchor":"const url = `${apiBase}/api/oauth/accounts?provider=${encodeURIComponent(provider)}`;","role":"gui","claim":"Authentication roster reads OAuth account IDs/active selection.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":434,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"function handleShow(args: string[]): void {","role":"cli","claim":"handleShow parses the local provider name and --json.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":454,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"...(prov.apiKey ? { apiKey: maskSecret(prov.apiKey) } : {}),","role":"cli","claim":"handleShow masks the primary key and line445 masks each key-pool entry.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/index/serve-options.ts","line":604,"snapshot":"WP8-ledger-source-snapshot","sha256":"c19e839c8c67c43524f60cc12bc4fe71b52e7b27d66e829a754f9c7f91f33e4e","anchor":"if (url.pathname === \"/healthz\" && req.method === \"GET\") {","role":"common-owner","claim":"Local liveness /healthz owner.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/config-routes.ts","line":406,"snapshot":"WP8-ledger-source-snapshot","sha256":"a8e37c1e2816f1d6082cddb3d21d9d526f395bab78ecdbc1f0073c0375f57eba","anchor":"if (url.pathname === \"/api/startup-health\" && req.method === \"GET\") {","role":"common-owner","claim":"Bound local startup-health management read.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/index/serve-options.ts","line":823,"snapshot":"WP8-ledger-source-snapshot","sha256":"c19e839c8c67c43524f60cc12bc4fe71b52e7b27d66e829a754f9c7f91f33e4e","anchor":"const admission = resolveApiAuth(req, policy);","role":"common-owner","claim":"Connected Hub state requires data-plane admission.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"}],"sourceJoin":{"guiAction":"set-P01 — Inspect configured providers, presets, authentication availability","guiRefs":[0,1,34,35],"cliRefs":[2,3,5,6,8,9,11,12,13,14,15,16,22,23,24,25,26,27,28,29,30,36,37],"ownerRefs":[4,7,10,17,18,19,20,21,31,32,33,38,39,40],"routeContracts":[{"method":"GET","path":"/api/provider-presets"},{"method":"GET","path":"/api/config"},{"method":"GET","path":"/api/oauth/providers","condition":"only list without a provider; discover admitted OAuth families"},{"method":"GET","path":"/api/codex-auth/accounts","query":"refresh=1 only with --quota --refresh","condition":"only Codex account family"},{"method":"GET","path":"/api/codex-auth/active","condition":"only Codex account family"},{"method":"GET","path":"/api/oauth/accounts","query":"provider=; quota=1 only with --quota; refresh=1 only with both quota and refresh","condition":"only OAuth account family"},{"method":"GET","path":"/api/providers/keys","query":"name=; quota=1 only with --quota; refresh=1 only with both quota and refresh","condition":"only API-key account family"},{"method":"GET","path":"/api/codex-auth/accounts","query":"","condition":"only Codex account family"},{"method":"GET","path":"/api/codex-auth/active","condition":"only Codex account family","query":""},{"method":"GET","path":"/api/oauth/accounts","query":"provider=","condition":"only OAuth account family"},{"method":"GET","path":"/api/providers/keys","query":"name=","condition":"only API-key account family"},{"method":"GET","path":"/healthz","condition":"local proxy identity probe"},{"method":"GET","path":"/api/startup-health","condition":"only a verified live local proxy"},{"method":"GET","path":"/v1/hub-state","condition":"only an enrolled connected-client Hub observation"}],"argument":"Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations.","workflowJoins":[{"workflowIndex":0,"guiRefs":[0,1,34,35],"cliRefs":[2,3],"ownerRefs":[4],"argument":"ocx provider list [--json|--jsonl] selects local. Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations."},{"workflowIndex":1,"guiRefs":[0,1,34,35],"cliRefs":[36,37],"ownerRefs":[4],"argument":"ocx provider show [--json] selects local. Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations."},{"workflowIndex":2,"guiRefs":[0,1,34,35],"cliRefs":[5,6],"ownerRefs":[7],"argument":"ocx provider presets [--json] selects live-management. Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations."},{"workflowIndex":3,"guiRefs":[0,1,34,35],"cliRefs":[8,9],"ownerRefs":[10],"argument":"ocx inspect config [--json] selects live-management. Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations."},{"workflowIndex":4,"guiRefs":[0,1,34,35],"cliRefs":[11,12,13,14,15,16],"ownerRefs":[17,18,19,20,21],"argument":"ocx account list [provider] [--all] [--quota [--refresh]] [--json] selects live-management. Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations."},{"workflowIndex":5,"guiRefs":[0,1,34,35],"cliRefs":[22,23,24,25,26],"ownerRefs":[18,19,20,21],"argument":"ocx account current [--json] selects live-management. Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations."},{"workflowIndex":6,"guiRefs":[0,1,34,35],"cliRefs":[27,28,29,30],"ownerRefs":[31,32,33,38,39,40],"argument":"ocx status --json selects local. Providers fetchConfig reads the live safeConfigDTO; inspect config reads that same DTO. Provider list/show instead load the local provider map and registry and mask secrets in show, so they are supplementary local inventory rather than live-configuration evidence. Presets shares deriveProviderPresets. Account list/current uses fetchRows to select the Codex, OAuth or API-key roster and project active IDs/readiness; status combines local diagnostics and identity-bound runtime/Hub observations."}]},"proofIds":["equivalence-set-P01","wp2-final-suite"],"limitations":["Mixed local provider list/show and live reads; local view does not certify runtime configuration."],"acceptanceBasis":"Corrected immutable per-workflow source equivalence; retained historical proof IDs require the dedicated evidence join and parent current-head publication checks."} + }, + { + "id": "set-P02", + "domain": "settings", + "baseline": "IMPLEMENTATION_GAP", + "status": "complete", + "unit": "wp3", + "evidence": [ + "032_provider_verification.md", + "src/cli/provider-lifecycle-runtime.ts", + "src/cli/provider-settings.ts", + "src/cli/provider-batch.ts", + "tests/cli/cli-provider-sync-result.test.ts" + ], + "task": "set-P02 — Add a preset/custom provider; enable canonical OpenAI account provider", + "existingCli": "provider add P [--adapter A --base-url U --api-key ... --default-model M --allow-private-network --force --set-default --sync]; provider edit openai --enabled on", + "sourceInventory": "004_settings_coverage.md", + "final": {"disposition":"verified","phase":"wp3","pr":"https://github.com/lidge-jun/opencodex/pull/6528","workflows":[{"command":"ocx provider add","invocation":"ocx provider add [--adapter ] [--base-url ] [--responses-path ] [--auth-mode ] [--api-key ] [--api-key-transport ] [--default-model ] [--model --text-only] [--google-tool-schema-policy ] [--allow-private-network] [--set-default] [--force] [--sync] [--json]","target":"local","authority":"scoped-automation","help":"ocx provider add --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P02","wp3-final-suite"],"routes":[],"limitations":["Without --live this changes local configuration. --sync optionally discovers a local daemon and runs syncModelsToCodex; saved and applied outcomes are separate. No management provider CRUD is performed by this branch."],"condition":"omit --live"},{"command":"ocx provider add","invocation":"ocx provider add [--adapter ] [--base-url ] [--responses-path ] [--auth-mode ] [--api-key ] [--api-key-transport ] [--default-model ] [--model --text-only] [--google-tool-schema-policy ] [--allow-private-network] [--set-default] [--force] --live [--json]","target":"live-management","authority":"scoped-automation","help":"ocx provider add --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P02","wp3-final-suite"],"routes":[{"method":"GET","path":"/api/providers"},{"method":"GET","path":"/api/provider-presets"},{"method":"POST","path":"/api/providers","body":"{name,provider,setDefault?:true}"}],"limitations":["Without --live, saves local configuration; --sync reports its actual disposition and needsSync, never fabricated client application. --live cannot be combined with --sync.","Live add reads the target roster and presets. An observed existing row requires --force; POST remains upsert, so the preflight is not atomic create-only protection.","Canonical OpenAI uses the target preset unchanged; transport/auth/model overrides are refused in live add. No local registry fallback when a needed target preset is missing.","Keep real credentials out of argv and agent transcripts; this does not authorize credential capture."],"condition":"explicit --live"},{"command":"ocx provider edit","invocation":"ocx provider edit openai --enabled on [--json]","target":"live-management","authority":"scoped-automation","help":"ocx provider edit --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P02","wp3-final-suite"],"routes":[{"method":"PATCH","path":"/api/providers","query":"name=","body":"only explicitly supplied provider patch fields"}],"limitations":["At least one edit is required; only supplied fields are patched. update is an existing alias. Use provider show only for local state, not as proof of the live write.","Management errors use safe fixed provider diagnostics; catalog failures retain the saved receipt and a nonzero exit."]}],"sourceRefs":[{"path":"gui/src/components/AddProviderModal.tsx","line":203,"snapshot":"WP8-ledger-source-snapshot","sha256":"058ee863fee6b8cfc3b567542bf3b1b48c56eea6d1cc7a6df2ffa6ace9937e9b","anchor":"const res = await fetch(`${apiBase}/api/providers`, {","role":"gui","claim":"AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/pages/use-providers-crud.ts","line":92,"snapshot":"WP8-ledger-source-snapshot","sha256":"55058d624e1c69661c170fe1116d5f746a3309db8f2cc1122ae6ce1b39da02b8","anchor":"const res = await fetch(`${apiBase}/api/providers?name=${encodeURIComponent(name)}`, {","role":"gui","claim":"AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":151,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"const ADD_USAGE = \"Usage: ocx provider add [--adapter ] [--base-url ] [--responses-path ] [--auth-mode ] [--api-key ] [--api-key-transport ] [--default-model ] [--model --text-only] [--google-tool-schema-policy ] [--allow-private-network] [--set-default] [--force] [--json] [--sync | --live]\";","role":"cli","claim":"ocx provider add [--adapter ] [--base-url ] [--responses-path ] [--auth-mode ] [--api-key ] [--api-key-transport ] [--default-model ] [--model --text-only] [--google-tool-schema-policy ] [--allow-private-network] [--set-default] [--force] [--sync] [--json] — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":304,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"const { initializeProviderModelSelection } = await import(\"../providers/initial-model-selection\");","role":"cli","claim":"ocx provider add [--adapter ] [--base-url ] [--responses-path ] [--auth-mode ] [--api-key ] [--api-key-transport ] [--default-model ] [--model --text-only] [--google-tool-schema-policy ] [--allow-private-network] [--set-default] [--force] [--sync] [--json] — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":309,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"validateAndSave(config);","role":"cli","claim":"ocx provider add [--adapter ] [--base-url ] [--responses-path ] [--auth-mode ] [--api-key ] [--api-key-transport ] [--default-model ] [--model --text-only] [--google-tool-schema-policy ] [--allow-private-network] [--set-default] [--force] [--sync] [--json] — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":317,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"const result = await (deps.syncModels ?? syncModelsToCodex)(live.port, config, null);","role":"cli","claim":"ocx provider add [--adapter ] [--base-url ] [--responses-path ] [--auth-mode ] [--api-key ] [--api-key-transport ] [--default-model ] [--model --text-only] [--google-tool-schema-policy ] [--allow-private-network] [--set-default] [--force] [--sync] [--json] — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/config.ts","line":370,"snapshot":"WP8-ledger-source-snapshot","sha256":"2540d8851dbc204cc78fd570f73818bd6a4103a55441de1225730780b5c1fd87","anchor":"export function saveConfig(config: OcxConfig): void {","role":"common-owner","claim":"saveConfig persists the local provider map; catalog synchronization is a separate optional action.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":527,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"const liveArgs = subArgs.filter(arg => arg === \"--live\" || arg.startsWith(\"--live=\"));","role":"cli","claim":"ocx provider add [--adapter ] [--base-url ] [--responses-path ] [--auth-mode ] [--api-key ] [--api-key-transport ] [--default-model ] [--model --text-only] [--google-tool-schema-policy ] [--allow-private-network] [--set-default] [--force] --live [--json] — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-lifecycle-runtime.ts","line":50,"snapshot":"WP8-ledger-source-snapshot","sha256":"a3e846c284e3d218afb488107c9233183fb4db92166a8d7f98a838943ba0ad47","anchor":"function parseAdd(args: string[]) {","role":"cli","claim":"ocx provider add [--adapter ] [--base-url ] [--responses-path ] [--auth-mode ] [--api-key ] [--api-key-transport ] [--default-model ] [--model --text-only] [--google-tool-schema-policy ] [--allow-private-network] [--set-default] [--force] --live [--json] — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-lifecycle-runtime.ts","line":112,"snapshot":"WP8-ledger-source-snapshot","sha256":"a3e846c284e3d218afb488107c9233183fb4db92166a8d7f98a838943ba0ad47","anchor":"const roster = await runtimeRequest(\"/api/providers\", { redirect: \"error\" }, deps);","role":"cli","claim":"ocx provider add [--adapter ] [--base-url ] [--responses-path ] [--auth-mode ] [--api-key ] [--api-key-transport ] [--default-model ] [--model --text-only] [--google-tool-schema-policy ] [--allow-private-network] [--set-default] [--force] --live [--json] — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-lifecycle-runtime.ts","line":120,"snapshot":"WP8-ledger-source-snapshot","sha256":"a3e846c284e3d218afb488107c9233183fb4db92166a8d7f98a838943ba0ad47","anchor":"const presets = await runtimeRequest(\"/api/provider-presets\", { redirect: \"error\" }, deps);","role":"cli","claim":"ocx provider add [--adapter ] [--base-url ] [--responses-path ] [--auth-mode ] [--api-key ] [--api-key-transport ] [--default-model ] [--model --text-only] [--google-tool-schema-policy ] [--allow-private-network] [--set-default] [--force] --live [--json] — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-lifecycle-runtime.ts","line":140,"snapshot":"WP8-ledger-source-snapshot","sha256":"a3e846c284e3d218afb488107c9233183fb4db92166a8d7f98a838943ba0ad47","anchor":"const result = await runtimeRequest(\"/api/providers\", {","role":"cli","claim":"ocx provider add [--adapter ] [--base-url ] [--responses-path ] [--auth-mode ] [--api-key ] [--api-key-transport ] [--default-model ] [--model --text-only] [--google-tool-schema-policy ] [--allow-private-network] [--set-default] [--force] --live [--json] — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":934,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"return jsonResponse(Object.entries(config.providers).map(([name, p]) => ({","role":"common-owner","claim":"GET /api/providers — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1991,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"return jsonResponse({ providers: deriveProviderPresets() });","role":"common-owner","claim":"GET /api/provider-presets — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1161,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"if (url.pathname === \"/api/providers\" && req.method === \"POST\") {","role":"common-owner","claim":"POST /api/providers — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1177,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"const providerError = providerManagementConfigError(name, transportCandidate)","role":"common-owner","claim":"Shared providerManagementConfigError checks incoming provider settings before provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":66,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"const patch: Record = takeProviderEditSettings(args);","role":"cli","claim":"ocx provider edit openai --enabled on [--json] — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":135,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"if (enabled !== undefined) patch.disabled = !enabled;","role":"cli","claim":"ocx provider edit openai --enabled on [--json] — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":143,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"const result = await runtimeRequest(`/api/providers?name=${encodeURIComponent(name)}`, {","role":"cli","claim":"ocx provider edit openai --enabled on [--json] — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1412,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"if (url.pathname === \"/api/providers\" && req.method === \"PATCH\") {","role":"common-owner","claim":"PATCH /api/providers — AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":280,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"const providerError = providerManagementConfigError(name, transportCandidate, { allowOperatorOverlays: true })","role":"common-owner","claim":"applyProviderPatch validates the merged transport candidate.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1505,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":": providerManagementConfigError(","role":"common-owner","claim":"PATCH delegates full merged provider validation before save.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"}],"sourceJoin":{"guiAction":"set-P02 — Add a preset/custom provider; enable canonical OpenAI account provider","guiRefs":[0,1],"cliRefs":[2,3,4,5,7,8,9,10,11,16,17,18],"ownerRefs":[6,12,13,14,15,19,20,21],"routeContracts":[{"method":"GET","path":"/api/providers"},{"method":"GET","path":"/api/provider-presets"},{"method":"POST","path":"/api/providers","body":"{name,provider,setDefault?:true}"},{"method":"PATCH","path":"/api/providers","query":"name=","body":"only explicitly supplied provider patch fields"}],"argument":"AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation.","workflowJoins":[{"workflowIndex":0,"guiRefs":[0,1],"cliRefs":[2,3,4,5],"ownerRefs":[6],"argument":"ocx provider add [--adapter ] [--base-url ] [--responses-path ] [--auth-mode ] [--api-key ] [--api-key-transport ] [--default-model ] [--model --text-only] [--google-tool-schema-policy ] [--allow-private-network] [--set-default] [--force] [--sync] [--json] selects local. AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation."},{"workflowIndex":1,"guiRefs":[0,1],"cliRefs":[7,8,9,10,11],"ownerRefs":[12,13,14,15],"argument":"ocx provider add [--adapter ] [--base-url ] [--responses-path ] [--auth-mode ] [--api-key ] [--api-key-transport ] [--default-model ] [--model --text-only] [--google-tool-schema-policy ] [--allow-private-network] [--set-default] [--force] --live [--json] selects live-management. AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation."},{"workflowIndex":2,"guiRefs":[0,1],"cliRefs":[16,17,18],"ownerRefs":[19,20,21],"argument":"ocx provider edit openai --enabled on [--json] selects live-management. AddProviderModal posts {name,provider,setDefault}; explicit provider add --live builds the target preset/custom seed after reading that target roster and presets, validates it, and posts the same provider body. Canonical openai uses the target seed verbatim; provider edit openai --enabled on patches disabled:false. Ordinary add instead derives a local seed, initializes model selection and saves config; --sync optionally invokes syncModelsToCodex and reports its distinct result, not POST provider creation."}]},"proofIds":["equivalence-set-P02","wp3-final-suite"],"limitations":["For runtime parity use add --live; default remains local. Enable canonical openai using edit --enabled on."],"acceptanceBasis":"Corrected immutable per-workflow source equivalence; retained historical proof IDs require the dedicated evidence join and parent current-head publication checks."} + }, + { + "id": "set-P03", + "domain": "settings", + "baseline": "IMPLEMENTATION_GAP", + "status": "complete", + "unit": "wp3", + "evidence": [ + "032_provider_verification.md", + "src/cli/provider-lifecycle-runtime.ts", + "src/cli/provider-settings.ts", + "src/cli/provider-batch.ts", + "tests/cli/cli-provider-sync-result.test.ts" + ], + "task": "set-P03 — Edit provider transport, discovery, default model, note, network permission", + "existingCli": "provider edit/update P --adapter A --base-url U --default-model M\\|- --auth-mode MODE\\|- --note TEXT\\|- --api-key-transport MODE\\|- --enabled on\\|off --live-models on\\|off --allow-private-network on\\|off", + "sourceInventory": "004_settings_coverage.md", + "final": {"disposition":"verified","phase":"wp3","pr":"https://github.com/lidge-jun/opencodex/pull/6528","workflows":[{"command":"ocx provider edit","invocation":"ocx provider edit [--adapter ] [--base-url ] [--default-model ] [--auth-mode ] [--note ] [--api-key-transport ] [--headers ] [--enabled ] [--live-models ] [--retain-models ] [--model --text-only] [--xai-chat ] [--allow-private-network ] [--model-context-tier ] [--upstream-http-version ] [--fast ] [--context-window ] [--json]","target":"live-management","authority":"scoped-automation","help":"ocx provider edit --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P03","wp3-final-suite"],"routes":[{"method":"PATCH","path":"/api/providers","query":"name=","body":"only explicitly supplied provider patch fields"}],"limitations":["At least one edit is required; only supplied fields are patched. update is an existing alias. Use provider show only for local state, not as proof of the live write.","Management errors use safe fixed provider diagnostics; catalog failures retain the saved receipt and a nonzero exit."]}],"sourceRefs":[{"path":"gui/src/components/provider-workspace/ProviderSettings.tsx","line":250,"snapshot":"WP8-ledger-source-snapshot","sha256":"a8edade4ec7ff1b3c5860e10a31ef5ba6048e36949a2db3d6a15b0b3734b94fd","anchor":"const patch: ProviderUpdatePatch = pacingOnly","role":"gui","claim":"ProviderSettings.save constructs adapter/baseUrl/defaultModel/authMode/note/allowPrivateNetwork and conditionally liveModels/upstreamHttpVersion. provider-runtime.edit parses the corresponding named flags, maps --enabled to disabled and clear sentinels to the documented empty/null forms, then PATCHes the same named provider. applyProviderPatch and providerManagementConfigError validate the merged server configuration before persistence; the CLI receipt preserves catalog-pending outcomes.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/pages/use-providers-crud.ts","line":112,"snapshot":"WP8-ledger-source-snapshot","sha256":"55058d624e1c69661c170fe1116d5f746a3309db8f2cc1122ae6ce1b39da02b8","anchor":"const res = await fetch(`${apiBase}/api/providers?name=${encodeURIComponent(name)}`, {","role":"gui","claim":"ProviderSettings.save constructs adapter/baseUrl/defaultModel/authMode/note/allowPrivateNetwork and conditionally liveModels/upstreamHttpVersion. provider-runtime.edit parses the corresponding named flags, maps --enabled to disabled and clear sentinels to the documented empty/null forms, then PATCHes the same named provider. applyProviderPatch and providerManagementConfigError validate the merged server configuration before persistence; the CLI receipt preserves catalog-pending outcomes.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":66,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"const patch: Record = takeProviderEditSettings(args);","role":"cli","claim":"ocx provider edit [--adapter ] [--base-url ] [--default-model ] [--auth-mode ] [--note ] [--api-key-transport ] [--headers ] [--enabled ] [--live-models ] [--retain-models ] [--model --text-only] [--xai-chat ] [--allow-private-network ] [--model-context-tier ] [--upstream-http-version ] [--fast ] [--context-window ] [--json] — ProviderSettings.save constructs adapter/baseUrl/defaultModel/authMode/note/allowPrivateNetwork and conditionally liveModels/upstreamHttpVersion. provider-runtime.edit parses the corresponding named flags, maps --enabled to disabled and clear sentinels to the documented empty/null forms, then PATCHes the same named provider. applyProviderPatch and providerManagementConfigError validate the merged server configuration before persistence; the CLI receipt preserves catalog-pending outcomes.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":135,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"if (enabled !== undefined) patch.disabled = !enabled;","role":"cli","claim":"ocx provider edit [--adapter ] [--base-url ] [--default-model ] [--auth-mode ] [--note ] [--api-key-transport ] [--headers ] [--enabled ] [--live-models ] [--retain-models ] [--model --text-only] [--xai-chat ] [--allow-private-network ] [--model-context-tier ] [--upstream-http-version ] [--fast ] [--context-window ] [--json] — ProviderSettings.save constructs adapter/baseUrl/defaultModel/authMode/note/allowPrivateNetwork and conditionally liveModels/upstreamHttpVersion. provider-runtime.edit parses the corresponding named flags, maps --enabled to disabled and clear sentinels to the documented empty/null forms, then PATCHes the same named provider. applyProviderPatch and providerManagementConfigError validate the merged server configuration before persistence; the CLI receipt preserves catalog-pending outcomes.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":143,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"const result = await runtimeRequest(`/api/providers?name=${encodeURIComponent(name)}`, {","role":"cli","claim":"ocx provider edit [--adapter ] [--base-url ] [--default-model ] [--auth-mode ] [--note ] [--api-key-transport ] [--headers ] [--enabled ] [--live-models ] [--retain-models ] [--model --text-only] [--xai-chat ] [--allow-private-network ] [--model-context-tier ] [--upstream-http-version ] [--fast ] [--context-window ] [--json] — ProviderSettings.save constructs adapter/baseUrl/defaultModel/authMode/note/allowPrivateNetwork and conditionally liveModels/upstreamHttpVersion. provider-runtime.edit parses the corresponding named flags, maps --enabled to disabled and clear sentinels to the documented empty/null forms, then PATCHes the same named provider. applyProviderPatch and providerManagementConfigError validate the merged server configuration before persistence; the CLI receipt preserves catalog-pending outcomes.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1412,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"if (url.pathname === \"/api/providers\" && req.method === \"PATCH\") {","role":"common-owner","claim":"PATCH /api/providers — ProviderSettings.save constructs adapter/baseUrl/defaultModel/authMode/note/allowPrivateNetwork and conditionally liveModels/upstreamHttpVersion. provider-runtime.edit parses the corresponding named flags, maps --enabled to disabled and clear sentinels to the documented empty/null forms, then PATCHes the same named provider. applyProviderPatch and providerManagementConfigError validate the merged server configuration before persistence; the CLI receipt preserves catalog-pending outcomes.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":280,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"const providerError = providerManagementConfigError(name, transportCandidate, { allowOperatorOverlays: true })","role":"common-owner","claim":"applyProviderPatch validates the merged transport candidate.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1505,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":": providerManagementConfigError(","role":"common-owner","claim":"PATCH delegates full merged provider validation before save.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"}],"sourceJoin":{"guiAction":"set-P03 — Edit provider transport, discovery, default model, note, network permission","guiRefs":[0,1],"cliRefs":[2,3,4],"ownerRefs":[5,6,7],"routeContracts":[{"method":"PATCH","path":"/api/providers","query":"name=","body":"only explicitly supplied provider patch fields"}],"argument":"ProviderSettings.save constructs adapter/baseUrl/defaultModel/authMode/note/allowPrivateNetwork and conditionally liveModels/upstreamHttpVersion. provider-runtime.edit parses the corresponding named flags, maps --enabled to disabled and clear sentinels to the documented empty/null forms, then PATCHes the same named provider. applyProviderPatch and providerManagementConfigError validate the merged server configuration before persistence; the CLI receipt preserves catalog-pending outcomes.","workflowJoins":[{"workflowIndex":0,"guiRefs":[0,1],"cliRefs":[2,3,4],"ownerRefs":[5,6,7],"argument":"ocx provider edit [--adapter ] [--base-url ] [--default-model ] [--auth-mode ] [--note ] [--api-key-transport ] [--headers ] [--enabled ] [--live-models ] [--retain-models ] [--model --text-only] [--xai-chat ] [--allow-private-network ] [--model-context-tier ] [--upstream-http-version ] [--fast ] [--context-window ] [--json] selects live-management. ProviderSettings.save constructs adapter/baseUrl/defaultModel/authMode/note/allowPrivateNetwork and conditionally liveModels/upstreamHttpVersion. provider-runtime.edit parses the corresponding named flags, maps --enabled to disabled and clear sentinels to the documented empty/null forms, then PATCHes the same named provider. applyProviderPatch and providerManagementConfigError validate the merged server configuration before persistence; the CLI receipt preserves catalog-pending outcomes."}]},"proofIds":["equivalence-set-P03","wp3-final-suite"],"limitations":["ProviderSettings.save constructs adapter/baseUrl/defaultModel/authMode/note/allowPrivateNetwork and conditionally liveModels/upstreamHttpVersion. provider-runtime.edit parses the corresponding named flags, maps --enabled to disabled and clear sentinels to the documented empty/null forms, then PATCHes the same named provider. applyProviderPatch and providerManagementConfigError validate the merged server configuration before persistence; the CLI receipt preserves catalog-pending outcomes."],"acceptanceBasis":"Corrected immutable per-workflow source equivalence; retained historical proof IDs require the dedicated evidence join and parent current-head publication checks."} + }, + { + "id": "set-P04", + "domain": "settings", + "baseline": "IMPLEMENTATION_GAP", + "status": "complete", + "unit": "wp3", + "evidence": [ + "032_provider_verification.md", + "src/cli/provider-lifecycle-runtime.ts", + "src/cli/provider-settings.ts", + "src/cli/provider-batch.ts", + "tests/cli/cli-provider-sync-result.test.ts" + ], + "task": "set-P04 — Configure and inspect request pacing", + "existingCli": "inspect pacing --name P --json", + "sourceInventory": "004_settings_coverage.md", + "final": {"disposition":"verified","phase":"wp3","pr":"https://github.com/lidge-jun/opencodex/pull/6528","workflows":[{"command":"ocx provider pacing","invocation":"ocx provider pacing [--json]; ocx provider pacing [--enabled ] [--rpm ] [--min-interval-ms ] [--max-concurrent ] [--json]; ocx provider pacing --file [--json]","target":"live-management","authority":"scoped-automation","help":"ocx provider pacing --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P04","wp3-final-suite"],"routes":[{"method":"GET","path":"/api/config","condition":"read mode or scalar edit; file replacement does not need this read"},{"method":"GET","path":"/api/provider-request-pacing","condition":"read mode only","query":"name="},{"method":"PATCH","path":"/api/providers","condition":"only scalar/file edit","query":"name=","body":"{requestPacing:validatedRules}"}],"limitations":["Read returns {provider,rules,status}; rules:null means no configured block, not an unavailable target.","Scalar edits preserve model rules observed from the pinned config; PATCH replaces the block and can overwrite an intervening edit. Use snapshot/apply for public-baseline CAS.","File mode validates a complete non-null rules block and writes without a prior rule read. Pacing limits reject zero."]},{"command":"ocx inspect pacing","invocation":"ocx inspect pacing [--name ] [--json]","target":"live-management","authority":"scoped-automation","help":"ocx inspect pacing --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P04","wp3-final-suite"],"routes":[{"method":"GET","path":"/api/provider-request-pacing","query":"optional name=; absent means all configured providers"}],"limitations":["An unknown provider name is a 404 rather than an empty result."]}],"sourceRefs":[{"path":"gui/src/components/provider-workspace/ProviderSettings.tsx","line":178,"snapshot":"WP8-ledger-source-snapshot","sha256":"a8edade4ec7ff1b3c5860e10a31ef5ba6048e36949a2db3d6a15b0b3734b94fd","anchor":"fetch(`${apiBase}/api/provider-request-pacing?name=${encodeURIComponent(item.name)}`, { signal: bounded.signal })","role":"gui","claim":"ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/components/provider-workspace/ProviderSettings.tsx","line":189,"snapshot":"WP8-ledger-source-snapshot","sha256":"a8edade4ec7ff1b3c5860e10a31ef5ba6048e36949a2db3d6a15b0b3734b94fd","anchor":"const pacingDraft = useMemo(() => ({","role":"gui","claim":"ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/components/provider-workspace/ProviderSettings.tsx","line":250,"snapshot":"WP8-ledger-source-snapshot","sha256":"a8edade4ec7ff1b3c5860e10a31ef5ba6048e36949a2db3d6a15b0b3734b94fd","anchor":"const patch: ProviderUpdatePatch = pacingOnly","role":"gui","claim":"ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-settings.ts","line":100,"snapshot":"WP8-ledger-source-snapshot","sha256":"40315c5b767d0118a045355fec6607dc54849fb1fde427fae11ddb7e4d7c2892","anchor":"export async function handleProviderPacingCommand(argv: string[], deps: RuntimeApiDeps = {}): Promise {","role":"cli","claim":"ocx provider pacing [--json]; ocx provider pacing [--enabled ] [--rpm ] [--min-interval-ms ] [--max-concurrent ] [--json]; ocx provider pacing --file [--json] — ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-settings.ts","line":121,"snapshot":"WP8-ledger-source-snapshot","sha256":"40315c5b767d0118a045355fec6607dc54849fb1fde427fae11ddb7e4d7c2892","anchor":"const observed = await configuredRules(name, pinned);","role":"cli","claim":"ocx provider pacing [--json]; ocx provider pacing [--enabled ] [--rpm ] [--min-interval-ms ] [--max-concurrent ] [--json]; ocx provider pacing --file [--json] — ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-settings.ts","line":127,"snapshot":"WP8-ledger-source-snapshot","sha256":"40315c5b767d0118a045355fec6607dc54849fb1fde427fae11ddb7e4d7c2892","anchor":"const result = await runtimeRequest(`/api/providers?name=${encodeURIComponent(name)}`, {","role":"cli","claim":"ocx provider pacing [--json]; ocx provider pacing [--enabled ] [--rpm ] [--min-interval-ms ] [--max-concurrent ] [--json]; ocx provider pacing --file [--json] — ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-settings.ts","line":133,"snapshot":"WP8-ledger-source-snapshot","sha256":"40315c5b767d0118a045355fec6607dc54849fb1fde427fae11ddb7e4d7c2892","anchor":"const status = pacingStatus(await runtimeRequest(`/api/provider-request-pacing?name=${encodeURIComponent(name)}`, {","role":"cli","claim":"ocx provider pacing [--json]; ocx provider pacing [--enabled ] [--rpm ] [--min-interval-ms ] [--max-concurrent ] [--json]; ocx provider pacing --file [--json] — ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/config-routes.ts","line":307,"snapshot":"WP8-ledger-source-snapshot","sha256":"a8e37c1e2816f1d6082cddb3d21d9d526f395bab78ecdbc1f0073c0375f57eba","anchor":"return jsonResponse(withProviderCatalogCapabilityDTO(safeConfigDTO(config), config));","role":"common-owner","claim":"GET /api/config — ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":923,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"return jsonResponse(providerRequestPacingStatus(name, config.providers[name]!));","role":"common-owner","claim":"GET /api/provider-request-pacing — ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1412,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"if (url.pathname === \"/api/providers\" && req.method === \"PATCH\") {","role":"common-owner","claim":"PATCH /api/providers — ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":485,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"const pacingError = requestPacingConfigError(value);","role":"common-owner","claim":"requestPacingConfigError validates replacement pacing rules.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/inspect.ts","line":162,"snapshot":"WP8-ledger-source-snapshot","sha256":"562bcd9a74505cae5031b3113127b04c8d7001689743b80ba58eb65866f43335","anchor":"async function pacing(argv: string[], deps: RuntimeApiDeps): Promise {","role":"cli","claim":"ocx inspect pacing [--name ] [--json] — ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/inspect.ts","line":168,"snapshot":"WP8-ledger-source-snapshot","sha256":"562bcd9a74505cae5031b3113127b04c8d7001689743b80ba58eb65866f43335","anchor":"const result = await runtimeRequest(`/api/provider-request-pacing${suffix}`, {}, deps);","role":"cli","claim":"ocx inspect pacing [--name ] [--json] — ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"}],"sourceJoin":{"guiAction":"set-P04 — Configure and inspect request pacing","guiRefs":[0,1,2],"cliRefs":[3,4,5,6,11,12],"ownerRefs":[7,8,9,10],"routeContracts":[{"method":"GET","path":"/api/config","condition":"read mode or scalar edit; file replacement does not need this read"},{"method":"GET","path":"/api/provider-request-pacing","condition":"read mode only","query":"name="},{"method":"PATCH","path":"/api/providers","condition":"only scalar/file edit","query":"name=","body":"{requestPacing:validatedRules}"},{"method":"GET","path":"/api/provider-request-pacing","query":"optional name=; absent means all configured providers"}],"argument":"ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS.","workflowJoins":[{"workflowIndex":0,"guiRefs":[0,1,2],"cliRefs":[3,4,5,6],"ownerRefs":[7,8,9,10],"argument":"ocx provider pacing [--json]; ocx provider pacing [--enabled ] [--rpm ] [--min-interval-ms ] [--max-concurrent ] [--json]; ocx provider pacing --file [--json] selects live-management. ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS."},{"workflowIndex":1,"guiRefs":[0,1,2],"cliRefs":[11,12],"ownerRefs":[8],"argument":"ocx inspect pacing [--name ] [--json] selects live-management. ProviderSettings builds requestPacing with enabled, requestsPerMinute, minIntervalMs, maxConcurrentRequests and model rules, and polls providerRequestPacingStatus. handleProviderPacingCommand reads stored rules from /api/config, merges explicit scalar flags while preserving observed model rules, or replaces from a validated file, and PATCHes requestPacing through requestPacingConfigError. No edit means stored rules plus runtime queue counters; inspect pacing reads runtime counters alone. Scalar read/replace is not CAS."}]},"proofIds":["equivalence-set-P04","wp3-final-suite"],"limitations":["Configuration and runtime pacing observations distinct; scalar edits preserve observed model rules but do not gain CAS."],"acceptanceBasis":"Corrected immutable per-workflow source equivalence; retained historical proof IDs require the dedicated evidence join and parent current-head publication checks."} + }, + { + "id": "set-P05", + "domain": "settings", + "baseline": "IMPLEMENTATION_GAP", + "status": "complete", + "unit": "wp3", + "evidence": [ + "032_provider_verification.md", + "src/cli/provider-lifecycle-runtime.ts", + "src/cli/provider-settings.ts", + "src/cli/provider-batch.ts", + "tests/cli/cli-provider-sync-result.test.ts" + ], + "task": "set-P05 — Enable/disable, choose default, remove provider", + "existingCli": "provider edit P --enabled on\\|off; provider set-default P; provider remove P", + "sourceInventory": "004_settings_coverage.md", + "final": {"disposition":"verified","phase":"wp3","pr":"https://github.com/lidge-jun/opencodex/pull/6528","workflows":[{"command":"ocx provider edit","invocation":"ocx provider edit --enabled [--json]","target":"live-management","authority":"scoped-automation","help":"ocx provider edit --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P05","wp3-final-suite"],"routes":[{"method":"PATCH","path":"/api/providers","query":"name=","body":"only explicitly supplied provider patch fields"}],"limitations":["At least one edit is required; only supplied fields are patched. update is an existing alias. Use provider show only for local state, not as proof of the live write.","Management errors use safe fixed provider diagnostics; catalog failures retain the saved receipt and a nonzero exit."]},{"command":"ocx provider set-default","invocation":"ocx provider set-default [--json]","target":"local","authority":"scoped-automation","help":"ocx provider set-default --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P05","wp3-final-suite"],"routes":[],"limitations":["Omission of --live retains local save/no-op semantics. Live sends only setDefault:true; disabled/unknown providers are refused by the server. This operation does not claim client synchronization."],"condition":"omit --live"},{"command":"ocx provider set-default","invocation":"ocx provider set-default --live [--json]","target":"live-management","authority":"scoped-automation","help":"ocx provider set-default --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P05","wp3-final-suite"],"routes":[{"method":"PATCH","path":"/api/providers","query":"name=","body":"{setDefault:true}"}],"limitations":["Omission of --live retains local save/no-op semantics. Live sends only setDefault:true; disabled/unknown providers are refused by the server. This operation does not claim client synchronization."],"condition":"explicit --live"},{"command":"ocx provider remove","invocation":"ocx provider remove [--json]","target":"local","authority":"scoped-automation","help":"ocx provider remove --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P05","wp3-final-suite"],"routes":[],"limitations":["Local removal preserves its existing refusal for the current default and has no --yes option.","Live removal uses one server DELETE. The server can choose a replacement default and clean custom models, context caps and OAuth accounts; dependency/last-provider refusals stay authoritative.","No local fallback, multi-step deletion or automatic retry. A saved result with failed catalog convergence exits nonzero without claiming rollback."],"condition":"omit --live"},{"command":"ocx provider remove","invocation":"ocx provider remove --live --yes [--json]","target":"live-management","authority":"scoped-automation","help":"ocx provider remove --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P05","wp3-final-suite"],"routes":[{"method":"DELETE","path":"/api/providers","query":"name="}],"limitations":["Local removal preserves its existing refusal for the current default and has no --yes option.","Live removal uses one server DELETE. The server can choose a replacement default and clean custom models, context caps and OAuth accounts; dependency/last-provider refusals stay authoritative.","No local fallback, multi-step deletion or automatic retry. A saved result with failed catalog convergence exits nonzero without claiming rollback."],"condition":"explicit --live"}],"sourceRefs":[{"path":"gui/src/pages/use-providers-crud.ts","line":67,"snapshot":"WP8-ledger-source-snapshot","sha256":"55058d624e1c69661c170fe1116d5f746a3309db8f2cc1122ae6ce1b39da02b8","anchor":"const res = await fetch(`${apiBase}/api/providers?name=${encodeURIComponent(name)}`, { method: \"DELETE\" });","role":"gui","claim":"The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/pages/use-providers-crud.ts","line":92,"snapshot":"WP8-ledger-source-snapshot","sha256":"55058d624e1c69661c170fe1116d5f746a3309db8f2cc1122ae6ce1b39da02b8","anchor":"const res = await fetch(`${apiBase}/api/providers?name=${encodeURIComponent(name)}`, {","role":"gui","claim":"The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/pages/use-providers-crud.ts","line":145,"snapshot":"WP8-ledger-source-snapshot","sha256":"55058d624e1c69661c170fe1116d5f746a3309db8f2cc1122ae6ce1b39da02b8","anchor":"const res = await fetch(`${apiBase}/api/providers?name=${encodeURIComponent(name)}`, {","role":"gui","claim":"The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":66,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"const patch: Record = takeProviderEditSettings(args);","role":"cli","claim":"ocx provider edit --enabled [--json] — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":135,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"if (enabled !== undefined) patch.disabled = !enabled;","role":"cli","claim":"ocx provider edit --enabled [--json] — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":143,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"const result = await runtimeRequest(`/api/providers?name=${encodeURIComponent(name)}`, {","role":"cli","claim":"ocx provider edit --enabled [--json] — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1412,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"if (url.pathname === \"/api/providers\" && req.method === \"PATCH\") {","role":"common-owner","claim":"PATCH /api/providers — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":280,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"const providerError = providerManagementConfigError(name, transportCandidate, { allowOperatorOverlays: true })","role":"common-owner","claim":"applyProviderPatch validates the merged transport candidate.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1505,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":": providerManagementConfigError(","role":"common-owner","claim":"PATCH delegates full merged provider validation before save.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":476,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"function handleSetDefault(args: string[]): void {","role":"cli","claim":"ocx provider set-default [--json] — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":502,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"validateAndSave(config);","role":"cli","claim":"ocx provider set-default [--json] — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/config.ts","line":370,"snapshot":"WP8-ledger-source-snapshot","sha256":"2540d8851dbc204cc78fd570f73818bd6a4103a55441de1225730780b5c1fd87","anchor":"export function saveConfig(config: OcxConfig): void {","role":"common-owner","claim":"Local config persistence; runtime default reassignment/removal route is not executed.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":527,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"const liveArgs = subArgs.filter(arg => arg === \"--live\" || arg.startsWith(\"--live=\"));","role":"cli","claim":"ocx provider set-default --live [--json] — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-lifecycle-runtime.ts","line":158,"snapshot":"WP8-ledger-source-snapshot","sha256":"a3e846c284e3d218afb488107c9233183fb4db92166a8d7f98a838943ba0ad47","anchor":"if (sub === \"remove\" && !yes) invalid(\"Live removal requires --yes.\");","role":"cli","claim":"ocx provider set-default --live [--json] — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-lifecycle-runtime.ts","line":164,"snapshot":"WP8-ledger-source-snapshot","sha256":"a3e846c284e3d218afb488107c9233183fb4db92166a8d7f98a838943ba0ad47","anchor":"const result = await runtimeRequest(`/api/providers?name=${encodeURIComponent(name)}`, {","role":"cli","claim":"ocx provider set-default --live [--json] — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":371,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"function handleRemove(args: string[]): void {","role":"cli","claim":"ocx provider remove [--json] — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":408,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"validateAndSave(config);","role":"cli","claim":"ocx provider remove [--json] — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider.ts","line":527,"snapshot":"WP8-ledger-source-snapshot","sha256":"1db7575512fe9d29a8b0452a2e0f0fae843442cd7131d7df2caa9c6f6396926e","anchor":"const liveArgs = subArgs.filter(arg => arg === \"--live\" || arg.startsWith(\"--live=\"));","role":"cli","claim":"ocx provider remove --live --yes [--json] — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-lifecycle-runtime.ts","line":158,"snapshot":"WP8-ledger-source-snapshot","sha256":"a3e846c284e3d218afb488107c9233183fb4db92166a8d7f98a838943ba0ad47","anchor":"if (sub === \"remove\" && !yes) invalid(\"Live removal requires --yes.\");","role":"cli","claim":"ocx provider remove --live --yes [--json] — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-lifecycle-runtime.ts","line":164,"snapshot":"WP8-ledger-source-snapshot","sha256":"a3e846c284e3d218afb488107c9233183fb4db92166a8d7f98a838943ba0ad47","anchor":"const result = await runtimeRequest(`/api/providers?name=${encodeURIComponent(name)}`, {","role":"cli","claim":"ocx provider remove --live --yes [--json] — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1824,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"if (url.pathname === \"/api/providers\" && req.method === \"DELETE\") {","role":"common-owner","claim":"DELETE /api/providers — The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"}],"sourceJoin":{"guiAction":"set-P05 — Enable/disable, choose default, remove provider","guiRefs":[0,1,2],"cliRefs":[3,4,5,9,10,12,13,14,15,16,17,18,19],"ownerRefs":[6,7,8,11,20],"routeContracts":[{"method":"PATCH","path":"/api/providers","query":"name=","body":"only explicitly supplied provider patch fields"},{"method":"PATCH","path":"/api/providers","query":"name=","body":"{setDefault:true}"},{"method":"DELETE","path":"/api/providers","query":"name="}],"argument":"The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect.","workflowJoins":[{"workflowIndex":0,"guiRefs":[0,1,2],"cliRefs":[3,4,5],"ownerRefs":[6,7,8],"argument":"ocx provider edit --enabled [--json] selects live-management. The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect."},{"workflowIndex":1,"guiRefs":[0,1,2],"cliRefs":[9,10],"ownerRefs":[11],"argument":"ocx provider set-default [--json] selects local. The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect."},{"workflowIndex":2,"guiRefs":[0,1,2],"cliRefs":[12,13,14],"ownerRefs":[6],"argument":"ocx provider set-default --live [--json] selects live-management. The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect."},{"workflowIndex":3,"guiRefs":[0,1,2],"cliRefs":[15,16],"ownerRefs":[11],"argument":"ocx provider remove [--json] selects local. The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect."},{"workflowIndex":4,"guiRefs":[0,1,2],"cliRefs":[17,18,19],"ownerRefs":[20],"argument":"ocx provider remove --live --yes [--json] selects live-management. The GUI toggles disabled, selects setDefault:true and removes the named provider through PATCH/DELETE. provider edit maps --enabled to disabled; explicit set-default/remove --live uses the same runtime handlers with --yes for deletion. Local handleSetDefault/handleRemove instead validate and save config, refuse deleting the local default/last provider and return needsSync. Runtime deletion owns default reassignment, dependencies and cleanup; local CRUD is not that live side effect."}]},"proofIds":["equivalence-set-P05","wp3-final-suite"],"limitations":["For runtime default/removal use --live; removal requires --yes. Local default deletion differs intentionally."],"acceptanceBasis":"Corrected immutable per-workflow source equivalence; retained historical proof IDs require the dedicated evidence join and parent current-head publication checks."} + }, + { + "id": "set-P06", + "domain": "settings", + "baseline": "DISCOVERY_ONLY", + "status": "complete", + "unit": "wp2", + "evidence": [ + "022_discovery_verification.md", + "src/cli/capabilities.ts", + "skills/ocx/references/01_management_surface.md" + ], + "task": "set-P06 — Change OpenAI account mode", + "existingCli": "provider account-mode pool\\|direct --json", + "sourceInventory": "004_settings_coverage.md", + "final": {"disposition":"verified","phase":"wp2","pr":"https://github.com/lidge-jun/opencodex/pull/6526","workflows":[{"command":"ocx provider account-mode","invocation":"ocx provider account-mode [--json]","target":"live-management","authority":"scoped-automation","help":"ocx provider account-mode --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P06","wp2-final-suite"],"routes":[{"method":"PATCH","path":"/api/providers","query":"name=openai","body":"{codexAccountMode:pool|direct}"}],"limitations":["Patches codexAccountMode on provider openai; this does not switch the physical Codex login."]}],"sourceRefs":[{"path":"gui/src/components/provider-workspace/ProviderSettings.tsx","line":295,"snapshot":"WP8-ledger-source-snapshot","sha256":"a8edade4ec7ff1b3c5860e10a31ef5ba6048e36949a2db3d6a15b0b3734b94fd","anchor":"const res = await onUpdateProvider(\"openai\", { codexAccountMode: next });","role":"gui","claim":"ProviderSettings.applyAccountMode passes {codexAccountMode:next} for openai. accountMode accepts pool|direct and sends that identical single-field PATCH. The server requires canonical openai and an unmixed account-mode patch, then persists mode and performs its existing quota/thread/cache reconciliation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":254,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"async function accountMode(argv: string[], deps: RuntimeApiDeps): Promise {","role":"cli","claim":"ocx provider account-mode [--json] — ProviderSettings.applyAccountMode passes {codexAccountMode:next} for openai. accountMode accepts pool|direct and sends that identical single-field PATCH. The server requires canonical openai and an unmixed account-mode patch, then persists mode and performs its existing quota/thread/cache reconciliation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":260,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"const result = await runtimeRequest(\"/api/providers?name=openai\", {","role":"cli","claim":"ocx provider account-mode [--json] — ProviderSettings.applyAccountMode passes {codexAccountMode:next} for openai. accountMode accepts pool|direct and sends that identical single-field PATCH. The server requires canonical openai and an unmixed account-mode patch, then persists mode and performs its existing quota/thread/cache reconciliation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1412,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"if (url.pathname === \"/api/providers\" && req.method === \"PATCH\") {","role":"common-owner","claim":"PATCH /api/providers — ProviderSettings.applyAccountMode passes {codexAccountMode:next} for openai. accountMode accepts pool|direct and sends that identical single-field PATCH. The server requires canonical openai and an unmixed account-mode patch, then persists mode and performs its existing quota/thread/cache reconciliation.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1433,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"if (name !== \"openai\") return jsonResponse({ error: \"codexAccountMode is valid only for provider openai\" }, 400);","role":"common-owner","claim":"Account-mode branch requires openai and validates pool/direct.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1443,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"config.providers.openai = { ...provider, codexAccountMode: mode };","role":"common-owner","claim":"Persist the account mode in the shared provider config.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"}],"sourceJoin":{"guiAction":"set-P06 — Change OpenAI account mode","guiRefs":[0],"cliRefs":[1,2],"ownerRefs":[3,4,5],"routeContracts":[{"method":"PATCH","path":"/api/providers","query":"name=openai","body":"{codexAccountMode:pool|direct}"}],"argument":"ProviderSettings.applyAccountMode passes {codexAccountMode:next} for openai. accountMode accepts pool|direct and sends that identical single-field PATCH. The server requires canonical openai and an unmixed account-mode patch, then persists mode and performs its existing quota/thread/cache reconciliation.","workflowJoins":[{"workflowIndex":0,"guiRefs":[0],"cliRefs":[1,2],"ownerRefs":[3,4,5],"argument":"ocx provider account-mode [--json] selects live-management. ProviderSettings.applyAccountMode passes {codexAccountMode:next} for openai. accountMode accepts pool|direct and sends that identical single-field PATCH. The server requires canonical openai and an unmixed account-mode patch, then persists mode and performs its existing quota/thread/cache reconciliation."}]},"proofIds":["equivalence-set-P06","wp2-final-suite"],"limitations":["ProviderSettings.applyAccountMode passes {codexAccountMode:next} for openai. accountMode accepts pool|direct and sends that identical single-field PATCH. The server requires canonical openai and an unmixed account-mode patch, then persists mode and performs its existing quota/thread/cache reconciliation."],"acceptanceBasis":"Corrected immutable per-workflow source equivalence; retained historical proof IDs require the dedicated evidence join and parent current-head publication checks."} + }, + { + "id": "set-P07", + "domain": "settings", + "baseline": "IMPLEMENTATION_GAP", + "status": "complete", + "unit": "wp3", + "evidence": [ + "032_provider_verification.md", + "src/cli/provider-lifecycle-runtime.ts", + "src/cli/provider-settings.ts", + "src/cli/provider-batch.ts", + "tests/cli/cli-provider-sync-result.test.ts" + ], + "task": "set-P07 — Atomically edit provider JSON", + "existingCli": "config import exists but is not provider-editor CAS parity", + "sourceInventory": "004_settings_coverage.md", + "final": {"disposition":"verified","phase":"wp3","pr":"https://github.com/lidge-jun/opencodex/pull/6528","workflows":[{"command":"ocx provider snapshot","invocation":"ocx provider snapshot [--json]","target":"live-management","authority":"scoped-automation","help":"ocx provider snapshot --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P07","wp3-final-suite"],"routes":[{"method":"GET","path":"/api/config"}],"limitations":["Emits exactly {defaultProvider,providers}, validating the canonical editor DTO after removing only GUI display markers. Unexpected secret/unknown fields fail closed.","Save this JSON as the baseline for apply; it is not raw config export and carries no cross-invocation target identity. Use the intended host/context."],"sourceJoin":{"guiRefs":[0,1],"cliRefs":[2,3],"ownerRefs":[7,8,9],"argument":"The GUI opens an editable {defaultProvider,providers} projection of /api/config, excluding hasApiKey/hasHeaders/xaiResponsesOptInState/initialModelSelection. CLI snapshot fetches the same live config, removes those four decorations and validates the editor DTO before emitting it; it is not a full secret-bearing config export."}},{"command":"ocx provider apply","invocation":"ocx provider apply --baseline --file [--yes] [--json]","target":"live-management","authority":"scoped-automation","help":"ocx provider apply --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P07","wp3-final-suite"],"routes":[{"method":"PUT","path":"/api/providers"}],"limitations":["Both documents must contain exactly defaultProvider and providers, with no credential/derived/unknown fields. At most one source may be stdin; each input and the complete serialized body are bounded to 4 MiB, with a 30-second read deadline.","Sends one {baseline,next} PUT. Stale baseline is exit 5; no automatic refresh, rebase, retry or local fallback.","Batch removal preserves the server batch contract and does not promise single-provider DELETE OAuth cleanup. Saved-but-unconverged catalog outcomes remain visible and nonzero."],"sourceJoin":{"guiRefs":[0,1],"cliRefs":[4,5,6],"ownerRefs":[7,8,9],"argument":"The GUI saves its captured baseline and parsed draft as {baseline,next}; CLI apply reads the two required documents and sends precisely that PUT body. Both reach the provider batch owner, which checks the projected baseline again inside mutatePersistedConfig before replacing provider state. --yes gates CLI removals/renames; stale baseline is not retried or silently rebased. The batch cleanup limits in the existing record remain."}}],"sourceRefs":[{"path":"gui/src/hooks/useJsonConfigEditor.ts","line":25,"snapshot":"WP8-ledger-source-snapshot","sha256":"aa8a0d212fea85289f51fbabbe73b307ed49b612db3177edc9071168379ac145","anchor":"defaultProvider: config.defaultProvider,","role":"gui","claim":"Project defaultProvider and providers, stripping only the four editor decorations.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/hooks/useJsonConfigEditor.ts","line":70,"snapshot":"WP8-ledger-source-snapshot","sha256":"aa8a0d212fea85289f51fbabbe73b307ed49b612db3177edc9071168379ac145","anchor":"const res = await fetch(`${apiBase}/api/providers`, {","role":"gui","claim":"Save edited JSON with the captured baseline and next document in one PUT.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-batch.ts","line":23,"snapshot":"WP8-ledger-source-snapshot","sha256":"b7424d6c1d46cd5e8580122e60e1b6fb1ff6d204bbb7e34de41d0eb64563211e","anchor":"const fetched = await runtimeRequest(\"/api/config\", { method: \"GET\", redirect: \"error\" }, { ...deps, baseUrl });","role":"cli","claim":"snapshot fetches the live config.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-batch.ts","line":32,"snapshot":"WP8-ledger-source-snapshot","sha256":"b7424d6c1d46cd5e8580122e60e1b6fb1ff6d204bbb7e34de41d0eb64563211e","anchor":"const parsed = parseProviderEditorConfigDTO({","role":"cli","claim":"snapshot projects the same editable provider DTO and validates it.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-batch.ts","line":53,"snapshot":"WP8-ledger-source-snapshot","sha256":"b7424d6c1d46cd5e8580122e60e1b6fb1ff6d204bbb7e34de41d0eb64563211e","anchor":"const baseline = editorInput(await readJsonInput(baselinePath, deps, \"Provider baseline\"));","role":"cli","claim":"apply parses the explicit baseline and next inputs without rebasing them.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-batch.ts","line":58,"snapshot":"WP8-ledger-source-snapshot","sha256":"b7424d6c1d46cd5e8580122e60e1b6fb1ff6d204bbb7e34de41d0eb64563211e","anchor":"const body = serializeManagementJson({ baseline, next });","role":"cli","claim":"apply serializes the single baseline/next request.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-batch.ts","line":60,"snapshot":"WP8-ledger-source-snapshot","sha256":"b7424d6c1d46cd5e8580122e60e1b6fb1ff6d204bbb7e34de41d0eb64563211e","anchor":"const result = await runtimeRequest(\"/api/providers\", {","role":"cli","claim":"apply issues one PUT to the provider batch route.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/config-routes.ts","line":307,"snapshot":"WP8-ledger-source-snapshot","sha256":"a8e37c1e2816f1d6082cddb3d21d9d526f395bab78ecdbc1f0073c0375f57eba","anchor":"return jsonResponse(withProviderCatalogCapabilityDTO(safeConfigDTO(config), config));","role":"common-owner","claim":"Config GET exposes safe provider configuration.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1070,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"const baselineResult = parseProviderEditorConfigDTO(rawBody.baseline);","role":"common-owner","claim":"Batch route validates both documents with the canonical editor parser.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1094,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"const outcome = mutatePersistedConfig(persisted => {","role":"common-owner","claim":"Persistence callback repeats the baseline comparison before committing the candidate.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"}],"sourceJoin":{"guiAction":"set-P07 — Atomically edit provider JSON","guiRefs":[0,1],"cliRefs":[2,3,4,5,6],"ownerRefs":[7,8,9],"routeContracts":[{"method":"GET","path":"/api/config"},{"method":"PUT","path":"/api/providers"}],"argument":"The GUI opens an editable {defaultProvider,providers} projection of /api/config, excluding hasApiKey/hasHeaders/xaiResponsesOptInState/initialModelSelection. CLI snapshot fetches the same live config, removes those four decorations and validates the editor DTO before emitting it; it is not a full secret-bearing config export. The GUI saves its captured baseline and parsed draft as {baseline,next}; CLI apply reads the two required documents and sends precisely that PUT body. Both reach the provider batch owner, which checks the projected baseline again inside mutatePersistedConfig before replacing provider state. --yes gates CLI removals/renames; stale baseline is not retried or silently rebased. The batch cleanup limits in the existing record remain.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},"proofIds":["equivalence-set-P07","wp3-final-suite"],"limitations":["Stale projected baseline refuses without retry/rebase; batch deletion is not credential erasure."],"acceptanceBasis":"source-equivalence + historical executed suites; current-head publication still pending"} + }, + { + "id": "set-P08", + "domain": "settings", + "baseline": "DISCOVERY_ONLY", + "status": "complete", + "unit": "wp2", + "evidence": [ + "022_discovery_verification.md", + "src/cli/capabilities.ts", + "skills/ocx/references/01_management_surface.md" + ], + "task": "set-P08 — Check provider connectivity", + "existingCli": "provider test P --json", + "sourceInventory": "004_settings_coverage.md", + "final": {"disposition":"verified","phase":"wp2","pr":"https://github.com/lidge-jun/opencodex/pull/6526","workflows":[{"command":"ocx provider test","invocation":"ocx provider test [--json]","target":"live-management","authority":"scoped-automation","help":"ocx provider test --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P08","wp2-final-suite","wp8-provider-exit","wp8-supplement-cli"],"routes":[{"method":"POST","path":"/api/providers/test","query":"name="}],"limitations":["May contact the upstream provider; requires operator intent to probe. Static catalogs can return applicable:false; failed connectivity exits nonzero."]}],"sourceRefs":[{"path":"gui/src/components/provider-workspace/ProviderOverview.tsx","line":109,"snapshot":"WP8-ledger-source-snapshot","sha256":"ee87b22df6b81a205fc1606000ea072625a9ae5d859b4a4e450b3f09f4c37863","anchor":"const response = await fetch(`${apiBase}/api/providers/test?name=${encodeURIComponent(item.name)}`, {","role":"gui","claim":"ProviderOverview POSTs /api/providers/test?name and renders its connection result. testProvider selects the identical route and distinguishes applicable:false static catalogs from ok:true connectivity; an applicable failure sets exit 1. The named server handler checks provider existence/disabled state and owns the upstream probe. No real upstream success is inferred from source inspection.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":150,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"async function testProvider(argv: string[], deps: RuntimeApiDeps): Promise {","role":"cli","claim":"ocx provider test [--json] — ProviderOverview POSTs /api/providers/test?name and renders its connection result. testProvider selects the identical route and distinguishes applicable:false static catalogs from ok:true connectivity; an applicable failure sets exit 1. The named server handler checks provider existence/disabled state and owns the upstream probe. No real upstream success is inferred from source inspection.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":156,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"const result = await runtimeRequest>(`/api/providers/test?name=${encodeURIComponent(name)}`, {","role":"cli","claim":"ocx provider test [--json] — ProviderOverview POSTs /api/providers/test?name and renders its connection result. testProvider selects the identical route and distinguishes applicable:false static catalogs from ok:true connectivity; an applicable failure sets exit 1. The named server handler checks provider existence/disabled state and owns the upstream probe. No real upstream success is inferred from source inspection.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":1619,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"if (url.pathname === \"/api/providers/test\" && req.method === \"POST\") {","role":"common-owner","claim":"POST /api/providers/test — ProviderOverview POSTs /api/providers/test?name and renders its connection result. testProvider selects the identical route and distinguishes applicable:false static catalogs from ok:true connectivity; an applicable failure sets exit 1. The named server handler checks provider existence/disabled state and owns the upstream probe. No real upstream success is inferred from source inspection.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"}],"sourceJoin":{"guiAction":"set-P08 — Check provider connectivity","guiRefs":[0],"cliRefs":[1,2],"ownerRefs":[3],"routeContracts":[{"method":"POST","path":"/api/providers/test","query":"name="}],"argument":"ProviderOverview POSTs /api/providers/test?name and renders its connection result. testProvider selects the identical route and distinguishes applicable:false static catalogs from ok:true connectivity; an applicable failure sets exit 1. The named server handler checks provider existence/disabled state and owns the upstream probe. No real upstream success is inferred from source inspection.","workflowJoins":[{"workflowIndex":0,"guiRefs":[0],"cliRefs":[1,2],"ownerRefs":[3],"argument":"ocx provider test [--json] selects live-management. ProviderOverview POSTs /api/providers/test?name and renders its connection result. testProvider selects the identical route and distinguishes applicable:false static catalogs from ok:true connectivity; an applicable failure sets exit 1. The named server handler checks provider existence/disabled state and owns the upstream probe. No real upstream success is inferred from source inspection."}]},"proofIds":["equivalence-set-P08","wp2-final-suite","wp8-provider-exit","wp8-supplement-cli"],"limitations":["Connectivity probe can contact upstream; applicable:false is not live-provider success."],"acceptanceBasis":"Corrected immutable per-workflow source equivalence; retained historical proof IDs require the dedicated evidence join and parent current-head publication checks."} + }, + { + "id": "set-P09", + "domain": "settings", + "baseline": "COMPLETE", + "status": "complete", + "unit": "wp2", + "evidence": [ + "022_discovery_verification.md", + "src/cli/capabilities.ts", + "skills/ocx/references/01_management_surface.md" + ], + "task": "set-P09 — Inspect provider quota/capacity/usage", + "existingCli": "provider quota [--refresh] --json; account list P --quota [--refresh] --json; usage --range 30d --surface codex\\|all --json", + "sourceInventory": "004_settings_coverage.md", + "final": {"disposition":"verified","phase":"wp2","pr":"https://github.com/lidge-jun/opencodex/pull/6526","workflows":[{"command":"ocx provider quota","invocation":"ocx provider quota [--refresh] [--json]","target":"live-management","authority":"scoped-automation","help":"ocx provider quota --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P09","wp2-final-suite","wp8-key-quota","wp8-residual-integrated"],"routes":[{"method":"GET","path":"/api/provider-quotas","query":"refresh=1 only with --refresh"}],"limitations":["Reads quota observations without changing operator settings. Cache misses may probe upstream even without --refresh; --refresh bypasses the cache and updates observations. This GET is not the POST account-refresh operation."]},{"command":"ocx account list","invocation":"ocx account list [provider] [--all] [--quota [--refresh]] [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account list --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P09","wp2-final-suite","wp8-key-quota","wp8-residual-integrated"],"routes":[{"method":"GET","path":"/api/oauth/providers","condition":"only list without a provider; discover admitted OAuth families"},{"method":"GET","path":"/api/codex-auth/accounts","query":"refresh=1 only with --quota --refresh","condition":"only Codex account family"},{"method":"GET","path":"/api/codex-auth/active","condition":"only Codex account family"},{"method":"GET","path":"/api/oauth/accounts","query":"provider=; quota=1 only with --quota; refresh=1 only with both quota and refresh","condition":"only OAuth account family"},{"method":"GET","path":"/api/providers/keys","query":"name=; quota=1 only with --quota; refresh=1 only with both quota and refresh","condition":"only API-key account family"}],"limitations":["Omitting provider discovers OAuth providers and includes configured families. JSON is {accounts,notes}, not a raw server account array.","Default listing is observational. --quota can probe provider quotas; --refresh is only acted on together with --quota. A refresh observation is not pending-account validation consent."]},{"command":"ocx usage","invocation":"ocx usage [--range ] [--surface ] [--since ] [--until ] [--provider ] [--model ] [--api-key-id ] [--json]","target":"live-management","authority":"scoped-automation","help":"ocx usage --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P09","wp2-final-suite","wp8-key-quota","wp8-residual-integrated"],"routes":[{"method":"GET","path":"/api/usage","condition":"only when readClientConnectionState is not connected","query":"range=30d default; optional range/surface/provider/model/apiKeyId/since/until"}],"limitations":["This workflow is the non-connected management branch. Provider/model are exact attribution filters. Custom window and selected apiKeyId require server acknowledgment. See the separate connected-client workflow for enrolled self usage."]},{"command":"ocx usage","invocation":"ocx usage [--range ] [--surface ] [--since ] [--until ] [--provider ] [--model ] [--json]","target":"connected-client","authority":"scoped-automation","help":"ocx usage --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P09","wp2-final-suite","wp8-key-quota","wp8-residual-integrated"],"routes":[{"method":"GET","path":"/v1/usage","condition":"connection.kind === connected; enrolled data key, self scope","query":"range/surface/provider/model/since/until; --api-key-id forbidden"}],"limitations":["Only the enrolled client key is visible. Requires matching connection/token fingerprint and rechecks ownership after read; never falls back to local /api/usage."]}],"sourceRefs":[{"path":"gui/src/components/provider-workspace/ProviderWorkspaceShell.tsx","line":182,"snapshot":"WP8-ledger-source-snapshot","sha256":"76eb33aa3244f06b3fca7b0b699fc6940f673bff0e61dd6868711b20a2e77c36","anchor":"const res = await fetch(apiBase + \"/api/usage?range=30d\", { signal });","role":"gui","claim":"Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/hooks/useProviderAccountPools.ts","line":279,"snapshot":"WP8-ledger-source-snapshot","sha256":"aad9433a78ea30dc8426b227cc80b0b6bf2687688ab6b8d30c9b041f3ffa554f","anchor":"const url = `${apiBase}/api/providers/keys?name=${encodeURIComponent(name)}`;","role":"gui","claim":"Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":175,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"async function quota(argv: string[], deps: RuntimeApiDeps): Promise {","role":"cli","claim":"ocx provider quota [--refresh] [--json] — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/provider-runtime.ts","line":180,"snapshot":"WP8-ledger-source-snapshot","sha256":"4d99275d83329b55780550cdb68297ee8d00dca4fe1b5c4dfcddf8357a0ea68b","anchor":"const result = await runtimeRequest(`/api/provider-quotas${refresh ? \"?refresh=1\" : \"\"}`, {}, deps);","role":"cli","claim":"ocx provider quota [--refresh] [--json] — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/provider-routes.ts","line":906,"snapshot":"WP8-ledger-source-snapshot","sha256":"715e1382a8567a983fe5291023d2c924a961b4a00bbe9c7a1fbc00d4693d8be9","anchor":"const snapshot = await fetchProviderQuotaReports(config, forceRefresh);","role":"common-owner","claim":"GET /api/provider-quotas — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account.ts","line":179,"snapshot":"WP8-ledger-source-snapshot","sha256":"7f35085b33227f080dfe88c40824435923336d15f28a05869226c49bf5c1f0b8","anchor":"async function cmdList(rest: string[], deps: AccountDeps): Promise {","role":"cli","claim":"ocx account list [provider] [--all] [--quota [--refresh]] [--json] — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account.ts","line":229,"snapshot":"WP8-ledger-source-snapshot","sha256":"7f35085b33227f080dfe88c40824435923336d15f28a05869226c49bf5c1f0b8","anchor":"const r = await fetchRows(deps, baseUrl, t.name, t.type, wantsQuota ? { refresh: refreshQuota } : undefined);","role":"cli","claim":"ocx account list [provider] [--all] [--quota [--refresh]] [--json] — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":456,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"if (type === \"codex\") return fetchCodexRows(deps, baseUrl, Boolean(quota?.refresh), quota !== undefined);","role":"cli","claim":"ocx account list [provider] [--all] [--quota [--refresh]] [--json] — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":293,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"const accountsPath = `/api/codex-auth/accounts${refreshAction ? \"/refresh\" : forceRefresh ? \"?refresh=1\" : \"\"}`;","role":"cli","claim":"ocx account list [provider] [--all] [--quota [--refresh]] [--json] — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":379,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"const query = quota","role":"cli","claim":"ocx account list [provider] [--all] [--quota [--refresh]] [--json] — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":424,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"const query = `?name=${encodeURIComponent(name)}${quota ? `"a=1${quota.refresh ? \"&refresh=1\" : \"\"}` : \"\"}`;","role":"cli","claim":"ocx account list [provider] [--all] [--quota [--refresh]] [--json] — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":225,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"return jsonResponse({ providers: listOAuthProviders().filter(provider => canStartManagementOAuth(provider, principal)) });","role":"common-owner","claim":"GET /api/oauth/providers — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/codex/auth-api/routes.ts","line":37,"snapshot":"WP8-ledger-source-snapshot","sha256":"e1cf949c54f9c63141b5a5316d7f5cc9e14a23df45d03ac5ac0898eead022466","anchor":"return jsonResponse({ accounts: await listCodexAuthAccounts(config, forceRefresh) });","role":"common-owner","claim":"GET /api/codex-auth/accounts — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/codex/auth-api/routes.ts","line":292,"snapshot":"WP8-ledger-source-snapshot","sha256":"e1cf949c54f9c63141b5a5316d7f5cc9e14a23df45d03ac5ac0898eead022466","anchor":"if (url.pathname === \"/api/codex-auth/active\" && req.method === \"GET\") {","role":"common-owner","claim":"GET /api/codex-auth/active — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":398,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/oauth/accounts\" && req.method === \"GET\") {","role":"common-owner","claim":"GET /api/oauth/accounts — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":929,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/providers/keys\" && req.method === \"GET\") {","role":"common-owner","claim":"GET /api/providers/keys — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/observe.ts","line":167,"snapshot":"WP8-ledger-source-snapshot","sha256":"bb55ba6db1ffc6b71653a1a9149e3440b599d5a849823cd9d2b4b5fab52735f0","anchor":"async function usage(argv: string[], deps: RuntimeApiDeps): Promise {","role":"cli","claim":"ocx usage [--range ] [--surface ] [--since ] [--until ] [--provider ] [--model ] [--api-key-id ] [--json] — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/observe.ts","line":200,"snapshot":"WP8-ledger-source-snapshot","sha256":"bb55ba6db1ffc6b71653a1a9149e3440b599d5a849823cd9d2b4b5fab52735f0","anchor":"if (connection.kind === \"connected\") {","role":"cli","claim":"ocx usage [--range ] [--surface ] [--since ] [--until ] [--provider ] [--model ] [--api-key-id ] [--json] — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/observe.ts","line":217,"snapshot":"WP8-ledger-source-snapshot","sha256":"bb55ba6db1ffc6b71653a1a9149e3440b599d5a849823cd9d2b4b5fab52735f0","anchor":"if (apiKeyId === undefined) result = await runtimeRequest(`/api/usage${suffix}`, {}, deps);","role":"cli","claim":"ocx usage [--range ] [--surface ] [--since ] [--until ] [--provider ] [--model ] [--api-key-id ] [--json] — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/logs-usage-routes.ts","line":188,"snapshot":"WP8-ledger-source-snapshot","sha256":"f491959dc9b3131050712bcda93354b153961e84f21123dda932d60f8742f1c2","anchor":"if (url.pathname === \"/api/usage\" && req.method === \"GET\") {","role":"common-owner","claim":"GET /api/usage — Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/observe.ts","line":200,"snapshot":"WP8-ledger-source-snapshot","sha256":"bb55ba6db1ffc6b71653a1a9149e3440b599d5a849823cd9d2b4b5fab52735f0","anchor":"if (connection.kind === \"connected\") {","role":"cli","claim":"Connected usage refuses selected apiKeyId and uses enrolled key.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/client/hub-client.ts","line":494,"snapshot":"WP8-ledger-source-snapshot","sha256":"39125a7b92f9bf33200a2003ed95f246ceb3dd33c6ba59cfb4c32d1115c7d3fc","anchor":"const response = await fetchBounded(options.fetchImpl ?? fetch, `${origin}/v1/usage?${query}`, {","role":"cli","claim":"fetchHubUsage GET /v1/usage with x-opencodex-api-key, bounded transport.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/index/serve-options.ts","line":799,"snapshot":"WP8-ledger-source-snapshot","sha256":"c19e839c8c67c43524f60cc12bc4fe71b52e7b27d66e829a754f9c7f91f33e4e","anchor":"if (url.pathname === \"/v1/usage\" && req.method === \"GET\") {","role":"common-owner","claim":"Enrolled self-scoped usage handler; distinct from management /api/usage.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/hooks/useProviderAccountPools.ts","line":298,"snapshot":"WP8-ledger-source-snapshot","sha256":"aad9433a78ea30dc8426b227cc80b0b6bf2687688ab6b8d30c9b041f3ffa554f","anchor":"const data = await readRoster<{ activeId?: string | null; keys?: ApiKeyEntry[] }>(`${url}"a=1${refresh ? \"&refresh=1\" : \"\"}`);","role":"gui","claim":"Per-key quota enrichment opts in with quota=1 and optional refresh=1.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-key-quota.ts","line":67,"snapshot":"WP8-ledger-source-snapshot","sha256":"88e2c3b59ecb6b4818cf5c1c7c27d874a52eda8a5a6e22142e3768b65edb35ae","anchor":"export function projectApiKeyQuotaRows(value: unknown, provider: string): FamilyRows {","role":"cli","claim":"projectApiKeyQuotaRows validates the exact public key-quota projection, including quotaMode, nullable quota and unavailable state.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":943,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"const rows = await fetchProviderApiKeyQuotas(config, name, url.searchParams.get(\"refresh\") === \"1\");","role":"common-owner","claim":"fetchProviderApiKeyQuotas is invoked only for probe-mode keys after quota=1 admission.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/components/provider-workspace/ProviderWorkspaceShell.tsx","line":265,"snapshot":"WP8-ledger-source-snapshot","sha256":"76eb33aa3244f06b3fca7b0b699fc6940f673bff0e61dd6868711b20a2e77c36","anchor":"void fetch(`${apiBase}/api/provider-quotas${quotaForceRefresh ? \"?refresh=1\" : \"\"}`, { signal: bounded.signal })","role":"gui","claim":"Workspace provider quota fetch preserves optional force refresh.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/hub-usage.ts","line":16,"snapshot":"WP8-ledger-source-snapshot","sha256":"15e1ba83765eef62d075aa57463e21d93bd20ea57da2590878b7e12810702992","anchor":"const admission = resolveDataPlaneAdmissionSecret(token, config, \"dedicated\", options);","role":"common-owner","claim":"Connected usage admits a configured dedicated data key only.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/hub-usage.ts","line":40,"snapshot":"WP8-ledger-source-snapshot","sha256":"15e1ba83765eef62d075aa57463e21d93bd20ea57da2590878b7e12810702992","anchor":"apiKeyId: keyId, provider: query.get(\"provider\"), model: query.get(\"model\"),","role":"common-owner","claim":"The usage aggregate is forced to admission.keyId, not caller-selected API-key identity.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"}],"sourceJoin":{"guiAction":"set-P09 — Inspect provider quota/capacity/usage","guiRefs":[0,1,23,26],"cliRefs":[2,3,5,6,7,8,9,10,16,17,18,20,21,24],"ownerRefs":[4,11,12,13,14,15,19,22,25,27,28],"routeContracts":[{"method":"GET","path":"/api/provider-quotas","query":"refresh=1 only with --refresh"},{"method":"GET","path":"/api/oauth/providers","condition":"only list without a provider; discover admitted OAuth families"},{"method":"GET","path":"/api/codex-auth/accounts","query":"refresh=1 only with --quota --refresh","condition":"only Codex account family"},{"method":"GET","path":"/api/codex-auth/active","condition":"only Codex account family"},{"method":"GET","path":"/api/oauth/accounts","query":"provider=; quota=1 only with --quota; refresh=1 only with both quota and refresh","condition":"only OAuth account family"},{"method":"GET","path":"/api/providers/keys","query":"name=; quota=1 only with --quota; refresh=1 only with both quota and refresh","condition":"only API-key account family"},{"method":"GET","path":"/api/usage","condition":"only when readClientConnectionState is not connected","query":"range=30d default; optional range/surface/provider/model/apiKeyId/since/until"},{"method":"GET","path":"/v1/usage","condition":"connection.kind === connected; enrolled data key, self scope","query":"range/surface/provider/model/since/until; --api-key-id forbidden"}],"argument":"Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id.","workflowJoins":[{"workflowIndex":0,"guiRefs":[0,1,23,26],"cliRefs":[2,3],"ownerRefs":[4],"argument":"ocx provider quota [--refresh] [--json] selects live-management. Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id."},{"workflowIndex":1,"guiRefs":[0,1,23,26],"cliRefs":[5,6,7,8,9,10,24],"ownerRefs":[11,12,13,14,15,25],"argument":"ocx account list [provider] [--all] [--quota [--refresh]] [--json] selects live-management. Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id."},{"workflowIndex":2,"guiRefs":[0,1,23,26],"cliRefs":[16,17,18],"ownerRefs":[19],"argument":"ocx usage [--range ] [--surface ] [--since ] [--until ] [--provider ] [--model ] [--api-key-id ] [--json] selects live-management. Provider workspace usage reads /api/usage?range=30d and quota panels read provider/account quota. quota() returns fetchProviderQuotaReports; cmdList selects fetchCodexRows/fetchOAuthRows/fetchKeyRows. The implemented key branch explicitly forwards quota=1 and optional refresh=1 and projectApiKeyQuotaRows preserves quotaMode/quota/quotaUnavailable, credits and custom windows from the same identity-checked key DTO as the GUI. usage() is management-scoped only when not connected; connected mode calls fetchHubUsage at /v1/usage with the enrolled data key and refuses --api-key-id."},{"workflowIndex":3,"guiRefs":[0,1,23,26],"cliRefs":[20,21],"ownerRefs":[22,27,28],"argument":"ocx usage [--range ] [--surface ] [--since ] [--until ] [--provider ] [--model ] [--json] selects connected-client. Connected-client equivalent is intentionally self-scoped and does not grant the GUI administrator provider-wide usage visibility."}]},"proofIds":["equivalence-set-P09","wp2-final-suite","wp8-key-quota","wp8-residual-integrated"],"limitations":["Quota reads may contact upstream even without --refresh; usage remains observational.","Per-key quota is implemented at this revision; --quota opts in and --refresh is effective only with it. The existing WP8 P14 per-key cases must be joined by the evidence worker; this repair did not run tests."],"acceptanceBasis":"Corrected immutable per-workflow source equivalence; retained historical proof IDs require the dedicated evidence join and parent current-head publication checks."} + }, + { + "id": "set-P10", + "domain": "settings", + "baseline": "IMPLEMENTATION_GAP", + "status": "complete", + "unit": "wp5", + "evidence": [ + "052_accounts_verification.md", + "src/cli/account-policy.ts", + "src/cli/account-auth.ts", + "src/cli/agent-settings.ts", + "src/cli/agent-runtime-settings.ts", + "src/cli/system-settings-parity.ts", + "src/cli/v2-runtime.ts", + "tests/cli/cli-account-policy.test.ts", + "tests/cli/cli-v2-runtime.test.ts" + ], + "task": "set-P10 — OAuth login, add another account, reauthenticate, submit callback, cancel, logout", + "existingCli": "account login P [--reauth --id I] [--no-wait]; account reauth/code/cancel; login P (local); logout P --json (local)", + "sourceInventory": "004_settings_coverage.md", + "final": {"disposition":"verified","phase":"wp5","pr":"https://github.com/lidge-jun/opencodex/pull/6539","workflows":[{"command":"ocx account login","invocation":"ocx account login [--id ] [--reauth] [--device] [--method ] [--code -] [--no-wait] [--open-browser ] [--add-account ] [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account login --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P10","wp5-discovery","wp5-auth"],"routes":[{"method":"POST","path":"/api/oauth/login","condition":"non-Codex provider OAuth, without Kiro native --method","body":"{provider,addAccount,openBrowser?,accountId?,reauth?}"},{"method":"POST","path":"/api/oauth/login/code","condition":"OAuth --code only","body":"{provider,input}"},{"method":"GET","path":"/api/oauth/status","condition":"OAuth polling unless --no-wait","query":"provider="},{"method":"POST","path":"/api/codex-auth/login","condition":"provider openai/codex/chatgpt","body":"{id?,reauth?,device?,openBrowser?}"},{"method":"POST","path":"/api/codex-auth/login/code","condition":"Codex --code only","body":"{flowId,input}"},{"method":"GET","path":"/api/codex-auth/login-status","condition":"Codex polling unless --no-wait","query":"flowId=; optional accountId/reauth"},{"method":"POST","path":"/api/oauth/login","condition":"kiro with explicit --method; rejects --device, --code, reauth, --open-browser and --add-account","body":"{provider:\"kiro\",method:\"builder-id\"|\"google\"|\"github\"}"},{"method":"GET","path":"/api/oauth/status","condition":"native Kiro method polling unless --no-wait","query":"provider=kiro&flowId="}],"limitations":["openai, codex and chatgpt address the Codex pool. Native main uses the separate account main reauth flow.","Login is a human authorization handoff. Codes, verification material and credentials must not be copied into agent transcripts. Legacy argv code input warns about exposure; use stdin.","No --live flag is implemented. Provider OAuth --id requires --reauth; --method is Kiro add-only.","Omitted options preserve each flow's existing defaults. Explicit browser flags are refused for Codex device, known native-device providers and native Kiro method; add-account is refused for reauth/Codex/Kiro method.","Start/code/poll are pinned; human verification remains required. Only public handoff/flow/state fields are printed. Completed-but-validation/catalog-pending outcomes remain visible and nonzero.","--id on provider OAuth requires --reauth. --add-account is not accepted for Codex or reauth. --open-browser is a browser-flow override and is rejected on device-native methods. --method selects the separate Kiro flow documented in P11."]},{"command":"ocx account reauth","invocation":"ocx account reauth [--id ] [--device] [--code -] [--no-wait] [--open-browser ] [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account reauth --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P10","wp5-discovery","wp5-auth"],"routes":[{"method":"POST","path":"/api/oauth/login","condition":"non-Codex provider OAuth, without Kiro native --method","body":"{provider,addAccount,openBrowser?,accountId?,reauth?}"},{"method":"POST","path":"/api/oauth/login/code","condition":"OAuth --code only","body":"{provider,input}"},{"method":"GET","path":"/api/oauth/status","condition":"OAuth polling unless --no-wait","query":"provider="},{"method":"POST","path":"/api/codex-auth/login","condition":"provider openai/codex/chatgpt","body":"{id?,reauth?,device?,openBrowser?}"},{"method":"POST","path":"/api/codex-auth/login/code","condition":"Codex --code only","body":"{flowId,input}"},{"method":"GET","path":"/api/codex-auth/login-status","condition":"Codex polling unless --no-wait","query":"flowId=; optional accountId/reauth"}],"limitations":["Alias of account login with --reauth. openai, codex and chatgpt address the pool; native main uses account main reauth. Human completes authorization; keep returned login material out of transcripts. Kiro --method cannot be combined with reauth.","Explicit add-account is incompatible with reauth. Browser preference is accepted only by browser-capable flows; device verification remains manual.","--id on provider OAuth requires --reauth. --add-account is not accepted for Codex or reauth. --open-browser is a browser-flow override and is rejected on device-native methods. --method selects the separate Kiro flow documented in P11."]},{"command":"ocx account code","invocation":"ocx account code [--flow ] --code - [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account code --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P10","wp5-discovery","wp5-auth"],"routes":[{"method":"POST","path":"/api/oauth/login/code","condition":"provider OAuth","body":"{provider,input:stdin}"},{"method":"POST","path":"/api/codex-auth/login/code","condition":"Codex; --flow required","body":"{flowId,input:stdin}"}],"limitations":["A short-lived credential is a human handoff. Legacy positional/argv code input exists but warns about exposure; use stdin and never echo it."]},{"command":"ocx account cancel","invocation":"ocx account cancel [--flow ] [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account cancel --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P10","wp5-discovery","wp5-auth"],"routes":[{"method":"POST","path":"/api/oauth/login/cancel","condition":"provider OAuth","body":"{provider,flowId?} (flowId only forwarded for kiro)"},{"method":"POST","path":"/api/codex-auth/login/cancel","condition":"Codex; --flow required","body":"{flowId}"}],"limitations":["Codex cancellation sends flowId. Other providers send provider; Kiro additionally forwards a supplied flowId."]},{"command":"ocx logout","invocation":"ocx logout [--json]","target":"local","authority":"scoped-automation","help":"ocx logout --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P10","wp5-discovery","wp5-auth"],"routes":[],"limitations":["No-live retains atomic local removed/not-found results and exits0/4. Live sends one provider-level OAuth logout and reports only its success receipt, not removed:true or global sign-out.","Live does not sign out Codex/native-main. Target/argument failure never causes local credential removal."],"condition":"omit --live"},{"command":"ocx logout","invocation":"ocx logout --live [--json]","target":"live-management","authority":"scoped-automation","help":"ocx logout --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P10","wp5-discovery","wp5-auth"],"routes":[{"method":"POST","path":"/api/oauth/logout","query":"provider="}],"limitations":["No-live retains atomic local removed/not-found results and exits0/4. Live sends one provider-level OAuth logout and reports only its success receipt, not removed:true or global sign-out.","Live does not sign out Codex/native-main. Target/argument failure never causes local credential removal."],"condition":"explicit --live"}],"sourceRefs":[{"path":"gui/src/pages/use-providers-oauth.ts","line":129,"snapshot":"WP8-ledger-source-snapshot","sha256":"5e0fffcaa6b66d91b5ecd7eb24a40bdf3c6ab030727412c557f14fcb3376422b","anchor":"return fetch(`${apiBase}/api/oauth/login`, {","role":"gui","claim":"use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/pages/use-providers-oauth.ts","line":264,"snapshot":"WP8-ledger-source-snapshot","sha256":"5e0fffcaa6b66d91b5ecd7eb24a40bdf3c6ab030727412c557f14fcb3376422b","anchor":"const res = await fetch(`${apiBase}/api/oauth/logout?provider=${encodeURIComponent(provider)}`, { method: \"POST\" });","role":"gui","claim":"use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":199,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"async function login(argv: string[], deps: RuntimeApiDeps): Promise {","role":"cli","claim":"ocx account login [--id ] [--reauth] [--device] [--method ] [--code -] [--no-wait] [--open-browser ] [--add-account ] [--json] — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":263,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"const start = publicLoginStart(await runtimeRequest(\"/api/codex-auth/login\", {","role":"cli","claim":"ocx account login [--id ] [--reauth] [--device] [--method ] [--code -] [--no-wait] [--open-browser ] [--add-account ] [--json] — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":327,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"const start = publicLoginStart(await runtimeRequest(\"/api/oauth/login\", {","role":"cli","claim":"ocx account login [--id ] [--reauth] [--device] [--method ] [--code -] [--no-wait] [--open-browser ] [--add-account ] [--json] — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":236,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/oauth/login\" && req.method === \"POST\") {","role":"common-owner","claim":"POST /api/oauth/login — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":337,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/oauth/login/code\" && req.method === \"POST\") {","role":"common-owner","claim":"POST /api/oauth/login/code — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":352,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/oauth/status\" && req.method === \"GET\") {","role":"common-owner","claim":"GET /api/oauth/status — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/codex/auth-api/routes.ts","line":494,"snapshot":"WP8-ledger-source-snapshot","sha256":"e1cf949c54f9c63141b5a5316d7f5cc9e14a23df45d03ac5ac0898eead022466","anchor":"return handleCodexAuthLoginStart(req, config, convergeCodexCatalog);","role":"common-owner","claim":"POST /api/codex-auth/login — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/codex/auth-api/routes.ts","line":498,"snapshot":"WP8-ledger-source-snapshot","sha256":"e1cf949c54f9c63141b5a5316d7f5cc9e14a23df45d03ac5ac0898eead022466","anchor":"return handleCodexAuthLoginCode(req);","role":"common-owner","claim":"POST /api/codex-auth/login/code — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/codex/auth-api/routes.ts","line":506,"snapshot":"WP8-ledger-source-snapshot","sha256":"e1cf949c54f9c63141b5a5316d7f5cc9e14a23df45d03ac5ac0898eead022466","anchor":"return handleCodexAuthLoginStatus(req, url, config);","role":"common-owner","claim":"GET /api/codex-auth/login-status — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":485,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"if (sub === \"login\" || sub === \"reauth\") return runCatalogAction(() => login(sub === \"reauth\" ? [...argv, \"--reauth\"] : argv, deps));","role":"cli","claim":"ocx account reauth [--id ] [--device] [--code -] [--no-wait] [--open-browser ] [--json] — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":263,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"const start = publicLoginStart(await runtimeRequest(\"/api/codex-auth/login\", {","role":"cli","claim":"ocx account reauth [--id ] [--device] [--code -] [--no-wait] [--open-browser ] [--json] — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":327,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"const start = publicLoginStart(await runtimeRequest(\"/api/oauth/login\", {","role":"cli","claim":"ocx account reauth [--id ] [--device] [--code -] [--no-wait] [--open-browser ] [--json] — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":363,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"async function code(argv: string[], deps: RuntimeApiDeps): Promise {","role":"cli","claim":"ocx account code [--flow ] --code - [--json] — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":389,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"const path = CODEX_NAMES.has(provider) ? \"/api/codex-auth/login/code\" : \"/api/oauth/login/code\";","role":"cli","claim":"ocx account code [--flow ] --code - [--json] — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":396,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"async function cancel(argv: string[], deps: RuntimeApiDeps): Promise {","role":"cli","claim":"ocx account cancel [--flow ] [--json] — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":407,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"const result = await runtimeRequest(codex ? \"/api/codex-auth/login/cancel\" : \"/api/oauth/login/cancel\", {","role":"cli","claim":"ocx account cancel [--flow ] [--json] — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":321,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/oauth/login/cancel\" && req.method === \"POST\") {","role":"common-owner","claim":"POST /api/oauth/login/cancel — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/codex/auth-api/routes.ts","line":502,"snapshot":"WP8-ledger-source-snapshot","sha256":"e1cf949c54f9c63141b5a5316d7f5cc9e14a23df45d03ac5ac0898eead022466","anchor":"return handleCodexAuthLoginCancel(req);","role":"common-owner","claim":"POST /api/codex-auth/login/cancel — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/logout-command.ts","line":33,"snapshot":"WP8-ledger-source-snapshot","sha256":"8b0599061859d3c47e98c08c9bc0de82d3c8edf978a699b364861f545b15be48","anchor":"const remove = deps.removeCredential ?? (await import(\"../oauth/store\")).removeCredential;","role":"cli","claim":"ocx logout [--json] — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/logout-command.ts","line":34,"snapshot":"WP8-ledger-source-snapshot","sha256":"8b0599061859d3c47e98c08c9bc0de82d3c8edf978a699b364861f545b15be48","anchor":"const outcome = await remove(provider);","role":"cli","claim":"ocx logout [--json] — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/oauth/store.ts","line":1113,"snapshot":"WP8-ledger-source-snapshot","sha256":"39976ad4f878984875e9a1e4e285a0383e97a4ba5e56e38f9eeacd2020209306","anchor":"export async function removeCredential(provider: string): Promise<\"removed\" | \"not-found\"> {","role":"common-owner","claim":"removeCredential is the actual local store owner also invoked by live logout on the server host.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/logout-command.ts","line":20,"snapshot":"WP8-ledger-source-snapshot","sha256":"8b0599061859d3c47e98c08c9bc0de82d3c8edf978a699b364861f545b15be48","anchor":"if (live) {","role":"cli","claim":"ocx logout --live [--json] — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/logout-command.ts","line":24,"snapshot":"WP8-ledger-source-snapshot","sha256":"8b0599061859d3c47e98c08c9bc0de82d3c8edf978a699b364861f545b15be48","anchor":"const data = await runtimeRequest(`/api/oauth/logout?provider=${encodeURIComponent(provider)}`, {","role":"cli","claim":"ocx logout --live [--json] — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":376,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"await removeCredential(provider);","role":"common-owner","claim":"POST /api/oauth/logout — use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/components/provider-workspace/ProviderAuthPanel.tsx","line":301,"snapshot":"WP8-ledger-source-snapshot","sha256":"8a36636e92f849ee514012df6fd287fd8d2406a26312b68b464179d1a63a1602","anchor":"const res = await fetch(`${apiBase}/api/oauth/login/code`, {","role":"gui","claim":"OAuth callback submission sends the user-supplied input through /api/oauth/login/code.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":239,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"const start = publicLoginStart(await runtimeRequest(\"/api/oauth/login\", {","role":"cli","claim":"Native Kiro --method login posts method to the shared OAuth login owner.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":250,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"const state = publicLoginStart(await runtimeRequest(`/api/oauth/status?provider=kiro&flowId=${encodeURIComponent(start.flowId)}`, { redirect: \"error\" }, pinned), true);","role":"cli","claim":"Native Kiro status carries exact flowId.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":258,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"return jsonResponse(await startKiroDeviceLogin(body.method as KiroDeviceMethod, principal ?? \"admin-token\", readConfigDiagnostics().config));","role":"common-owner","claim":"Explicit native method uses startKiroDeviceLogin with admitted principal.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"}],"sourceJoin":{"guiAction":"set-P10 — OAuth login, add another account, reauthenticate, submit callback, cancel, logout","guiRefs":[0,1,26],"cliRefs":[2,3,4,11,12,13,14,15,16,17,20,21,23,24,27,28],"ownerRefs":[5,6,7,8,9,10,18,19,22,25,29],"routeContracts":[{"method":"POST","path":"/api/oauth/login","condition":"non-Codex provider OAuth, without Kiro native --method","body":"{provider,addAccount,openBrowser?,accountId?,reauth?}"},{"method":"POST","path":"/api/oauth/login/code","condition":"OAuth --code only","body":"{provider,input}"},{"method":"GET","path":"/api/oauth/status","condition":"OAuth polling unless --no-wait","query":"provider="},{"method":"POST","path":"/api/codex-auth/login","condition":"provider openai/codex/chatgpt","body":"{id?,reauth?,device?,openBrowser?}"},{"method":"POST","path":"/api/codex-auth/login/code","condition":"Codex --code only","body":"{flowId,input}"},{"method":"GET","path":"/api/codex-auth/login-status","condition":"Codex polling unless --no-wait","query":"flowId=; optional accountId/reauth"},{"method":"POST","path":"/api/oauth/login","condition":"kiro with explicit --method; rejects --device, --code, reauth, --open-browser and --add-account","body":"{provider:\"kiro\",method:\"builder-id\"|\"google\"|\"github\"}"},{"method":"GET","path":"/api/oauth/status","condition":"native Kiro method polling unless --no-wait","query":"provider=kiro&flowId="},{"method":"POST","path":"/api/oauth/login/code","condition":"provider OAuth","body":"{provider,input:stdin}"},{"method":"POST","path":"/api/codex-auth/login/code","condition":"Codex; --flow required","body":"{flowId,input:stdin}"},{"method":"POST","path":"/api/oauth/login/cancel","condition":"provider OAuth","body":"{provider,flowId?} (flowId only forwarded for kiro)"},{"method":"POST","path":"/api/codex-auth/login/cancel","condition":"Codex; --flow required","body":"{flowId}"},{"method":"POST","path":"/api/oauth/logout","query":"provider="}],"argument":"use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store.","workflowJoins":[{"workflowIndex":0,"guiRefs":[0,1,26],"cliRefs":[2,3,4,27,28],"ownerRefs":[5,6,7,8,9,10,29],"argument":"ocx account login [--id ] [--reauth] [--device] [--method ] [--code -] [--no-wait] [--open-browser ] [--add-account ] [--json] selects live-management. use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store."},{"workflowIndex":1,"guiRefs":[0,1,26],"cliRefs":[11,12,13],"ownerRefs":[5,6,7,8,9,10],"argument":"ocx account reauth [--id ] [--device] [--code -] [--no-wait] [--open-browser ] [--json] selects live-management. use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store."},{"workflowIndex":2,"guiRefs":[0,1,26],"cliRefs":[14,15],"ownerRefs":[6,9],"argument":"ocx account code [--flow ] --code - [--json] selects live-management. use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store."},{"workflowIndex":3,"guiRefs":[0,1,26],"cliRefs":[16,17],"ownerRefs":[18,19],"argument":"ocx account cancel [--flow ] [--json] selects live-management. use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store."},{"workflowIndex":4,"guiRefs":[0,1,26],"cliRefs":[20,21],"ownerRefs":[22],"argument":"ocx logout [--json] selects local. use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store."},{"workflowIndex":5,"guiRefs":[0,1,26],"cliRefs":[23,24],"ownerRefs":[25],"argument":"ocx logout --live [--json] selects live-management. use-providers-oauth sends provider/addAccount/accountId/reauth/openBrowser, polls provider status and logs out the selected proxy. account-auth.login builds those fields for OAuth; the Codex spelling branch instead uses id/reauth/device/flowId and the Codex login endpoints. code reads input from stdin and cancel identifies the correct provider or exact Codex flow. --live logout invokes the shared server credential removal/reconciliation; ordinary logout removes only the operating machine credential store."}]},"proofIds":["equivalence-set-P10","wp5-discovery","wp5-auth"],"limitations":["Runtime logout requires --live; no local fallback. Pending catalog exits nonzero; human handles browser/credentials."],"acceptanceBasis":"Corrected immutable per-workflow source equivalence; retained historical proof IDs require the dedicated evidence join and parent current-head publication checks."} + }, + { + "id": "set-P11", + "domain": "settings", + "baseline": "COMPLETE", + "status": "complete", + "unit": "wp2", + "evidence": [ + "022_discovery_verification.md", + "src/cli/capabilities.ts", + "skills/ocx/references/01_management_surface.md" + ], + "task": "set-P11 — Kiro native device login", + "existingCli": "account login kiro --method builder-id\\|google\\|github [--no-wait] --json; account cancel kiro --flow F --json", + "sourceInventory": "004_settings_coverage.md", + "final": {"disposition":"verified","phase":"wp2","pr":"https://github.com/lidge-jun/opencodex/pull/6526","workflows":[{"command":"ocx account login","invocation":"ocx account login kiro --method [--no-wait] [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account login --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P11","wp2-final-suite","wp5-auth","wp8-supplement-kiro"],"routes":[{"method":"POST","path":"/api/oauth/login","body":"{provider:\"kiro\",method}"},{"method":"GET","path":"/api/oauth/status","condition":"unless --no-wait","query":"provider=kiro&flowId="}],"limitations":["openai, codex and chatgpt address the Codex pool. Native main uses the separate account main reauth flow.","Login is a human authorization handoff. Codes, verification material and credentials must not be copied into agent transcripts. Legacy argv code input warns about exposure; use stdin.","No --live flag is implemented. Provider OAuth --id requires --reauth; --method is Kiro add-only.","Omitted options preserve each flow's existing defaults. Explicit browser flags are refused for Codex device, known native-device providers and native Kiro method; add-account is refused for reauth/Codex/Kiro method.","Start/code/poll are pinned; human verification remains required. Only public handoff/flow/state fields are printed. Completed-but-validation/catalog-pending outcomes remain visible and nonzero."]},{"command":"ocx account cancel","invocation":"ocx account cancel kiro --flow [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account cancel --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P11","wp2-final-suite","wp5-auth","wp8-supplement-kiro"],"routes":[{"method":"POST","path":"/api/oauth/login/cancel","body":"{provider:\"kiro\",flowId}"}],"limitations":["Codex cancellation sends flowId. Other providers send provider; Kiro additionally forwards a supplied flowId."]}],"sourceRefs":[{"path":"gui/src/components/use-kiro-device-login.ts","line":88,"snapshot":"WP8-ledger-source-snapshot","sha256":"1b495b5cefc7b0fc9b2f6ee2851870e41f92fcddda39bfb1511dec387ad47def","anchor":"response = await afterOAuthCancellation(apiBase, \"kiro\", () => fetch(`${apiBase}/api/oauth/login`, {","role":"gui","claim":"use-kiro-device-login starts {provider:kiro,method}, retains flowId for status/finalization and sends it on cancellation. account-auth.login --method selects the native Kiro branch, projects the public handoff fields, polls /api/oauth/status with provider=kiro and flowId, and terminates on done/failed/expired/cancelled. The server starts/statuses/cancels Kiro device flows for the same admitted principal; no generic Codex login route participates.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/components/use-kiro-device-login.ts","line":46,"snapshot":"WP8-ledger-source-snapshot","sha256":"1b495b5cefc7b0fc9b2f6ee2851870e41f92fcddda39bfb1511dec387ad47def","anchor":"return await fetch(`${apiBase}/api/oauth/login/cancel`, {","role":"gui","claim":"use-kiro-device-login starts {provider:kiro,method}, retains flowId for status/finalization and sends it on cancellation. account-auth.login --method selects the native Kiro branch, projects the public handoff fields, polls /api/oauth/status with provider=kiro and flowId, and terminates on done/failed/expired/cancelled. The server starts/statuses/cancels Kiro device flows for the same admitted principal; no generic Codex login route participates.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":213,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"if (method !== undefined) {","role":"cli","claim":"ocx account login kiro --method [--no-wait] [--json] — use-kiro-device-login starts {provider:kiro,method}, retains flowId for status/finalization and sends it on cancellation. account-auth.login --method selects the native Kiro branch, projects the public handoff fields, polls /api/oauth/status with provider=kiro and flowId, and terminates on done/failed/expired/cancelled. The server starts/statuses/cancels Kiro device flows for the same admitted principal; no generic Codex login route participates.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":239,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"const start = publicLoginStart(await runtimeRequest(\"/api/oauth/login\", {","role":"cli","claim":"ocx account login kiro --method [--no-wait] [--json] — use-kiro-device-login starts {provider:kiro,method}, retains flowId for status/finalization and sends it on cancellation. account-auth.login --method selects the native Kiro branch, projects the public handoff fields, polls /api/oauth/status with provider=kiro and flowId, and terminates on done/failed/expired/cancelled. The server starts/statuses/cancels Kiro device flows for the same admitted principal; no generic Codex login route participates.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":250,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"const state = publicLoginStart(await runtimeRequest(`/api/oauth/status?provider=kiro&flowId=${encodeURIComponent(start.flowId)}`, { redirect: \"error\" }, pinned), true);","role":"cli","claim":"ocx account login kiro --method [--no-wait] [--json] — use-kiro-device-login starts {provider:kiro,method}, retains flowId for status/finalization and sends it on cancellation. account-auth.login --method selects the native Kiro branch, projects the public handoff fields, polls /api/oauth/status with provider=kiro and flowId, and terminates on done/failed/expired/cancelled. The server starts/statuses/cancels Kiro device flows for the same admitted principal; no generic Codex login route participates.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":236,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/oauth/login\" && req.method === \"POST\") {","role":"common-owner","claim":"POST /api/oauth/login — use-kiro-device-login starts {provider:kiro,method}, retains flowId for status/finalization and sends it on cancellation. account-auth.login --method selects the native Kiro branch, projects the public handoff fields, polls /api/oauth/status with provider=kiro and flowId, and terminates on done/failed/expired/cancelled. The server starts/statuses/cancels Kiro device flows for the same admitted principal; no generic Codex login route participates.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":352,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/oauth/status\" && req.method === \"GET\") {","role":"common-owner","claim":"GET /api/oauth/status — use-kiro-device-login starts {provider:kiro,method}, retains flowId for status/finalization and sends it on cancellation. account-auth.login --method selects the native Kiro branch, projects the public handoff fields, polls /api/oauth/status with provider=kiro and flowId, and terminates on done/failed/expired/cancelled. The server starts/statuses/cancels Kiro device flows for the same admitted principal; no generic Codex login route participates.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":258,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"return jsonResponse(await startKiroDeviceLogin(body.method as KiroDeviceMethod, principal ?? \"admin-token\", readConfigDiagnostics().config));","role":"common-owner","claim":"Native method branch calls startKiroDeviceLogin.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":358,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"const status = await statusKiroDeviceLogin(flowId, principal ?? \"admin-token\");","role":"common-owner","claim":"Native flow status is tied to flowId and principal.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":396,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"async function cancel(argv: string[], deps: RuntimeApiDeps): Promise {","role":"cli","claim":"ocx account cancel kiro --flow [--json] — use-kiro-device-login starts {provider:kiro,method}, retains flowId for status/finalization and sends it on cancellation. account-auth.login --method selects the native Kiro branch, projects the public handoff fields, polls /api/oauth/status with provider=kiro and flowId, and terminates on done/failed/expired/cancelled. The server starts/statuses/cancels Kiro device flows for the same admitted principal; no generic Codex login route participates.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-auth.ts","line":407,"snapshot":"WP8-ledger-source-snapshot","sha256":"6a1c6809dc5172b4de19653124cee69a6e1a721c29331faf41ae7d3da601cdc6","anchor":"const result = await runtimeRequest(codex ? \"/api/codex-auth/login/cancel\" : \"/api/oauth/login/cancel\", {","role":"cli","claim":"ocx account cancel kiro --flow [--json] — use-kiro-device-login starts {provider:kiro,method}, retains flowId for status/finalization and sends it on cancellation. account-auth.login --method selects the native Kiro branch, projects the public handoff fields, polls /api/oauth/status with provider=kiro and flowId, and terminates on done/failed/expired/cancelled. The server starts/statuses/cancels Kiro device flows for the same admitted principal; no generic Codex login route participates.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":321,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/oauth/login/cancel\" && req.method === \"POST\") {","role":"common-owner","claim":"POST /api/oauth/login/cancel — use-kiro-device-login starts {provider:kiro,method}, retains flowId for status/finalization and sends it on cancellation. account-auth.login --method selects the native Kiro branch, projects the public handoff fields, polls /api/oauth/status with provider=kiro and flowId, and terminates on done/failed/expired/cancelled. The server starts/statuses/cancels Kiro device flows for the same admitted principal; no generic Codex login route participates.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"}],"sourceJoin":{"guiAction":"set-P11 — Kiro native device login","guiRefs":[0,1],"cliRefs":[2,3,4,9,10],"ownerRefs":[5,6,7,8,11],"routeContracts":[{"method":"POST","path":"/api/oauth/login","body":"{provider:\"kiro\",method}"},{"method":"GET","path":"/api/oauth/status","condition":"unless --no-wait","query":"provider=kiro&flowId="},{"method":"POST","path":"/api/oauth/login/cancel","body":"{provider:\"kiro\",flowId}"}],"argument":"use-kiro-device-login starts {provider:kiro,method}, retains flowId for status/finalization and sends it on cancellation. account-auth.login --method selects the native Kiro branch, projects the public handoff fields, polls /api/oauth/status with provider=kiro and flowId, and terminates on done/failed/expired/cancelled. The server starts/statuses/cancels Kiro device flows for the same admitted principal; no generic Codex login route participates.","workflowJoins":[{"workflowIndex":0,"guiRefs":[0,1],"cliRefs":[2,3,4],"ownerRefs":[5,6,7,8],"argument":"ocx account login kiro --method [--no-wait] [--json] selects live-management. use-kiro-device-login starts {provider:kiro,method}, retains flowId for status/finalization and sends it on cancellation. account-auth.login --method selects the native Kiro branch, projects the public handoff fields, polls /api/oauth/status with provider=kiro and flowId, and terminates on done/failed/expired/cancelled. The server starts/statuses/cancels Kiro device flows for the same admitted principal; no generic Codex login route participates."},{"workflowIndex":1,"guiRefs":[0,1],"cliRefs":[9,10],"ownerRefs":[11],"argument":"ocx account cancel kiro --flow [--json] selects live-management. use-kiro-device-login starts {provider:kiro,method}, retains flowId for status/finalization and sends it on cancellation. account-auth.login --method selects the native Kiro branch, projects the public handoff fields, polls /api/oauth/status with provider=kiro and flowId, and terminates on done/failed/expired/cancelled. The server starts/statuses/cancels Kiro device flows for the same admitted principal; no generic Codex login route participates."}]},"proofIds":["equivalence-set-P11","wp2-final-suite","wp5-auth","wp8-supplement-kiro"],"limitations":["Specify kiro and supported --method; cancel uses observed --flow. No real OAuth success is claimed."],"acceptanceBasis":"Corrected immutable per-workflow source equivalence; retained historical proof IDs require the dedicated evidence join and parent current-head publication checks."} + }, + { + "id": "set-P12", + "domain": "settings", + "baseline": "DISCOVERY_ONLY", + "status": "complete", + "unit": "wp2", + "evidence": [ + "022_discovery_verification.md", + "src/cli/capabilities.ts", + "skills/ocx/references/01_management_surface.md" + ], + "task": "set-P12 — Manage OAuth account roster", + "existingCli": "account list/current/use P I; account alias P I TEXT\\|-; account pause/resume P I; account remove P I --yes; all support --json", + "sourceInventory": "004_settings_coverage.md", + "final": {"disposition":"verified","phase":"wp2","pr":"https://github.com/lidge-jun/opencodex/pull/6526","workflows":[{"command":"ocx account list","invocation":"ocx account list [--quota [--refresh]] [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account list --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P12","wp2-final-suite"],"routes":[{"method":"GET","path":"/api/oauth/accounts","query":"provider=; quota=1 only with --quota; refresh=1 only with both quota and refresh"}],"limitations":["Default listing is observational. --quota can probe provider quotas; --refresh is only acted on together with --quota. A refresh observation is not pending-account validation consent.","This row supplies a provider explicitly and selects only its account family; JSON is {accounts,notes}."]},{"command":"ocx account current","invocation":"ocx account current [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account current --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P12","wp2-final-suite"],"routes":[{"method":"GET","path":"/api/oauth/accounts","query":"provider="}],"limitations":["Uses the live route for the configured account family; this does not report the physical native-main profile."]},{"command":"ocx account use","invocation":"ocx account use [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account use --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P12","wp2-final-suite"],"routes":[{"method":"PUT","path":"/api/oauth/accounts/active","body":"{provider,accountId}"}],"limitations":["This family passes the exact account/key ID to its live selection route. Codex main/auto aliases do not apply."]},{"command":"ocx account alias","invocation":"ocx account alias [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account alias --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P12","wp2-final-suite"],"routes":[{"method":"PUT","path":"/api/oauth/accounts/alias","body":"{provider,accountId,alias}"}],"limitations":["This family takes an exact ID; - clears the alias, and aliases are limited to 80 printable characters."]},{"command":"ocx account pause","invocation":"ocx account pause [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account pause --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P12","wp2-final-suite"],"routes":[{"method":"GET","path":"/api/oauth/accounts","condition":"selector resolution","query":"provider="},{"method":"PUT","path":"/api/oauth/accounts/pause","body":"{provider,accountId,paused:true}"}],"limitations":["Codex pause unbinds pinned threads and selects a fallback when possible; with no fallback, a paused-but-selected Codex account still receives requests. Anthropic and generic OAuth pause exclude the account from new requests, failover and refresh, and an all-paused pool answers 403. Credentials and health are preserved; already-sent turns are not cancelled."]},{"command":"ocx account resume","invocation":"ocx account resume [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account resume --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P12","wp2-final-suite"],"routes":[{"method":"GET","path":"/api/oauth/accounts","condition":"selector resolution","query":"provider="},{"method":"PUT","path":"/api/oauth/accounts/pause","body":"{provider,accountId,paused:false}"}],"limitations":[]},{"command":"ocx account remove","invocation":"ocx account remove --yes [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account remove --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P12","wp2-final-suite"],"routes":[{"method":"GET","path":"/api/oauth/accounts","query":"provider=; quota=1 only with --quota; refresh=1 only with both quota and refresh"},{"method":"DELETE","path":"/api/oauth/accounts","query":"provider=&id="}],"limitations":["This family takes an exact ID and requires --yes. It reads the roster before and after deletion; a failed post-delete read means deletion may already have succeeded."]}],"sourceRefs":[{"path":"gui/src/hooks/useProviderAccountPools.ts","line":210,"snapshot":"WP8-ledger-source-snapshot","sha256":"aad9433a78ea30dc8426b227cc80b0b6bf2687688ab6b8d30c9b041f3ffa554f","anchor":"const url = `${apiBase}/api/oauth/accounts?provider=${encodeURIComponent(provider)}`;","role":"gui","claim":"useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/hooks/useProviderAccountPools.ts","line":399,"snapshot":"WP8-ledger-source-snapshot","sha256":"aad9433a78ea30dc8426b227cc80b0b6bf2687688ab6b8d30c9b041f3ffa554f","anchor":"const res = await fetch(`${apiBase}/api/oauth/accounts/active`, { method: \"PUT\", headers: { \"Content-Type\": \"application/json\" }, body: JSON.stringify({ provider, accountId: account.id }) });","role":"gui","claim":"useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/hooks/useProviderAccountPools.ts","line":492,"snapshot":"WP8-ledger-source-snapshot","sha256":"aad9433a78ea30dc8426b227cc80b0b6bf2687688ab6b8d30c9b041f3ffa554f","anchor":"const res = await fetch(`${apiBase}/api/oauth/accounts/pause`, {","role":"gui","claim":"useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"gui/src/hooks/useProviderAccountPools.ts","line":628,"snapshot":"WP8-ledger-source-snapshot","sha256":"aad9433a78ea30dc8426b227cc80b0b6bf2687688ab6b8d30c9b041f3ffa554f","anchor":"const response = await fetch(type === \"oauth\" ? `${apiBase}/api/oauth/accounts/alias` : `${apiBase}/api/providers/keys/alias`, {","role":"gui","claim":"useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account.ts","line":179,"snapshot":"WP8-ledger-source-snapshot","sha256":"7f35085b33227f080dfe88c40824435923336d15f28a05869226c49bf5c1f0b8","anchor":"async function cmdList(rest: string[], deps: AccountDeps): Promise {","role":"cli","claim":"ocx account list [--quota [--refresh]] [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account.ts","line":229,"snapshot":"WP8-ledger-source-snapshot","sha256":"7f35085b33227f080dfe88c40824435923336d15f28a05869226c49bf5c1f0b8","anchor":"const r = await fetchRows(deps, baseUrl, t.name, t.type, wantsQuota ? { refresh: refreshQuota } : undefined);","role":"cli","claim":"ocx account list [--quota [--refresh]] [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":456,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"if (type === \"codex\") return fetchCodexRows(deps, baseUrl, Boolean(quota?.refresh), quota !== undefined);","role":"cli","claim":"ocx account list [--quota [--refresh]] [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":379,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"const query = quota","role":"cli","claim":"ocx account list [--quota [--refresh]] [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":389,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"const rows = accounts.map((a, i) => ({","role":"cli","claim":"ocx account list [--quota [--refresh]] [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":398,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/oauth/accounts\" && req.method === \"GET\") {","role":"common-owner","claim":"GET /api/oauth/accounts — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account.ts","line":271,"snapshot":"WP8-ledger-source-snapshot","sha256":"7f35085b33227f080dfe88c40824435923336d15f28a05869226c49bf5c1f0b8","anchor":"async function cmdCurrent(rest: string[], deps: AccountDeps): Promise {","role":"cli","claim":"ocx account current [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account.ts","line":288,"snapshot":"WP8-ledger-source-snapshot","sha256":"7f35085b33227f080dfe88c40824435923336d15f28a05869226c49bf5c1f0b8","anchor":"const r = await fetchRows(deps, baseUrl, name, c.type);","role":"cli","claim":"ocx account current [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":379,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"const query = quota","role":"cli","claim":"ocx account current [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-api.ts","line":389,"snapshot":"WP8-ledger-source-snapshot","sha256":"b9200216e722b8ae68efe17c04926cc0b5530939232a5c134ccc66ffb98c8791","anchor":"const rows = accounts.map((a, i) => ({","role":"cli","claim":"ocx account current [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account.ts","line":340,"snapshot":"WP8-ledger-source-snapshot","sha256":"7f35085b33227f080dfe88c40824435923336d15f28a05869226c49bf5c1f0b8","anchor":"} else if (c.type === \"oauth\") {","role":"cli","claim":"ocx account use [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account.ts","line":342,"snapshot":"WP8-ledger-source-snapshot","sha256":"7f35085b33227f080dfe88c40824435923336d15f28a05869226c49bf5c1f0b8","anchor":"res = await apiJson(deps, baseUrl, \"PUT\", \"/api/oauth/accounts/active\", { provider: name, accountId: id });","role":"cli","claim":"ocx account use [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":472,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/oauth/accounts/active\" && req.method === \"PUT\") {","role":"common-owner","claim":"PUT /api/oauth/accounts/active — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-extended.ts","line":1064,"snapshot":"WP8-ledger-source-snapshot","sha256":"d05cdce1acbc47d1a7d1611f28e1b727b459043fd140300956e2e00a16f11ff7","anchor":"export async function cmdAlias(args: string[], deps: AccountDeps): Promise {","role":"cli","claim":"ocx account alias [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-extended.ts","line":1087,"snapshot":"WP8-ledger-source-snapshot","sha256":"d05cdce1acbc47d1a7d1611f28e1b727b459043fd140300956e2e00a16f11ff7","anchor":"const path = classified.type === \"codex\"","role":"cli","claim":"ocx account alias [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-extended.ts","line":1097,"snapshot":"WP8-ledger-source-snapshot","sha256":"d05cdce1acbc47d1a7d1611f28e1b727b459043fd140300956e2e00a16f11ff7","anchor":"const response = await apiJson(deps, baseUrl, \"PUT\", path, body);","role":"cli","claim":"ocx account alias [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":884,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/oauth/accounts/alias\" && req.method === \"PUT\") {","role":"common-owner","claim":"PUT /api/oauth/accounts/alias — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account.ts","line":432,"snapshot":"WP8-ledger-source-snapshot","sha256":"7f35085b33227f080dfe88c40824435923336d15f28a05869226c49bf5c1f0b8","anchor":"if (sub === \"pause\") return await cmdPause(rest, deps, true);","role":"cli","claim":"ocx account pause [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-extended.ts","line":821,"snapshot":"WP8-ledger-source-snapshot","sha256":"d05cdce1acbc47d1a7d1611f28e1b727b459043fd140300956e2e00a16f11ff7","anchor":"const response = await apiJson(deps, baseUrl, \"PUT\", \"/api/oauth/accounts/pause\", {","role":"cli","claim":"ocx account pause [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":508,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/oauth/accounts/pause\" && req.method === \"PUT\") {","role":"common-owner","claim":"PUT /api/oauth/accounts/pause — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account.ts","line":433,"snapshot":"WP8-ledger-source-snapshot","sha256":"7f35085b33227f080dfe88c40824435923336d15f28a05869226c49bf5c1f0b8","anchor":"if (sub === \"resume\") return await cmdPause(rest, deps, false);","role":"cli","claim":"ocx account resume [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-extended.ts","line":821,"snapshot":"WP8-ledger-source-snapshot","sha256":"d05cdce1acbc47d1a7d1611f28e1b727b459043fd140300956e2e00a16f11ff7","anchor":"const response = await apiJson(deps, baseUrl, \"PUT\", \"/api/oauth/accounts/pause\", {","role":"cli","claim":"ocx account resume [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-extended.ts","line":438,"snapshot":"WP8-ledger-source-snapshot","sha256":"d05cdce1acbc47d1a7d1611f28e1b727b459043fd140300956e2e00a16f11ff7","anchor":"export async function cmdRemove(args: string[], deps: AccountDeps): Promise {","role":"cli","claim":"ocx account remove --yes [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-extended.ts","line":473,"snapshot":"WP8-ledger-source-snapshot","sha256":"d05cdce1acbc47d1a7d1611f28e1b727b459043fd140300956e2e00a16f11ff7","anchor":"const before = await fetchRows(deps, baseUrl, name, classified.type);","role":"cli","claim":"ocx account remove --yes [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-extended.ts","line":479,"snapshot":"WP8-ledger-source-snapshot","sha256":"d05cdce1acbc47d1a7d1611f28e1b727b459043fd140300956e2e00a16f11ff7","anchor":"const response = await apiJson(deps, baseUrl, \"DELETE\", deletePath(classified.type, name, id));","role":"cli","claim":"ocx account remove --yes [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-extended.ts","line":484,"snapshot":"WP8-ledger-source-snapshot","sha256":"d05cdce1acbc47d1a7d1611f28e1b727b459043fd140300956e2e00a16f11ff7","anchor":"const after = await fetchRows(deps, baseUrl, name, classified.type);","role":"cli","claim":"ocx account remove --yes [--json] — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":898,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"if (url.pathname === \"/api/oauth/accounts\" && req.method === \"DELETE\") {","role":"common-owner","claim":"DELETE /api/oauth/accounts — useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"}],"sourceJoin":{"guiAction":"set-P12 — Manage OAuth account roster","guiRefs":[0,1,2,3],"cliRefs":[4,5,6,7,8,10,11,12,13,14,15,17,18,19,21,22,24,25,26,27,28,29],"ownerRefs":[9,16,20,23,30],"routeContracts":[{"method":"GET","path":"/api/oauth/accounts","query":"provider=; quota=1 only with --quota; refresh=1 only with both quota and refresh"},{"method":"GET","path":"/api/oauth/accounts","query":"provider="},{"method":"PUT","path":"/api/oauth/accounts/active","body":"{provider,accountId}"},{"method":"PUT","path":"/api/oauth/accounts/alias","body":"{provider,accountId,alias}"},{"method":"GET","path":"/api/oauth/accounts","condition":"selector resolution","query":"provider="},{"method":"PUT","path":"/api/oauth/accounts/pause","body":"{provider,accountId,paused:true}"},{"method":"PUT","path":"/api/oauth/accounts/pause","body":"{provider,accountId,paused:false}"},{"method":"DELETE","path":"/api/oauth/accounts","query":"provider=&id="}],"argument":"useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row.","workflowJoins":[{"workflowIndex":0,"guiRefs":[0,1,2,3],"cliRefs":[4,5,6,7,8],"ownerRefs":[9],"argument":"ocx account list [--quota [--refresh]] [--json] selects live-management. useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row."},{"workflowIndex":1,"guiRefs":[0,1,2,3],"cliRefs":[10,11,12,13],"ownerRefs":[9],"argument":"ocx account current [--json] selects live-management. useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row."},{"workflowIndex":2,"guiRefs":[0,1,2,3],"cliRefs":[14,15],"ownerRefs":[16],"argument":"ocx account use [--json] selects live-management. useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row."},{"workflowIndex":3,"guiRefs":[0,1,2,3],"cliRefs":[17,18,19],"ownerRefs":[20],"argument":"ocx account alias [--json] selects live-management. useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row."},{"workflowIndex":4,"guiRefs":[0,1,2,3],"cliRefs":[21,22],"ownerRefs":[9,23],"argument":"ocx account pause [--json] selects live-management. useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row."},{"workflowIndex":5,"guiRefs":[0,1,2,3],"cliRefs":[24,25],"ownerRefs":[9,23],"argument":"ocx account resume [--json] selects live-management. useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row."},{"workflowIndex":6,"guiRefs":[0,1,2,3],"cliRefs":[26,27,28,29],"ownerRefs":[9,30],"argument":"ocx account remove --yes [--json] selects live-management. useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row."}]},"proofIds":["equivalence-set-P12","wp2-final-suite"],"limitations":["useProviderAccountPools reads OAuth accounts with provider selector and manages active ID, printable alias, pause boolean and deletion. cmdList/cmdCurrent use fetchOAuthRows; cmdUse sends {provider,accountId}; cmdAlias sends {provider,accountId,alias}; cmdPause resolves an exact ID or unique alias then sends {provider,accountId,paused}; cmdRemove checks the roster, DELETEs provider/id and rereads it. All hit the same OAuth account handlers; main/auto and Codex-only selectors are not promised for this row."],"acceptanceBasis":"Corrected immutable per-workflow source equivalence; retained historical proof IDs require the dedicated evidence join and parent current-head publication checks."} + }, + { + "id": "set-P13", + "domain": "settings", + "baseline": "DISCOVERY_ONLY", + "status": "complete", + "unit": "wp2", + "evidence": [ + "022_discovery_verification.md", + "src/cli/capabilities.ts", + "skills/ocx/references/01_management_surface.md" + ], + "task": "set-P13 — Import Antigravity accounts", + "existingCli": "account import google-antigravity --format cockpit-tools --file F\\|--stdin --json", + "sourceInventory": "004_settings_coverage.md", + "final": {"disposition":"verified","phase":"wp2","pr":"https://github.com/lidge-jun/opencodex/pull/6526","workflows":[{"command":"ocx account import","invocation":"ocx account import google-antigravity --format cockpit-tools (--file |--stdin) [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account import --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P13","wp2-final-suite","wp8-supplement-import"],"routes":[{"method":"POST","path":"/api/oauth/accounts/import","body":"{provider:\"google-antigravity\",format:\"cockpit-tools\",document}"}],"limitations":["Human-controlled credential handoff: choose exactly one source. Unsupported providers/formats are refused before source I/O. Partial failures or unsupported records return nonzero; do not echo the document."]}],"sourceRefs":[{"path":"gui/src/components/provider-workspace/ProviderAuthPanel.tsx","line":358,"snapshot":"WP8-ledger-source-snapshot","sha256":"8a36636e92f849ee514012df6fd287fd8d2406a26312b68b464179d1a63a1602","anchor":"const response = await fetch(`${apiBase}/api/oauth/accounts/import`, {","role":"gui","claim":"ProviderAuthPanel parses the supplied Cockpit Tools document and POSTs {provider:google-antigravity,format:cockpit-tools,document}. cmdImport admits only that provider/format and exactly one bounded file/stdin source, parses JSON, posts the same body to importAccounts, and validates a safe result projection. The common import handler bounds/parses the request and reconciles live account/cache state only when the import changed it; source equivalence is separate from accepted-import execution proof.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-extended.ts","line":541,"snapshot":"WP8-ledger-source-snapshot","sha256":"d05cdce1acbc47d1a7d1611f28e1b727b459043fd140300956e2e00a16f11ff7","anchor":"export async function cmdImport(args: string[], deps: AccountDeps): Promise {","role":"cli","claim":"ocx account import google-antigravity --format cockpit-tools (--file |--stdin) [--json] — ProviderAuthPanel parses the supplied Cockpit Tools document and POSTs {provider:google-antigravity,format:cockpit-tools,document}. cmdImport admits only that provider/format and exactly one bounded file/stdin source, parses JSON, posts the same body to importAccounts, and validates a safe result projection. The common import handler bounds/parses the request and reconciles live account/cache state only when the import changed it; source equivalence is separate from accepted-import execution proof.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-extended.ts","line":595,"snapshot":"WP8-ledger-source-snapshot","sha256":"d05cdce1acbc47d1a7d1611f28e1b727b459043fd140300956e2e00a16f11ff7","anchor":"response = await apiJson(deps, baseUrl, \"POST\", \"/api/oauth/accounts/import\", {","role":"cli","claim":"ocx account import google-antigravity --format cockpit-tools (--file |--stdin) [--json] — ProviderAuthPanel parses the supplied Cockpit Tools document and POSTs {provider:google-antigravity,format:cockpit-tools,document}. cmdImport admits only that provider/format and exactly one bounded file/stdin source, parses JSON, posts the same body to importAccounts, and validates a safe result projection. The common import handler bounds/parses the request and reconciles live account/cache state only when the import changed it; source equivalence is separate from accepted-import execution proof.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/cli/account-extended.ts","line":616,"snapshot":"WP8-ledger-source-snapshot","sha256":"d05cdce1acbc47d1a7d1611f28e1b727b459043fd140300956e2e00a16f11ff7","anchor":"const result = safeImportResult(response.json);","role":"cli","claim":"ocx account import google-antigravity --format cockpit-tools (--file |--stdin) [--json] — ProviderAuthPanel parses the supplied Cockpit Tools document and POSTs {provider:google-antigravity,format:cockpit-tools,document}. cmdImport admits only that provider/format and exactly one bounded file/stdin source, parses JSON, posts the same body to importAccounts, and validates a safe result projection. The common import handler bounds/parses the request and reconciles live account/cache state only when the import changed it; source equivalence is separate from accepted-import execution proof.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"},{"path":"src/server/management/oauth-account-routes.ts","line":858,"snapshot":"WP8-ledger-source-snapshot","sha256":"9c7446ad100634fc3e379d3353d239e84000b90810f49f7662e64d323c87b7e8","anchor":"const imported = await importAccounts({","role":"common-owner","claim":"POST /api/oauth/accounts/import — ProviderAuthPanel parses the supplied Cockpit Tools document and POSTs {provider:google-antigravity,format:cockpit-tools,document}. cmdImport admits only that provider/format and exactly one bounded file/stdin source, parses JSON, posts the same body to importAccounts, and validates a safe result projection. The common import handler bounds/parses the request and reconciles live account/cache state only when the import changed it; source equivalence is separate from accepted-import execution proof.","revision":"49b0f34e0cb9fbfc22c48b170cc20b13dbcf0073"}],"sourceJoin":{"guiAction":"set-P13 — Import Antigravity accounts","guiRefs":[0],"cliRefs":[1,2,3],"ownerRefs":[4],"routeContracts":[{"method":"POST","path":"/api/oauth/accounts/import","body":"{provider:\"google-antigravity\",format:\"cockpit-tools\",document}"}],"argument":"ProviderAuthPanel parses the supplied Cockpit Tools document and POSTs {provider:google-antigravity,format:cockpit-tools,document}. cmdImport admits only that provider/format and exactly one bounded file/stdin source, parses JSON, posts the same body to importAccounts, and validates a safe result projection. The common import handler bounds/parses the request and reconciles live account/cache state only when the import changed it; source equivalence is separate from accepted-import execution proof.","workflowJoins":[{"workflowIndex":0,"guiRefs":[0],"cliRefs":[1,2,3],"ownerRefs":[4],"argument":"ocx account import google-antigravity --format cockpit-tools (--file |--stdin) [--json] selects live-management. ProviderAuthPanel parses the supplied Cockpit Tools document and POSTs {provider:google-antigravity,format:cockpit-tools,document}. cmdImport admits only that provider/format and exactly one bounded file/stdin source, parses JSON, posts the same body to importAccounts, and validates a safe result projection. The common import handler bounds/parses the request and reconciles live account/cache state only when the import changed it; source equivalence is separate from accepted-import execution proof."}]},"proofIds":["equivalence-set-P13","wp2-final-suite","wp8-supplement-import"],"limitations":["Only google-antigravity cockpit-tools import; credential files/stdin remain human-controlled."],"acceptanceBasis":"Corrected immutable per-workflow source equivalence; retained historical proof IDs require the dedicated evidence join and parent current-head publication checks."} + }, + { + "id": "set-P14", + "domain": "settings", + "baseline": "DISCOVERY_ONLY", + "status": "complete", + "unit": "wp2", + "evidence": [ + "022_discovery_verification.md", + "src/cli/capabilities.ts", + "skills/ocx/references/01_management_surface.md" + ], + "task": "set-P14 — Manage API-key pool", + "existingCli": "account list/use P I; account add-key P [--label L] (stdin); account alias P I TEXT\\|-; account remove P I --yes --json", + "sourceInventory": "004_settings_coverage.md", + "final": {"disposition":"verified","phase":"wp8","pr":"https://github.com/lidge-jun/opencodex/pull/6545","workflows":[{"command":"ocx account list","invocation":"ocx account list [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account list --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P14","wp2-final-suite"],"routes":[{"method":"GET","path":"/api/providers/keys","query":"name="}],"limitations":["Default listing is observational. --quota can probe provider quotas; --refresh is only acted on together with --quota. A refresh observation is not pending-account validation consent.","This row supplies a provider explicitly and selects only its account family; JSON is {accounts,notes}."]},{"command":"ocx account use","invocation":"ocx account use [--json]","target":"live-management","authority":"scoped-automation","help":"ocx account use --help","references":[{"path":"skills/ocx/SKILL.md"}],"proofIds":["equivalence-set-P14","wp2-final-suite"],"routes":[{"method":"PUT","path":"/api/providers/keys/active","body":"{name,id}"}],"limitations":["This family passes the exact account/key ID to its live selection route. Codex main/auto aliases do not apply."]},{"command":"ocx account add-key","invocation":"ocx account add-key [--label