Skip to content

feat(runtime): retire the Claude subscription OAuth provider - #3183

Open
Joob1n wants to merge 9 commits into
apache:mainfrom
Joob1n:feat/retire-claude-subscription-oauth
Open

feat(runtime): retire the Claude subscription OAuth provider#3183
Joob1n wants to merge 9 commits into
apache:mainfrom
Joob1n:feat/retire-claude-subscription-oauth

Conversation

@Joob1n

@Joob1n Joob1n commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Maka could sign in with a Claude Pro/Max subscription and send inference through it. Anthropic's Consumer Terms permit programmatic access to the consumer Claude services only through an API key or explicit permission, and this path had neither — it presented itself as Claude Code (borrowing that client_id, its User-Agent, its beta header set, and an x-app: cli marker) to get requests accepted. The account carrying that risk is the user's, not Maka's.

This removes the capability rather than gating it further.

claude-subscription keeps its registry entry, and the entry is marked retired — deliberately distinct from a provider that was never wired, since both end up with no Runtime adapter but only one of them used to work and needs to explain itself.

Keeping it registered is a product choice, not a technical necessity. Unregistering is possible: isRetiredGeminiCliConnection is the precedent, filtering by raw providerType string before canonical decode. It is avoided here because a user who signed in has a credential on this machine, and the connection is what makes that visible and deletable — unregister it and onboarding reports needs_connection, with the credential still on disk and nothing pointing at it. (decodeProviderType does throw on unknown types and connection-catalog-codec.ts would fail the whole document, so unregistering without the filtering precedent would cost the user every other connection — but that is a reason to use the precedent, not a reason the choice was forced.)

Retirement is refused at each authority that could otherwise admit the connection, so no single revert makes it sendable again:

Authority Behavior
deriveProviderAuthContract every action hidden — this is what makes the storage layer refuse a model fetch or a connection test
resolveExecutionConnection returns provider_retired, a first-class result, so a retained credential can no longer read as ready
SessionCatalogCoordinator refuses before a Session is committed — the path Bot, CLI and scheduled runs take, which never reaches the Desktop readiness gate
isConnectionReady provider_retired, before a Desktop send is admitted
buildConnectionModelCatalogEntries every model resolves to provider_removed, so the pickers stop offering them
resolveModelRuntime refuses ahead of the per-model override lookup, so a generated override row cannot hand a retired provider a working adapter
storage interactive-login allow list no longer names it
OAUTH_LOGIN_PROVIDERS (Host wire) no longer names it
getAIModel throws, as the last backstop

Loading the catalog also releases a defaultTarget that points at a retained retired connection, so new Sessions stop defaulting to something that cannot run. Settings no longer shows a 默认 Badge on one either, since retirement is checked before both default states — a Badge with no control to move the default off it would be exactly the read-only label that slot exists to avoid.

Settings explains the state instead of pointing at a sign-in that no longer exists, and stops offering "set as default" and "test connection". Onboarding gets its own all_connections_retired copy — the generic blocked text tells the user to re-check credentials, sign-in status, and network, and for a retired provider all three lead nowhere. Deleting the connection is what clears the credential this machine still holds.

The impersonation code goes with it: the cloaked request builder, the Claude token endpoint and its client identity, the cloaked model-fetch headers, and the subscription usage/quota path that needed that same identity to read.

RUNTIME_HOST_COMPATIBILITY_EPOCH moves 25 → 26 (main reached 25 while this PR was in review, so the rebase re-took the next slot; the description tracks the actual diff). This PR changes the wire contract (removes claude-subscription from OAUTH_LOGIN_PROVIDERS, deletes the oauth.account.usage.fetch operation), so it takes its own epoch rather than riding one raised elsewhere. An earlier revision of this description claimed a 22 → 23 bump that a rebase had turned into a no-op; that was wrong, and @Astro-Han and @hqhq1025 both caught it.

Refs #3143

Verification

Built from clean (npm run clean first) — an incremental build hid a real error during this work, so every gate below was re-run after a full rebuild.

  • npm run build, npm run typecheck — 0 errors
  • lint, format:check, astryx:surface-inventory, check:stale, check:third-party-notices, check:windows-cargo-notices — pass
  • npm test — the failing set is identical to the same suite on a clean origin/main worktree on this machine (environment-related ripgrep / PTY / filesystem-worker cases). Diffed programmatically rather than eyeballed.

Each enforcement point was mutation-checked: reverting it individually turns a test red.

Reverted Result
runtimeAdapter: 'unavailable' red
retired: true red
providerAvailable retirement branch red
auth-contract retirement branch red
resolveExecutionConnection refusal red
Session admission refusal red — 2 cases (default target, explicit target)
defaultTarget release red
OAUTH_LOGIN_PROVIDERS red
isConnectionReady gate red

One correction to an earlier revision of this table: the storage interactive-login allow-list row does not discriminate on its own — reverting only that line leaves the suite green, because the auth-contract gate independently produces the same refusal. That is defense in depth working as intended, but the row overstated its own power and @Astro-Han was right to flag it.

End-to-end, against a real claude-subscription connection already stored on this machine (packaged app, installed, launched) — see the before/after screenshots below.

Not run: Windows and Linux packaging, and the Playwright E2E suite. The new retired UI states have no Storybook stories.

Breaking change

A user signed in through the Claude subscription can no longer send with it. The connection and its stored credential are left in place and readable so the state is explainable; an Anthropic API key connection is the replacement path. Clients built against an earlier epoch are rejected at the handshake by the bump above.

Follow-up

The request_authorization_code / paste-code presentation has no producer left once this lands, and both @M4n5ter and @hqhq1025 asked for its removal. It is the immediate next PR rather than part of this one; the exact file-by-file scope, what stays because the live device flows import it, and why the unused #exchangeCode field is deliberately left in place here are in this comment.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Claude Code — drafted the removal, the enforcement points and their tests, and ran the verification described above. Reviewed and submitted by the contributor of record. Generated-by: Claude Code is on the commit.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No
retire-claude-subscription-before-after

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

What problem this solves

This PR retires the claude-subscription OAuth provider. It blocks authentication, model discovery, inference, testing, and quota operations through Claude Pro/Max subscriptions.

The provider remains registered as retired. Stored connections remain decodable, visible, and deletable. Users must delete retired connections to remove stored credentials. Anthropic API keys remain the supported replacement.

Source of truth

The PR extends the existing provider registry, readiness, authentication-contract, onboarding, storage-policy, runtime, and UI paths. It does not create a parallel retirement path.

The provider registry is the source of truth for retirement. Runtime and UI behavior derive from that state.

Scope and necessity

This is the smallest coherent solution for provider retirement:

  • It retains the registry entry to preserve stored-connection decoding.
  • It blocks all authentication and execution entry points.
  • It removes Claude-specific token, impersonation, model-fetch, cloaking, usage, and quota code.
  • It updates settings, onboarding, error copy, and model status.
  • It adds contract and regression coverage for retained retired connections.

The removed settings component, usage module, bridge methods, protocol operations, provider-specific helpers, fixtures, and stale export cannot be removed further without weakening compatibility or regression coverage.

The request_authorization_code cleanup is intentionally deferred to a follow-up PR. That follow-up will cover its wire types, desktop IPC, coordinator exchange path, runtime helpers, and path-specific tests.

Complexity delta

The PR removes:

  • One supported OAuth provider and its runtime adapter.
  • Claude-specific authorization, token refresh, model-fetch, cloaking, impersonation, and usage authorities.
  • Quota IPC operations and public bridge methods.
  • Claude-specific settings UI, fixtures, exports, and localization content.
  • Related test-maintenance burden.

The PR adds:

  • The retired provider state.
  • The isRetiredProvider registry helper.
  • The provider_retired readiness and error reason.
  • The all_connections_retired onboarding state.
  • Retirement branches in authentication, model catalogs, runtime resolution, connection testing, session admission, and settings.
  • Contract tests for retained-but-unavailable providers.

Total maintenance complexity decreases. The added retirement state is necessary to preserve stored-connection decoding while preventing use.

Validation and risks

Tests cover readiness rejection, provider contracts, onboarding, connection status, OAuth protocol behavior, storage policy, session admission, runtime rejection without network requests, and supported-provider OAuth flows.

The author reports passing build, typecheck, lint, formatting, and other listed checks. Environment-related failures match clean origin/main. Windows/Linux packaging and Playwright E2E tests were not run. Required-check status remains unverified without direct CI evidence.

Concrete risks include:

  • Retired connections now appear unavailable and require replacement.
  • Preload and runtime-host contracts remove Claude-specific methods and quota operations.
  • RUNTIME_HOST_COMPATIBILITY_EPOCH increases to 25.
  • OAuth protocol and provider unions change.
  • Stored-connection decoding depends on retaining the retired registry entry.
  • Authentication behavior changes from Claude subscription access to Anthropic API-key access.

Review-relevant risks

The current diff affects user-visible settings and onboarding, public bridge and protocol contracts, runtime compatibility, authentication behavior, and stored-connection handling. Material changes in these areas require independent human review under repository policy.

The follow-up for request_authorization_code should be tracked or linked. This is not an implementation request for the current PR.

The person performing the merge reviews the final diff. A maintainer makes the final determination.

Walkthrough

Claude subscription OAuth support is retired across core, runtime, runtime-host, storage, and desktop. Existing connections remain decodable but cannot authenticate, execute models, refresh quota, or initiate OAuth login. The UI directs users to replacement connections.

Changes

Claude subscription retirement

Layer / File(s) Summary
Core retirement contracts and readiness
packages/core/src/...
The provider registry marks claude-subscription as retired and unavailable. Readiness, onboarding, authentication, model catalogs, and repair routing report retired-provider states.
Runtime and runtime-host removal
packages/runtime/src/..., packages/runtime-host/src/...
Claude-specific OAuth login, model execution, discovery, credential handling, authorization-code flows, quota operations, and device identifiers are removed. Codex and xAI flows remain supported.
Desktop and storage policy
apps/desktop/src/..., packages/storage/src/...
Retired connections show dedicated status and guidance. Interactive login, execution, model testing, and default targeting reject retired providers.
Validation and fixtures
packages/*/src/__tests__/*, apps/desktop/stories/...
Tests and fixtures now cover retired-provider behavior and use supported Codex or xAI OAuth flows.

Estimated code review effort: 4 (Complex) | ~60 minutes

Merge Risk: 🟡 Moderate · up to 91c50

The change retires Claude subscription connections, but a retained retired connection can still be selected as the default and then cause new sessions to fail instead of running. Merge should wait for default-target validation to reject retired providers and for the corresponding mutation-path test.

Possibly related issues

  • maka-agent/maka-agent#3143 — The changes directly implement retirement of the claude-subscription OAuth path across registry, runtime, storage, desktop, and compatibility layers.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Ai Use Disclosure ✅ Passed The PR selects generative contribution, names Claude Code and its scope, and its sole introduced commit has the matching standalone trailer Generated-by: Claude Code.
Description check ✅ Passed The description follows the template and clearly documents scope, verification, behavior changes, AI use, and incomplete checks.
Linked Issues check ✅ Passed The description references issue #3143 and links the related follow-up discussion.
Out of Scope Changes check ✅ Passed The changes directly support retiring the Claude subscription provider and match the stated objectives.
Title check ✅ Passed The title is concise, specific, and accurately describes the primary change.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Retire Claude subscription OAuth across runtime and desktop

✨ Enhancement 🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Retires Claude subscription OAuth while preserving legacy connection decoding and deletion.
• Blocks retired connections across enrollment, discovery, readiness, testing, and runtime
 execution.
• Removes Claude Code impersonation, quota access, and related desktop surfaces.
Diagram

graph TD
  A["Stored Connection"] --> B["Provider Registry"] --> C["Auth Contract"] --> D["Storage Actions"]
  B --> E["Readiness Gate"] --> F["Runtime Backstop"]
  B --> G["Model Catalog"] --> H["Settings UI"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Purge the provider after catalog migration
  • ➕ Removes the retired provider type and compatibility branches completely.
  • ➕ Reduces the long-term registry and runtime state space.
  • ➖ Requires a tolerant catalog decoder or explicit migration before current clients can load data.
  • ➖ Could discard every connection when an older catalog contains the unknown provider type.
  • ➖ Makes stored credentials and legacy connections harder to expose for deliberate deletion.

Recommendation: Keep the PR’s registered-but-retired approach for immediate safety. It preserves catalog compatibility and credential cleanup while independently blocking enrollment, storage actions, model selection, task admission, and runtime execution. A later schema migration could purge the provider only after tolerant decoding and credential cleanup are guaranteed.

Files changed (65) +635 / -1567

Enhancement (28) +129 / -642
oauth-connection-identities.tsRemove Claude from interactive OAuth identities +0/-1

Remove Claude from interactive OAuth identities

• Drops the Claude subscription slug from the Desktop interactive OAuth provider mapping.

apps/desktop/src/main/oauth-connection-identities.ts

runtime-host-client.tsRemove OAuth account usage client operation +0/-6

Remove OAuth account usage client operation

• Deletes the Runtime Host client method for fetching subscription account usage.

apps/desktop/src/main/runtime-host-client.ts

runtime-host-oauth-ipc-main.tsRemove Claude quota IPC handling +2/-23

Remove Claude quota IPC handling

• Eliminates quota channels, account-usage fetching, quota caching, and quota-bearing account-state projections from Desktop OAuth IPC.

apps/desktop/src/main/runtime-host-oauth-ipc-main.ts

bridge-contract.d.tsRemove Claude subscription preload contract +0/-16

Remove Claude subscription preload contract

• Removes the renderer-facing Claude subscription bridge and its account-state type dependency.

apps/desktop/src/preload/bridge-contract.d.ts

preload.tsRemove Claude OAuth preload bridge +5/-47

Remove Claude OAuth preload bridge

• Deletes all Claude subscription authorization, account-state, quota, refresh, and logout bridge methods while retaining the Codex bridge.

apps/desktop/src/preload/preload.ts

conversation-copy.tsLocalize retired-provider conversation errors +4/-0

Localize retired-provider conversation errors

• Adds Chinese and English blocked-send and task-health copy for connections whose sign-in provider was retired.

apps/desktop/src/renderer/locales/conversation-copy.ts

onboarding-copy.tsAdd dedicated retired-connections onboarding copy +21/-3

Add dedicated retired-connections onboarding copy

• Keys blocked onboarding text by reason and adds Chinese and English guidance for workspaces containing only retired connections.

apps/desktop/src/renderer/locales/onboarding-copy.ts

settings-provider-copy.tsExplain Claude sign-in retirement in settings +6/-0

Explain Claude sign-in retirement in settings

• Adds bilingual retirement messaging recommending Anthropic API keys and explaining that deletion clears the stored credential.

apps/desktop/src/renderer/locales/settings-provider-copy.ts

provider-connection-detail.tsxRender retired connection state without actions +15/-7

Render retired connection state without actions

• Displays an informational retirement banner and suppresses connection testing and model refresh controls for retired providers.

apps/desktop/src/renderer/settings/provider-connection-detail.tsx

provider-oauth-section.tsxRemove Claude from OAuth account settings +4/-38

Remove Claude from OAuth account settings

• Removes the Claude OAuth card, experimental gate, state refresh, login panel, and subscription snapshot integration.

apps/desktop/src/renderer/settings/provider-oauth-section.tsx

use-connection-detail.tsExpose retirement state to connection details +8/-1

Expose retirement state to connection details

• Detects retired providers, disables their OAuth relogin service, and supplies retirement state to the detail view.

apps/desktop/src/renderer/settings/use-connection-detail.ts

connection-error-copy.tsAdd retired-provider repair guidance +2/-0

Add retired-provider repair guidance

• Maps provider_retired to guidance for selecting another model connection.

packages/core/src/connection-error-copy.ts

model-web-search.tsRemove Claude subscription web-search mapping +0/-2

Remove Claude subscription web-search mapping

• Stops assigning Anthropic hosted web-search adapters and defaults to the retired provider.

packages/core/src/model-web-search.ts

onboarding.tsDistinguish retired onboarding blockage +11/-3

Distinguish retired onboarding blockage

• Adds all_connections_retired and derives it when every candidate connection uses a retired provider.

packages/core/src/onboarding.ts

provider-registry.tsMark Claude subscription registered but retired +24/-4

Mark Claude subscription registered but retired

• Replaces its runtime adapter with unavailable, adds explicit retirement metadata and detection, and removes Claude-specific discovery authentication.

packages/core/src/provider-registry.ts

oauth.tsRemove Claude and account usage from OAuth protocol +1/-100

Remove Claude and account usage from OAuth protocol

• Restricts interactive OAuth providers to Codex and xAI and deletes account-usage operation types, codecs, and validation.

packages/runtime-host/src/protocol/oauth.ts

operations.tsRevoke remote OAuth usage operation grant +0/-1

Revoke remote OAuth usage operation grant

• Removes the deleted OAuth account-usage operation from remote owner grants.

packages/runtime-host/src/protocol/operations.ts

execution-composition.tsRemove Claude device identity wiring +0/-5

Remove Claude device identity wiring

• Stops passing the storage-root-derived Claude device identifier into interactive and auxiliary model compositions.

packages/runtime-host/src/server/execution-composition.ts

execution-model-authority.tsRemove Claude identity from auxiliary model authority +0/-5

Remove Claude identity from auxiliary model authority

• Eliminates Claude device identifiers from auxiliary model authority contracts and OAuth model-fetch construction.

packages/runtime-host/src/server/execution-model-authority.ts

execution-model-composition.tsRemove Claude identity from backend composition +0/-2

Remove Claude identity from backend composition

• Drops Claude device identity from Host AI SDK backend inputs and subscription model fetch construction.

packages/runtime-host/src/server/execution-model-composition.ts

oauth-coordinator.tsRemove Claude login and usage coordination +3/-134

Remove Claude login and usage coordination

• Deletes Claude paste-code authorization, token exchange, quota fetching, and associated dependencies, leaving Codex and xAI enrollment flows.

packages/runtime-host/src/server/oauth-coordinator.ts

oauth-execution-authority.tsRemove Claude request identity injection +0/-18

Remove Claude request identity injection

• Eliminates Claude account validation and cloaking metadata from Host OAuth model fetches.

packages/runtime-host/src/server/oauth-execution-authority.ts

model-fetcher.tsRemove cloaked Anthropic model discovery +3/-16

Remove cloaked Anthropic model discovery

• Deletes Claude bearer and impersonation headers so Anthropic model discovery only uses API-key authentication.

packages/runtime/src/model-fetcher.ts

oauth-login.tsRemove Claude authorization and token exchange support +17/-78

Remove Claude authorization and token exchange support

• Restricts authorization-code login to xAI and deletes Claude endpoints, request encoding, token decoding, and fixed redirect handling.

packages/runtime/src/oauth-login.ts

oauth-provider-contracts.tsRemove Claude OAuth client identity +1/-16

Remove Claude OAuth client identity

• Deletes Claude from enrollment providers and removes the borrowed client ID, endpoints, scopes, User-Agent, and experimental flag.

packages/runtime/src/oauth-provider-contracts.ts

subscription-credentials.tsRemove Claude credential refresh support +1/-44

Remove Claude credential refresh support

• Excludes Claude from OAuth subscription credential providers and deletes its token refresh implementation and client contract.

packages/runtime/src/subscription-credentials.ts

subscription-model-fetch.tsRemove Claude cloaked model transport +0/-71

Remove Claude cloaked model transport

• Deletes Claude cloak configuration, metadata validation, and request-rewriting transport while retaining supported subscription providers.

packages/runtime/src/subscription-model-fetch.ts

operations.tsNarrow interactive OAuth ticket providers +1/-1

Narrow interactive OAuth ticket providers

• Removes Claude subscription from the storage operation type for interactive OAuth login tickets.

packages/storage/src/runtime-policy/operations.ts

Bug fix (12) +99 / -71
chat-readiness.tsExplain retired providers at send admission +2/-0

Explain retired providers at send admission

• Adds a localized provider_retired message directing users to select another model connection.

apps/desktop/src/main/chat-readiness.ts

onboarding-hero-copy.tsSelect onboarding copy by blocked reason +9/-7

Select onboarding copy by blocked reason

• Routes blocked onboarding states to reason-specific copy so retired connections do not receive credential-repair guidance.

apps/desktop/src/renderer/onboarding-hero-copy.ts

providers-panel.tsxPrevent retired connections becoming defaults +5/-0

Prevent retired connections becoming defaults

• Suppresses the set-as-default action for retired provider connections.

apps/desktop/src/renderer/settings/providers-panel.tsx

connection-readiness.tsReject retired providers before credential checks +18/-9

Reject retired providers before credential checks

• Adds provider_retired to canonical readiness reasons and prioritizes it before disabled, credential, and model validation.

packages/core/src/connection-readiness.ts

model-catalog.tsMark retired-provider models unavailable +5/-1

Mark retired-provider models unavailable

• Forces models belonging to retired providers to resolve as provider_removed even when callers report the provider available.

packages/core/src/model-catalog.ts

provider-auth.tsHide every action for retired providers +24/-0

Hide every action for retired providers

• Introduces a retired-provider auth contract with no setup mode or available actions while preserving deletion-based credential cleanup.

packages/core/src/provider-auth.ts

task-submission-readiness.tsRoute retired tasks to provider catalog +3/-0

Route retired tasks to provider catalog

• Directs provider_retired repair actions to the provider catalog rather than an irreparable connection detail page.

packages/core/src/task-submission-readiness.ts

model-factory.tsBackstop retired providers during model construction +7/-10

Backstop retired providers during model construction

• Removes Claude subscription model creation and headers, adds an unavailable-adapter failure, and removes retired-provider thinking options.

packages/runtime/src/model-factory.ts

model-runtime.tsReject unavailable runtime adapters +5/-1

Reject unavailable runtime adapters

• Throws when runtime wire resolution reaches a retired provider’s unavailable adapter.

packages/runtime/src/model-runtime.ts

subscription-auth.tsRemove Claude Code impersonation headers +0/-13

Remove Claude Code impersonation headers

• Deletes Claude-specific beta headers, CLI User-Agent, direct-browser marker, and x-app identity generation.

packages/runtime/src/subscription-auth.ts

test-connection.tsFail retired connection tests without network access +20/-25

Fail retired connection tests without network access

• Returns an explicit retirement result for unavailable adapters and removes the former token-presence success path and Claude headers.

packages/runtime/src/test-connection.ts

coordinator.tsRemove Claude from interactive login admission +1/-5

Remove Claude from interactive login admission

• Restricts storage-authorized interactive OAuth login providers to Codex and xAI.

packages/storage/src/runtime-policy/coordinator.ts

Refactor (1) +7 / -139
oauth-subscription.tsGeneralize remaining OAuth subscription helpers +7/-139

Generalize remaining OAuth subscription helpers

• Removes Claude account, quota, runtime-state, and authorization configuration contracts while retaining provider-neutral action and PKCE utilities.

packages/core/src/oauth-subscription.ts

Tests (23) +399 / -714
chat-readiness.test.tsCover retired-provider chat readiness +20/-4

Cover retired-provider chat readiness

• Moves the active OAuth credential case to Codex and verifies Claude subscription reports provider_retired even when a credential remains stored.

apps/desktop/src/main/tests/chat-readiness.test.ts

runtime-host-account-connection.test.tsUse Codex for account synchronization tests +11/-11

Use Codex for account synchronization tests

• Replaces Claude subscription fixtures and model expectations with the remaining Codex OAuth provider.

apps/desktop/src/main/tests/runtime-host-account-connection.test.ts

runtime-host-oauth-ipc-main.test.tsRemove Claude OAuth and quota IPC expectations +12/-29

Remove Claude OAuth and quota IPC expectations

• Moves shared OAuth IPC coverage to Codex, removes Claude channels and quota refresh assertions, and verifies account state without quota data.

apps/desktop/src/main/tests/runtime-host-oauth-ipc-main.test.ts

provider-settings.stories.tsxRemove Claude subscription story fixture +0/-13

Remove Claude subscription story fixture

• Deletes the obsolete Claude OAuth bridge fixture from provider settings stories.

apps/desktop/stories/settings/provider-settings.stories.tsx

settings-pages.stories.tsxRemove Claude account settings fixture +0/-7

Remove Claude account settings fixture

• Removes the authenticated Claude subscription fixture from settings-page stories.

apps/desktop/stories/settings/settings-pages.stories.tsx

connection-readiness.test.tsTest canonical retired-provider readiness +31/-0

Test canonical retired-provider readiness

• Verifies retirement blocks sending regardless of whether a legacy OAuth credential remains stored.

packages/core/src/tests/connection-readiness.test.ts

oauth-subscription.test.tsRemove Claude authorization URL tests +0/-33

Remove Claude authorization URL tests

• Drops tests for the removed Claude-specific OAuth authorization URL builder while retaining provider-neutral PKCE helpers.

packages/core/src/tests/oauth-subscription.test.ts

onboarding.test.tsTest all-connections-retired onboarding +22/-2

Test all-connections-retired onboarding

• Moves healthy OAuth coverage to xAI and verifies a Claude-only workspace receives the dedicated retirement reason.

packages/core/src/tests/onboarding.test.ts

provider-auth.test.tsMove active OAuth auth tests off Claude +2/-3

Move active OAuth auth tests off Claude

• Uses xAI and Codex for active OAuth contract scenarios now that Claude subscription is retired.

packages/core/src/tests/provider-auth.test.ts

provider-catalog-contract.test.tsPin retired-provider safety invariants +91/-1

Pin retired-provider safety invariants

• Verifies retired providers remain registered but unwired, stay out of creation catalogs, expose no actions, and yield unselectable removed models.

packages/core/src/tests/provider-catalog-contract.test.ts

execution-model-composition.test.tsMove OAuth execution composition coverage to Codex +36/-49

Move OAuth execution composition coverage to Codex

• Reworks OAuth refresh and backend composition fixtures around Codex, including credential login, model-fetch seeding, and account identity preservation.

packages/runtime-host/src/tests/execution-model-composition.test.ts

oauth-coordinator.test.tsRemove Claude enrollment coordinator coverage +16/-171

Remove Claude enrollment coordinator coverage

• Deletes Claude paste-code and account-usage scenarios and moves shared coordinator concurrency and admission tests to Codex or xAI.

packages/runtime-host/src/tests/oauth-coordinator.test.ts

oauth-execution-authority.test.tsRemove Claude cloaked execution tests +28/-151

Remove Claude cloaked execution tests

• Deletes Claude refresh and request-identity coverage, moves lease reconciliation to Codex, and removes Claude device metadata from model fetches.

packages/runtime-host/src/tests/oauth-execution-authority.test.ts

oauth-protocol.test.tsVerify retired OAuth wire values are rejected +36/-38

Verify retired OAuth wire values are rejected

• Replaces quota projection validation with operation-removal coverage and verifies login projections reject Claude subscription.

packages/runtime-host/src/tests/oauth-protocol.test.ts

oauth-two-client-uds.test.tsUse Codex for multi-client OAuth transport tests +22/-16

Use Codex for multi-client OAuth transport tests

• Converts real-endpoint enrollment coverage from Claude paste-code login to Codex device authorization and removes Claude opt-out cases.

packages/runtime-host/src/tests/oauth-two-client-uds.test.ts

claude-subscription-runtime.test.tsVerify runtime refuses retired Claude connections +13/-9

Verify runtime refuses retired Claude connections

• Replaces successful Claude connection-test expectations with explicit retirement failures and confirms no network probe occurs.

packages/runtime/src/tests/claude-subscription-runtime.test.ts

computer-use-model-loop.test.tsRemove Claude subscription from computer-use matrix +0/-1

Remove Claude subscription from computer-use matrix

• Excludes the retired provider from Anthropic-compatible computer-use loop coverage.

packages/runtime/src/tests/computer-use-model-loop.test.ts

model-factory-thinking.test.tsRemove Claude from thinking-option matrix +1/-6

Remove Claude from thinking-option matrix

• Stops treating the retired provider as an active Anthropic-compatible model-factory target.

packages/runtime/src/tests/model-factory-thinking.test.ts

oauth-login.test.tsRemove Claude OAuth login coverage +19/-55

Remove Claude OAuth login coverage

• Deletes Claude authorization and token-decoding tests and moves generic exchange validation to the remaining xAI loopback flow.

packages/runtime/src/tests/oauth-login.test.ts

provider-contract-matrix.tsRemove Claude runtime wire adapter classification +1/-1

Remove Claude runtime wire adapter classification

• Drops claude-subscription from provider-specific subscription wire adapter kinds.

packages/runtime/src/tests/provider-contract-matrix.ts

subscription-credentials.test.tsMove OAuth refresh transaction tests to Codex +25/-28

Move OAuth refresh transaction tests to Codex

• Replaces Claude provider and credential slugs with Codex throughout refresh validation, CAS, concurrency, and logout tests.

packages/runtime/src/tests/subscription-credentials.test.ts

subscription-model-fetch.test.tsRemove Claude cloaked request tests +0/-85

Remove Claude cloaked request tests

• Deletes Claude Code request impersonation and metadata validation scenarios while retaining Codex subscription transport coverage.

packages/runtime/src/tests/subscription-model-fetch.test.ts

runtime-policy-stores.test.tsVerify storage refuses retired OAuth login +13/-1

Verify storage refuses retired OAuth login

• Moves active login-ticket coverage to Codex and asserts a stored Claude connection receives hidden-action refusal.

packages/storage/src/tests/runtime-policy-stores.test.ts

Other (1) +1 / -1
index.tsAdvance Runtime Host compatibility epoch +1/-1

Advance Runtime Host compatibility epoch

• Bumps the compatibility epoch from 22 to 23 for the OAuth provider enum and operation-set changes.

packages/runtime-host/src/protocol/index.ts

@Joob1n
Joob1n force-pushed the feat/retire-claude-subscription-oauth branch from 16f779f to 16c9576 Compare August 18, 2026 03:03
@qodo-code-review

qodo-code-review Bot commented Aug 18, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Stale package.json export for deleted module ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
The PR deletes packages/runtime/src/claude-subscription-usage.ts, but
packages/runtime/package.json still declares the subpath export `"./claude-subscription-usage":
"./dist/claude-subscription-usage.js"`. Any remaining import of
@maka/runtime/claude-subscription-usage will now resolve to a build artifact that will never be
produced again, failing at module resolution/runtime instead of being caught at compile time.
Code

packages/runtime/src/claude-subscription-usage.ts[L1-5]

-import type { QuotaSnapshot, QuotaWindow } from '@maka/core/oauth-subscription';
-import {
-  OAuthTokenEndpointError,
-  OAUTH_PROVIDER_CONTRACTS,
-  requireOAuthDataRecord,
Relevance

●●● Strong

A deleted module with a dangling package export is a concrete resolution bug; repository reviews
accept similarly defensive boundary fixes.

PR-#3169

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The file was fully deleted by this PR (packages_runtime_src_claude-subscription-usage_ts.patch shows
-64/+0), and the only importer, packages/runtime-host/src/server/oauth-coordinator.ts, dropped its
import { fetchClaudeSubscriptionUsage } from '@maka/runtime/claude-subscription-usage' line. Yet
packages/runtime/package.json line 44 still maps that subpath to
./dist/claude-subscription-usage.js, which will no longer exist after the build removes the
deleted source file, so the entry is dangling.

packages/runtime/package.json[44-44]
packages/runtime/src/claude-subscription-usage.ts: TBD

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR deletes `packages/runtime/src/claude-subscription-usage.ts` but leaves the corresponding subpath export in `packages/runtime/package.json` pointing at a build artifact (`./dist/claude-subscription-usage.js`) that will no longer be produced.

## Issue Context
This is part of a larger PR retiring the `claude-subscription` OAuth provider. All source-level imports of this module were removed, but the package.json `exports` map was not updated to match, so consumers relying on this subpath (in-repo or external) would fail to resolve it after a clean build.

## Fix Focus Areas
- packages/runtime/package.json[44-44]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Orphaned Claude settings copy left behind ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
claude-subscription-card.tsx was deleted entirely, but its dedicated claude copy object
(login/logout/quota/status strings) remains in both zhCopy and enCopy in
settings-provider-copy.ts, so ProviderSettingsCopy still carries a field nothing reads anymore.
This is dead data that will mislead future maintainers into thinking the Claude card copy is still
live.
Code

apps/desktop/src/renderer/locales/settings-provider-copy.ts[R57-59]

+    oauthRetired: '此登录方式已停用',
+    oauthRetiredDetail:
+      '这条连接使用的登录方式已从 Maka 移除,无法再登录,也无法用于对话。改用 Anthropic API Key 连接即可继续使用 Claude 模型;删除这条连接会一并清除本机保存的登录凭据。',
Relevance

●●● Strong

Dead localized copy after deleting its sole consumer is a straightforward maintainability cleanup
matching accepted cleanup-oriented review behavior.

PR-#3147

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Repository search confirms the claude object (distinct from the
oauthSection.claudeDescription/claudeSubtitle fields this PR did remove) still exists at
settings-provider-copy.ts:175-191 (zh) and :326-341 (en), fully intact with zero remaining
references (.claude. / copy.claude / getProviderSettingsCopy(locale).claude all return no
matches) after claude-subscription-card.tsx, its only consumer, was deleted.

apps/desktop/src/renderer/locales/settings-provider-copy.ts[175-191]
apps/desktop/src/renderer/locales/settings-provider-copy.ts[326-341]
apps/desktop/src/renderer/settings/claude-subscription-card.tsx: TBD

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `claude` copy sub-object in `ProviderSettingsCopy` (used only by the now-deleted `ClaudeSubscriptionCard` component) is now completely unreferenced dead code.

## Issue Context
The PR deletes `claude-subscription-card.tsx` and the `oauthSection.claudeDescription`/`claudeSubtitle` fields it used, but missed removing the separate top-level `claude` object that held the card's detailed login/quota copy strings.

## Fix Focus Areas
- apps/desktop/src/renderer/locales/settings-provider-copy.ts[175-191]
- apps/desktop/src/renderer/locales/settings-provider-copy.ts[326-341]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Login provider branch not future-exhaustive ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
#runLogin in oauth-coordinator.ts now branches with `attempt.provider === 'xai-oauth' ?
runXaiLogin : runCodexDeviceLogin`, defaulting every non-xai value to the Codex device flow instead
of using a case-per-value check. This works today because OAuthLoginProvider has only two members,
but a future addition to that union would silently be routed to Codex login with no compiler error —
the opposite of the fail-closed, exhaustively-checked pattern this same PR establishes for adapter
kinds (deriveProviderAuthContract, resolveModelRuntimeWire, getAIModel).
Code

packages/runtime-host/src/server/oauth-coordinator.ts[R330-333]

      const tokens =
        attempt.provider === 'xai-oauth'
          ? await this.#runXaiLogin(attempt, transport.fetch)
-          : attempt.provider === 'openai-codex'
-            ? await this.#runCodexDeviceLogin(attempt, transport.fetch)
-            : await this.#runAuthorizationCodeLogin(attempt, transport.fetch);
+          : await this.#runCodexDeviceLogin(attempt, transport.fetch);
Relevance

●● Moderate

Exhaustiveness concern is technically plausible, but history lacks a close precedent for rejecting
or requiring this specific branch style.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
packages/runtime-host/src/protocol/oauth.ts now declares `OAUTH_LOGIN_PROVIDERS = ['openai-codex',
'xai-oauth']. The coordinator's simplified ternary (attempt.provider === 'xai-oauth' ? ... :
this.#runCodexDeviceLogin(...)`) replaces what used to be a three-way branch (xai / codex / claude
authorization-code) with an implicit-default ternary not tied to the type system.

packages/runtime-host/src/server/oauth-coordinator.ts[330-333]
packages/runtime-host/src/protocol/oauth.ts[16-16]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The two-way ternary that picks the login flow by `attempt.provider` silently routes any provider other than `xai-oauth` to the Codex device flow, with no compiler-enforced exhaustiveness check.

## Issue Context
This PR removed the third (`claude-subscription`) branch of what used to be a three-way conditional, collapsing the remainder into a ternary. Not a bug today given the two-member union, but it drops the fail-closed pattern used elsewhere in the same PR (e.g. adapter-kind switches that explicitly handle `unavailable`).

## Fix Focus Areas
- packages/runtime-host/src/server/oauth-coordinator.ts[330-333]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: 🧠 Deep: This retires a security-sensitive OAuth capability across many independent authorities and runtime, storage, IPC, UI, and compatibility paths, creating a dense set of easy-to-miss regressions.

Grey Divider

Tip of the day
💡 Did you know, you can show, collapse, or hide each part of a finding: code, evidence, and all

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
packages/runtime-host/src/server/oauth-coordinator.ts (1)

2-2: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Delete the unused authorization-code exchange dependency.

Disposition: optional.

After Line 333, #runLogin calls only #runXaiLogin or #runCodexDeviceLogin. Neither method calls #exchangeCode. Delete exchangeOAuthAuthorizationCode, HostOAuthCoordinatorInput.exchangeCode, and the private #exchangeCode field.

As per path instructions, “Flag concrete cases where code can be deleted or simplified.”

Also applies to: 333-333

Source: Path instructions

packages/runtime-host/src/__tests__/oauth-execution-authority.test.ts (1)

439-439: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the duplicated union member.

'openai-codex' | 'openai-codex' is equivalent to one literal type. Declare providerType as 'openai-codex'.

Disposition: optional.

Proposed simplification
-  providerType: 'openai-codex' | 'openai-codex',
+  providerType: 'openai-codex',

As per path instructions, “Flag concrete cases where code can be deleted or simplified.”

Source: Path instructions

packages/runtime/src/__tests__/oauth-login.test.ts (1)

104-112: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Delete the unused Claude environment entry.

MAKA_CLAUDE_SUBSCRIPTION_EXPERIMENTAL affects neither assertion. The test only evaluates openai-codex and xai-oauth, and xAI ignores environment flags. Remove this entry to keep the test limited to active enrollment behavior. Disposition: optional.

As per path instructions, delete an unnecessary path before adding behavior.

Source: Path instructions


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 000f121d-fee4-4262-991f-407da136b587

📥 Commits

Reviewing files that changed from the base of the PR and between e685853 and 16c9576.

📒 Files selected for processing (71)
  • apps/desktop/src/main/__tests__/chat-readiness.test.ts
  • apps/desktop/src/main/__tests__/runtime-host-account-connection.test.ts
  • apps/desktop/src/main/__tests__/runtime-host-oauth-ipc-main.test.ts
  • apps/desktop/src/main/chat-readiness.ts
  • apps/desktop/src/main/oauth-connection-identities.ts
  • apps/desktop/src/main/runtime-host-client.ts
  • apps/desktop/src/main/runtime-host-oauth-ipc-main.ts
  • apps/desktop/src/preload/bridge-contract.d.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/locales/conversation-copy.ts
  • apps/desktop/src/renderer/locales/onboarding-copy.ts
  • apps/desktop/src/renderer/locales/settings-provider-copy.ts
  • apps/desktop/src/renderer/onboarding-hero-copy.ts
  • apps/desktop/src/renderer/settings/claude-subscription-card.tsx
  • apps/desktop/src/renderer/settings/provider-connection-detail.tsx
  • apps/desktop/src/renderer/settings/provider-oauth-section.tsx
  • apps/desktop/src/renderer/settings/providers-panel.tsx
  • apps/desktop/src/renderer/settings/use-connection-detail.ts
  • apps/desktop/stories/settings/provider-settings.stories.tsx
  • apps/desktop/stories/settings/settings-pages.stories.tsx
  • docs/astryx-surface-file-inventory.md
  • docs/astryx-surface-file-inventory.paths
  • packages/core/src/__tests__/connection-readiness.test.ts
  • packages/core/src/__tests__/oauth-subscription.test.ts
  • packages/core/src/__tests__/onboarding.test.ts
  • packages/core/src/__tests__/provider-auth.test.ts
  • packages/core/src/__tests__/provider-catalog-contract.test.ts
  • packages/core/src/connection-error-copy.ts
  • packages/core/src/connection-readiness.ts
  • packages/core/src/model-catalog.ts
  • packages/core/src/model-web-search.ts
  • packages/core/src/oauth-subscription.ts
  • packages/core/src/onboarding.ts
  • packages/core/src/provider-auth.ts
  • packages/core/src/provider-registry.ts
  • packages/core/src/task-submission-readiness.ts
  • packages/runtime-host/src/__tests__/execution-model-composition.test.ts
  • packages/runtime-host/src/__tests__/oauth-coordinator.test.ts
  • packages/runtime-host/src/__tests__/oauth-execution-authority.test.ts
  • packages/runtime-host/src/__tests__/oauth-protocol.test.ts
  • packages/runtime-host/src/__tests__/oauth-two-client-uds.test.ts
  • packages/runtime-host/src/protocol/index.ts
  • packages/runtime-host/src/protocol/oauth.ts
  • packages/runtime-host/src/protocol/operations.ts
  • packages/runtime-host/src/server/execution-composition.ts
  • packages/runtime-host/src/server/execution-model-authority.ts
  • packages/runtime-host/src/server/execution-model-composition.ts
  • packages/runtime-host/src/server/oauth-coordinator.ts
  • packages/runtime-host/src/server/oauth-execution-authority.ts
  • packages/runtime/src/__tests__/claude-subscription-runtime.test.ts
  • packages/runtime/src/__tests__/claude-subscription-usage.test.ts
  • packages/runtime/src/__tests__/computer-use-model-loop.test.ts
  • packages/runtime/src/__tests__/model-factory-thinking.test.ts
  • packages/runtime/src/__tests__/oauth-login.test.ts
  • packages/runtime/src/__tests__/provider-contract-matrix.ts
  • packages/runtime/src/__tests__/subscription-credentials.test.ts
  • packages/runtime/src/__tests__/subscription-model-fetch.test.ts
  • packages/runtime/src/claude-subscription-usage.ts
  • packages/runtime/src/model-factory.ts
  • packages/runtime/src/model-fetcher.ts
  • packages/runtime/src/model-runtime.ts
  • packages/runtime/src/oauth-login.ts
  • packages/runtime/src/oauth-provider-contracts.ts
  • packages/runtime/src/subscription-auth.ts
  • packages/runtime/src/subscription-cloaked-request.ts
  • packages/runtime/src/subscription-credentials.ts
  • packages/runtime/src/subscription-model-fetch.ts
  • packages/runtime/src/test-connection.ts
  • packages/storage/src/__tests__/runtime-policy-stores.test.ts
  • packages/storage/src/runtime-policy/coordinator.ts
  • packages/storage/src/runtime-policy/operations.ts
💤 Files with no reviewable changes (21)
  • docs/astryx-surface-file-inventory.paths
  • apps/desktop/src/main/oauth-connection-identities.ts
  • apps/desktop/stories/settings/provider-settings.stories.tsx
  • apps/desktop/stories/settings/settings-pages.stories.tsx
  • apps/desktop/src/renderer/settings/claude-subscription-card.tsx
  • packages/runtime/src/claude-subscription-usage.ts
  • packages/runtime/src/tests/computer-use-model-loop.test.ts
  • packages/runtime-host/src/protocol/operations.ts
  • packages/runtime/src/tests/subscription-model-fetch.test.ts
  • packages/runtime-host/src/server/execution-model-composition.ts
  • apps/desktop/src/preload/bridge-contract.d.ts
  • packages/core/src/model-web-search.ts
  • packages/runtime/src/subscription-cloaked-request.ts
  • packages/core/src/tests/oauth-subscription.test.ts
  • packages/runtime-host/src/server/execution-composition.ts
  • packages/runtime-host/src/server/execution-model-authority.ts
  • packages/runtime/src/tests/claude-subscription-usage.test.ts
  • apps/desktop/src/main/runtime-host-client.ts
  • packages/runtime-host/src/server/oauth-execution-authority.ts
  • packages/runtime/src/subscription-auth.ts
  • packages/runtime/src/subscription-model-fetch.ts

Included review availability: Your plan includes up to 3 reviews per rolling hour; 1 remains after this review.

Comment thread apps/desktop/src/main/__tests__/runtime-host-oauth-ipc-main.test.ts
Comment thread packages/runtime/src/__tests__/provider-contract-matrix.ts
Comment thread packages/runtime/src/model-runtime.ts
@Joob1n
Joob1n force-pushed the feat/retire-claude-subscription-oauth branch from 16c9576 to 2ddd92d Compare August 18, 2026 03:54
@Joob1n

Joob1n commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

All three are real. Fixed in the amended commit.

1. Stale package.json export. Correct, and the one I am most glad you caught — neither tsc nor Biome sees a subpath export pointing at a source file that no longer exists. Removed. I then scanned every workspace's exports map against the files this PR deletes; that was the only dangling entry.

2. Orphaned Claude settings copy. Removed the whole claude block from both zhCopy and enCopy, plus claudeDescription and claudeSubtitle, which lost their last reader when the OAuth card went.

3. Login branch not future-exhaustive. Agreed — the ternary was the one place in this PR that fails open. Replaced with a switch in #runProviderLogin. Verified it now fails closed: adding a third member to OAUTH_LOGIN_PROVIDERS makes the compiler reject the function (TS2366) instead of silently routing it to the Codex device flow.

On the alternative approach: agreed, and the reason is stronger than compatibility. connection-catalog-document.ts decodes provider types with a plain .map(), so an unregistered type fails the whole document — a user would lose every other connection along with this one. That is why the entry stays registered and marked retired.

@Joob1n
Joob1n force-pushed the feat/retire-claude-subscription-oauth branch 2 times, most recently from fd810dd to 1d978df Compare August 18, 2026 05:53
@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks for the removal — the impersonation is unambiguous and the decision is right: the deleted subscription-cloaked-request.ts fabricated the Claude Code identity end to end (UA claude-cli/2.1.153 (external, cli), x-app: cli, X-Claude-Code-Session-Id, a spoofed system prompt "You are Claude Code…", billing markers, Stainless client headers, and the borrowed client_id), and the risk lands on the user's Anthropic account under Consumer Terms — removing beats gating because gating only hides the violation behind a flag. The 7 enforcement points are real and mutually independent (registry truth → storage admission → send readiness → picker → OAuth login → wire enum → runtime backstop); I verified each one exists at its claimed location and that mutating it individually turns a test red. The delete is clean (4 files removed wholesale, shared infra correctly trimmed, no dangling imports; the stale package.json export is fixed). The retained-but-retired registry entry is justified — decodeProviderType throws on unknown types and the whole catalog document fails (connection-catalog-codec.ts:568), so unregistering would cost users every other connection — though note the repo does have a full-unregistration precedent (isRetiredGeminiCliConnection) that filters by raw providerType string before canonical decode, so "impossible" is slightly overstated; the real reason (keep the connection visible and deletable so the user can clear the credential, and onboarding returns needs_connection otherwise) is a fine product choice that should be stated honestly in the PR. The breaking-change note is honest apart from the epoch paragraph below. CI is green.

Conclusion: PASS with one P2 (epoch) — and the UI/UX screenshot gate must be satisfied before merge.

P2 — the epoch bump the PR claims ("RUNTIME_HOST_COMPATIBILITY_EPOCH moves to 23") is not in the diff. This PR changes wire protocol (removes claude-subscription from OAUTH_LOGIN_PROVIDERS and deletes the oauth.account.usage.fetch operation), but protocol/index.ts is untouched: the base already had 23 (raised by #3145, merged the same day), and current main sits at 24 (raised by #3165). So the "old clients rejected by the epoch gate" protection is coincidence, not this PR — a client built between #3145 and this PR (epoch 23, old protocol) would pass the handshake (exact-equality check at connection.ts:1290) and then fail mid-flight on oauth.account.usage.fetch/login with an unknown-operation error instead of a clean handshake rejection. Actual impact is low today (no released epoch-23 client), but the repo's convention is every breaking PR bumps its own epoch, and the description misleads maintainers into thinking the gate covers this. Please bump to 25 in this PR (coordinating with #3103, which also aims at 24→25) and correct the description, or explicitly state you're riding #3145's bump release-atomically and fix the wording.

P3-1 (merge gate): no before/after screenshots for the UI/UX changes. This PR visibly changes Settings (retirement banner, removed test/set-default buttons, list-row retired state), onboarding (all_connections_retired card), and deletes the 494-line subscription card — but the PR has no screenshot/recording. Per the repo review gate, UI/UX changes require before/after screenshots before merge. Please add them.

P3 (optional): one mutation-claim is not independently reproducible — the storage allow-list test (runtime-policy-stores.test.ts:2849-2859) would stay green if only the allow-list revert (the auth-contract gate independently produces the same result — defense-in-depth working as intended, but the mutation table overstates that row's discriminating power); dead code remains in oauth-coordinator.ts (exchangeOAuthAuthorizationCode import, #exchangeCode field, HostOAuthCoordinatorInput.exchangeCode — never called after the code-flow removal); oauth-login.test.ts:106 still sets the removed MAKA_CLAUDE_SUBSCRIPTION_EXPERIMENTAL env flag; the new retired UI states have no Storybook stories and the E2E suite wasn't run (per repo convention, UI states want deterministic Storybook/Playwright coverage).


AI-assisted review disclosure: this review was produced with AI assistance (two independent pi review subagents on opencode-go/deepseek-v4-flash). Both verified the enforcement points, the delete thoroughness, and the catalog decode rationale from the PR head; the epoch finding is a fact of the current tree (base already 23, main 24, diff empty for protocol/index.ts), not a prediction. P3-1 is from the PR having no screenshots. Please weigh these findings with your own judgment.

中文摘要(AI 辅助审查)

结论:PASS(1 个 P2 + UI/UX 截图 gate 阻塞)。冒充确凿、移除决定正确(subscription-cloaked-request.ts 全套伪造 Claude Code 身份——UA/x-app:cli/会话头/假系统提示/计费标记/借用的 client_id,风险在用户自己的 Anthropic 账户,gating 只是把违规路径藏起来,移除才消除风险载体)。7 个 enforcement point 全部真实且相互独立(注册表真相源→storage 准入→send 准入→picker→OAuth 登录→wire 枚举→runtime 兜底),逐一核实存在且单独 mutation 会红。删除干净(4 文件整删、共享基建正确裁剪、无悬空 import、stale export 已修)。retained-but-retired 的技术理由基本成立(decodeProviderType 对未注册类型 throw、整个 catalog 文档失败,unregister 会让用户失去全部连接)——但有 gemini-cli 先例(按原始 providerType 字符串先过滤)证明完全 unregister 可行,"不可能"略夸大;真实理由(保留可见可删、让用户清理本机凭据、否则 onboarding 退回 needs_connection)是合理产品选择,建议在 PR 中诚实陈述。breaking-change 叙述除 epoch 段外诚实。P2:PR 声称的 epoch bump(22→23)不在 diff 里——protocol/index.ts 未动,base 已是 23(#3145 升的),main 当前 24(#3165 升的);"旧客户端被 epoch gate 拒绝"是巧合保护——#3145 与本 PR 之间构建的 client(epoch 23、旧协议)会通过精确相等握手(connection.ts:1290)然后中途在 oauth.account.usage.fetch/login 上报 unknown-operation 而非握手期干净拒绝。实际影响低(无已发布 epoch-23 client),但违反仓库"每个 breaking PR 自己 bump"惯例且描述误导。建议本 PR bump 25(与 #3103 协调)并修正描述,或显式声明骑 #3145 的 bump 并 release-atomic。P3-1(合并 gate):大量 UI/UX 变化(Settings 退休横幅/移除 test 与 set-default 按钮/list 行 retired 状态/onboarding all_connections_retired 卡片/删 494 行卡片)但无任何截图或录屏——按仓库规则 UI/UX 变化合并前必须 before/after 截图。P3(可选):storage allow-list 的 mutation 声称不可独立复现(contract 门独立产生同结果,防御纵深生效但表格高估该行判别力);oauth-coordinator.ts 残留死代码(exchangeOAuthAuthorizationCode 导入、#exchangeCode 字段、HostOAuthCoordinatorInput.exchangeCode 永不被调用);oauth-login.test.ts:106 仍设置已删除的 MAKA_CLAUDE_SUBSCRIPTION_EXPERIMENTAL env flag;新增 retired UI 状态无 Storybook stories、E2E 未跑(仓库惯例 UI 状态要确定性 Storybook/Playwright 覆盖)。

@M4n5ter M4n5ter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

English

Requesting changes.

Correction: the compatibility-epoch issue was already reported in the top-level review comment from Astro-Han, so I removed my duplicate inline comment. That earlier comment also captures the current-main coordination correctly: main already uses epoch 24, so this PR needs the next distinct epoch rather than another bump to 24.

This review retains two independent actionable findings:

  1. Host session admission can still persist sessions targeting a retired connection when its credential remains stored.
  2. The authorization-code/paste-code OAuth path has lost its last production producer but remains across protocol, Host, runtime, and Desktop layers.

The Host admission gap independently warrants changes. The authorization-code comment intentionally covers the full producerless demand chain, extending beyond the narrower unused-#exchangeCode observation in the earlier review.

简体中文

建议 Request changes。

更正:compatibility epoch 问题已经由 Astro-Han 的顶层 review comment先行报告,因此我删除了自己的重复 inline comment。该评论对当前 main 的协调也更准确:main 已经使用 epoch 24,所以本 PR 需要下一个独立 epoch,而不是再次提升到 24。

本 review 保留两项独立且可执行的 finding:

  1. 当旧 credential 仍被保留时,Host session admission 仍能持久化指向退役 connection 的 session。
  2. authorization-code/paste-code OAuth 路径已经失去最后一个生产 producer,却仍横跨 protocol、Host、runtime 和 Desktop 层存在。

Host admission 缺口本身足以支持 Request changes。authorization-code 评论针对的是完整的无 producer demand chain,范围超过先前 review 中较窄的 unused #exchangeCode 观察。

Comment thread packages/core/src/provider-auth.ts
Comment thread packages/runtime-host/src/server/oauth-coordinator.ts

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

对抗性复核结论:当前 revision 不可合并。移除 Claude subscription OAuth 的方向正确,删除量也总体干净,但执行准入和协议边界还没有闭合。

  1. P1:breaking protocol 没有分配新的 compatibility epoch。

PR head 的 packages/runtime-host/src/protocol/index.ts:74 仍是 epoch 23;当前 main 已是 24。这个 PR 又删除了 OAuth provider/operation,所以合并结果会继续使用 24,epoch-24 的旧 Client/Host 能通过握手,再在 removed operation/provider 上中途失败。这里需要占用一个新的独立 epoch,并与其它并行 breaking PR 协调,不能依赖别的 PR 的 bump。

  1. P1:保留的旧 credential 会让 retired connection 通过 Host session admission。

packages/core/src/provider-auth.ts:93-100 对 retired provider 仍返回 requiresSecret: truepackages/storage/src/runtime-policy/coordinator.ts:562-587 只据此准备 credential,存在旧 token 时返回 readypackages/runtime-host/src/server/session-catalog-coordinator.ts:653-679 信任该结果,所以 default/explicit session.create 或 configuration update 可以持久化指向 retired Claude connection 的 Session,真正执行时才在更深层失败。

请把 retired/unavailable 作为 execution resolver 的一等结果,在 create/update commit 前拒绝,并覆盖 retained credential 下的 default 与 explicit target。

  1. P2:升级前的 retired Claude default target 没有释放。

packages/storage/src/runtime-policy/connection-catalog-document.ts:127-135 只清理旧 Gemini preview;Claude connection 保留后,其 defaultTarget 也原样保留。session-catalog-coordinator.ts:716-752 会继续选择它;apps/desktop/src/renderer/settings/providers-panel.tsx:266-270 还会显示“默认”,但不给用户切换该连接的操作。即使另有健康连接,新 Session 默认仍落到退役目标。迁移/读取时应释放 retired default,或提供等价的统一修复路径。

  1. P2:request_authorization_code / paste-code OAuth 链路已无生产 producer,却仍横跨 protocol、Host、runtime 和 Desktop 存在。

当前 Codex 与 xAI 都只产生 open_external,但 packages/runtime-host/src/protocol/oauth.ts:53-74server/oauth-coordinator.ts:69,124,452-476、Desktop pending-code/IPC、client presenter 以及 packages/runtime/src/oauth-login.ts:139 的 authorization-code exchange 仍被保留。应端到端删除这条无需求的敏感鉴权表面;未来有真实 provider 需要时再随 producer 引入。

对应地,apps/desktop/src/main/__tests__/runtime-host-oauth-ipc-main.test.ts:90 把 Codex fixture 伪造成 paste-code 流程,未覆盖真实 open_external 行为,应替换而不是继续维持死接口。

结论:方案目前不最优;需要局部根治,不需要推翻 retained-but-retired registry 设计。可删除完整 paste-code 链路、无调用的 exchange 注入、废弃 Claude env fixture 和重复 union member。CI 全绿,但没有覆盖 retained credential 的 Session admission、retired default migration 和合并后 epoch;这些是 merge blockers。

@Joob1n

Joob1n commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

@M4n5ter @hqhq1025 — on the request_authorization_code teardown you both raised: I agree it should go, and I am doing it as the immediate follow-up rather than inside this PR. Reasoning first, then the exact scope so it is a commitment you can hold me to rather than a promise to clean up later.

Why split. This PR's claim is "a provider that violates Anthropic's Consumer Terms no longer exists." The paste-code teardown is cleanup that removal enables — it is producerless because of this PR — but it is not needed for that claim to hold, and nothing in the retirement is incorrect while the dead path exists. Verifying them together also means one reviewer has to check both "the provider is gone from seven authorities" and "codex and xai device login still work after an OAuth-presentation teardown," which are unrelated failure modes. It matters for revert granularity too: if the teardown breaks a device flow in a way the tests miss, reverting it should not drag the Consumer Terms fix back with it. This PR is already 73 files; the teardown adds 14 across four packages.

I am taking the cost of that honestly: the teardown is another wire change and will need its own epoch, so splitting spends two epochs instead of one.

PR B scope — exactly this, nothing else.

Removed end to end:

  • protocol/oauth.ts — the request_authorization_code member of OAuthPresentationMethod, its request variant, the authorization_code result variant, and their decoders
  • client/oauth-presentation.ts — the request_authorization_code case and the requestAuthorizationCode backend hook
  • Desktop runtime-host-oauth-presentation.tsrequestAuthorizationCode, submitAuthorizationCode, and the pending-code state they drive
  • Desktop runtime-host-oauth-ipc-main.ts — the complete-authorization channel and handler
  • server/oauth-coordinator.ts#exchangeCode, the exchangeCode input, the exchangeOAuthAuthorizationCode import, and the exchange/timeout machinery around them. This is the root-cause removal @M4n5ter asked for, which is why I have deliberately not deleted the unused #exchangeCode field in this PR — deleting the field alone is the shallow fix you warned against
  • runtime/oauth-login.tsexchangeOAuthAuthorizationCode, buildOAuthLoginAuthorization, pkceChallengeFromVerifier, assertPkceVerifier and the PKCE/state helpers only they use. Checked: buildOAuthLoginAuthorization has no production caller either — xai is a device flow despite its contract naming a loopback presentation, so the whole code-flow half of this module is producerless
  • core/oauth-subscription.tsparsePastedAuthorization, constantTimeStringEqual, PENDING_AUTHORIZATION_TTL_MS and the paste-code doc comments, once the above stop importing them
  • the tests that exist only to cover the removed path

Kept, because the live device flows import them: requestOAuthEndpointJson, requestOAuthTokenEndpointJson, decodeOAuthInitialTokenPayload, OAUTH_LOGIN_MAX_TOKEN_CHARS, OAuthTokenEndpointError. Verified against the import lists in codex-oauth-enrollment.ts and xai-oauth-enrollment.ts.

Also in PR B: runtime-host-oauth-ipc-main.test.ts:90, which fakes the Codex fixture as a paste-code flow. @hqhq1025 is right that it should be replaced rather than kept alive against a dead interface — but replacing it means asserting open_external, which is the presentation the teardown leaves as the only one, so it belongs with the teardown rather than ahead of it.

Everything else you both raised — the epoch, Host session admission with a retained credential, the retired default target, the override-ordering bypass @coderabbitai found, and the MAKA_CLAUDE_SUBSCRIPTION_EXPERIMENTAL fixture — I am fixing in this PR. Detail in a follow-up comment once they are pushed.

If either of you would rather have the teardown here despite the above, say so and I will fold it in.

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@Joob1n The split is reasonable.

The current PR can remain focused on retiring claude-subscription. PR B has a clear end-to-end removal scope for the producerless request_authorization_code path. Separate review and revert scope are valid reasons to split it.

For PR B, remove the wire variants, desktop IPC channel, coordinator exchange path, runtime authorization-code helpers, and path-only tests in the same change. Keep the device-flow helpers that Codex and xAI still import.

Please open a tracked follow-up issue for PR B, or link the follow-up PR here when it exists. This will make the remaining protocol cleanup explicit and reviewable.

I will review the current-PR fixes after they are pushed.

You are interacting with an AI system.

@Joob1n
Joob1n force-pushed the feat/retire-claude-subscription-oauth branch from 1d978df to 91c504e Compare August 18, 2026 11:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e1ff2d6a-840f-468f-a678-dadabaee4355

📥 Commits

Reviewing files that changed from the base of the PR and between 1d978df and 91c504e.

📒 Files selected for processing (14)
  • apps/desktop/src/main/__tests__/runtime-host-oauth-ipc-main.test.ts
  • apps/desktop/src/main/runtime-host-client.ts
  • apps/desktop/src/renderer/settings/providers-panel.tsx
  • packages/runtime-host/src/__tests__/session-catalog-coordinator.test.ts
  • packages/runtime-host/src/protocol/index.ts
  • packages/runtime-host/src/server/session-catalog-coordinator.ts
  • packages/runtime/src/__tests__/claude-subscription-runtime.test.ts
  • packages/runtime/src/__tests__/oauth-login.test.ts
  • packages/runtime/src/__tests__/provider-contract-matrix.ts
  • packages/runtime/src/model-runtime.ts
  • packages/storage/src/__tests__/runtime-policy-stores.test.ts
  • packages/storage/src/runtime-policy/connection-catalog-document.ts
  • packages/storage/src/runtime-policy/coordinator.ts
  • packages/storage/src/runtime-policy/operations.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/desktop/src/main/runtime-host-client.ts
  • packages/runtime/src/tests/claude-subscription-runtime.test.ts
  • apps/desktop/src/renderer/settings/providers-panel.tsx
  • apps/desktop/src/main/tests/runtime-host-oauth-ipc-main.test.ts

Included review availability: Your plan includes up to 3 reviews per rolling hour; 2 remain after this review.

Comment thread packages/storage/src/runtime-policy/connection-catalog-document.ts
@Joob1n

Joob1n commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Pushed. Point by point, and where I was wrong I have said so rather than quietly fixed it.

@Astro-Han / @hqhq1025 — P1/P2, the epoch. Fixed: 24 → 25 in protocol/index.ts, and this time I checked git diff origin/main -- protocol/index.ts actually shows the line before claiming it. The earlier claim was a 22 → 23 bump that a rebase turned into a no-op while the description kept asserting it; the "old clients are rejected by the gate" sentence was therefore describing a coincidence, exactly as you said. The description now states the bump this PR makes and why it takes its own rather than riding #3145's.

@M4n5ter / @hqhq1025 — P1/P2, Host session admission with a retained credential. Fixed, and you were right that my earlier reasoning was too convenient — I had considered guarding resolveExecutionConnection and talked myself out of it as "protocol churn", which ignored that Bot, CLI and scheduled runs never pass the Desktop readiness gate.

ResolveExecutionConnectionResult gains provider_retired as a first-class result rather than reusing an existing kind: disabled is a user choice they can undo, credential_not_configured is repaired by signing in, and a retired connection is neither — it holds a working credential and can still never execute, so folding it into either would make callers give wrong advice. SessionCatalogCoordinator refuses on that result before the Session is committed. Regressions cover both the default target and an explicit one; reverting the admission check turns both red, reverting the resolver check turns the storage case red.

@hqhq1025 — P2, the retired default target. Fixed, and thank you for catching the second half of it, which was mine: I had hidden "set as default" for retired connections but left the branch order such that a connection which already was the default still rendered the 默认 Badge with no control to move it — I removed a dead end and added a new one. Loading the catalog now releases a defaultTarget pointing at a retained retired connection (the existing retiredConnectionIds mechanism, which previously only covered fully-removed Gemini rows), and the panel checks retirement before either default state.

@coderabbitai — Major, the override-ordering bypass. Fixed: resolveModelRuntime refuses a retired provider before lookupModelProviderOverride, so an override cannot build an active adapter and skip the refusal. To be accurate about severity: no override for claude-subscription exists today — I checked every model id in its fallback list — so this was latent, not live. It is still worth closing because that table is generated from an external source and one row should not be able to undo a retirement.

@coderabbitai — the contract matrix and the Codex IPC fixture. Both fixed here. wireDimensionCell and the reasoning-replay cell now classify an unavailable adapter as not-applicable instead of falling through to a generated anthropic-messages contract. The desktop fixture now drives openExternal rather than requestAuthorizationCode@hqhq1025, you asked for this to be replaced rather than kept alive against a dead interface, and on reflection you were right that it does not need to wait for the teardown, since open_external is the live presentation today. I said in my earlier comment that it belonged with PR B; changing that here rather than silently.

@Astro-Han — P3, the mutation table overstated one row. Correct, and now stated in the description: reverting only the storage interactive-login allow list leaves the suite green, because the auth-contract gate independently produces the same refusal. That is defense in depth working, but the row claimed discriminating power it does not have.

@Astro-Han — P3, "impossible to unregister" was overstated. Also corrected in the description. isRetiredGeminiCliConnection is the precedent and unregistering is possible; keeping the entry is a product choice — the user has a credential on this machine and the connection is what makes it visible and deletable — not a technical necessity.

Also in this push: oauth-login.test.ts no longer sets the removed MAKA_CLAUDE_SUBSCRIPTION_EXPERIMENTAL flag; nothing in the tree references it now.

Still deliberately not done here: the #exchangeCode field stays, because @M4n5ter asked for the root-cause removal rather than a field deletion, and it dies with the paste-code teardown in the follow-up PR whose scope is in the comment linked above.

Not covered: the new retired UI states have no Storybook stories and I have not run the Playwright E2E suite; both are stated in the description rather than left implied.

Before/after screenshots for the UI/UX gate are in the PR body.

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Changed files relevant to reported fixes ==='
git diff --name-only origin/main...HEAD | rg \
  '(^packages/runtime-host/src/protocol/index\.ts$|^packages/storage/src/runtime-policy/|^packages/runtime/src/model-runtime\.ts$|^packages/runtime/src/__tests__/provider-contract-matrix\.ts$|^apps/desktop/src/main/__tests__/runtime-host-oauth-ipc-main\.test\.ts$|^packages/runtime-host/src/server/session-catalog-coordinator\.ts$|^packages/runtime-host/src/__tests__/session-catalog-coordinator\.test\.ts$|^packages/storage/src/__tests__/runtime-policy-stores\.test\.ts$|^packages/storage/src/runtime-policy/connection-catalog-document\.ts$|^apps/desktop/src/renderer/settings/providers-panel\.tsx$|^packages/runtime/src/__tests__/claude-subscription-runtime\.test\.ts$|^packages/runtime/src/__tests__/oauth-login\.test\.ts$)' || true

echo '=== Targeted outlines ==='
ast-grep outline packages/runtime/src/model-runtime.ts --items all --view expanded
ast-grep outline packages/storage/src/runtime-policy/coordinator.ts --items all --view expanded
ast-grep outline packages/runtime-host/src/server/session-catalog-coordinator.ts --items all --view expanded

echo '=== Epoch and retirement enforcement references ==='
rg -n -C 4 \
  'RUNTIME_HOST_COMPATIBILITY_EPOCH|provider_retired|isRetiredProvider|lookupModelProviderOverride|wireDimensionCell|reasoning.*[Rr]eplay|openExternal|requestAuthorizationCode|MAKA_CLAUDE_SUBSCRIPTION_EXPERIMENTAL' \
  packages/runtime-host/src/protocol/index.ts \
  packages/runtime/src/model-runtime.ts \
  packages/runtime/src/__tests__/provider-contract-matrix.ts \
  packages/storage/src/runtime-policy \
  packages/storage/src/__tests__/runtime-policy-stores.test.ts \
  packages/runtime-host/src/server/session-catalog-coordinator.ts \
  packages/runtime-host/src/__tests__/session-catalog-coordinator.test.ts \
  apps/desktop/src/main/__tests__/runtime-host-oauth-ipc-main.test.ts \
  apps/desktop/src/renderer/settings/providers-panel.tsx \
  packages/runtime/src/__tests__/claude-subscription-runtime.test.ts \
  packages/runtime/src/__tests__/oauth-login.test.ts || true

echo '=== Remaining removed experimental flag references ==='
rg -n -C 2 'MAKA_CLAUDE_SUBSCRIPTION_EXPERIMENTAL' . -g '!node_modules' -g '!dist' -g '!build' || true

echo '=== Exact diff for relevant files ==='
git diff --unified=35 origin/main...HEAD -- \
  packages/runtime-host/src/protocol/index.ts \
  packages/runtime/src/model-runtime.ts \
  packages/runtime/src/__tests__/provider-contract-matrix.ts \
  packages/storage/src/runtime-policy/coordinator.ts \
  packages/storage/src/runtime-policy/operations.ts \
  packages/storage/src/runtime-policy/connection-catalog-document.ts \
  packages/storage/src/__tests__/runtime-policy-stores.test.ts \
  packages/runtime-host/src/server/session-catalog-coordinator.ts \
  packages/runtime-host/src/__tests__/session-catalog-coordinator.test.ts \
  apps/desktop/src/main/__tests__/runtime-host-oauth-ipc-main.test.ts \
  apps/desktop/src/renderer/settings/providers-panel.tsx \
  packages/runtime/src/__tests__/claude-subscription-runtime.test.ts \
  packages/runtime/src/__tests__/oauth-login.test.ts

Length of output: 156942


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== PR body ==='
gh pr view 3183 --repo maka-agent/maka-agent --json body,url --jq '"URL: \(.url)\n\n\(.body)"'

echo '=== Candidate tracked follow-ups ==='
gh search issues \
  --repo maka-agent/maka-agent \
  --state open \
  --limit 30 \
  --json number,title,url,body \
  --jq '.[] | select(
    ((.title + "\n" + .body) | test(
      "request_authorization_code|complete-authorization|exchangeOAuthAuthorizationCode|paste-code";
      "i"
    ))
  ) | "#\(.number) \(.title)\n\(.url)"' || true

Length of output: 8507


@Joob1n The amended implementation addresses the reported findings.

  • The compatibility epoch is now 25.
  • Storage returns provider_retired before credential resolution.
  • Session admission rejects retired default and explicit targets before persistence.
  • Runtime resolution rejects retired providers before model overrides.
  • Catalog loading clears retired default targets.
  • Settings suppresses the retired default badge and action.
  • The contract matrix marks unavailable adapters as not applicable.
  • The Codex fixture uses openExternal.
  • No MAKA_CLAUDE_SUBSCRIPTION_EXPERIMENTAL reference remains.

The Comment image satisfies the before/after UI screenshot requirement. It shows the retired composer state, model-list status, and connection-detail banner with unavailable actions removed.

One follow-up remains: the PR body links the teardown scope comment, but not a tracked issue or follow-up PR for request_authorization_code. Please create and link that follow-up before merge.


✏️ Learnings added
Learnt from: Joob1n
URL: https://github.com/maka-agent/maka-agent/pull/3183

Timestamp: 2026-08-18T11:56:10.342Z
Learning: For the TypeScript provider registry, `claude-subscription` remains registered with `retired: true` so stored connections and their local credentials stay visible and deletable. Retired providers must not be executable, selectable as defaults, or available for OAuth login.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking on the retirement as an end-to-end product change. The core direction is sound: keep the provider identity only so existing rows remain decodable and deletable, remove its OAuth/runtime implementation, and fail closed at execution. Four independent reviewer passes plus a read-only DeepSeek V4 Flash high-effort pass converged on the remaining boundary gap below.

The first-principles invariant should be: a retired provider may be decoded and deleted, but it cannot enter any ordinary mutation or action path. Today the read path clears a retired default and the execution path rejects it, while the catalog mutation API can still create a new claude-subscription row or commit it as a default because the provider remains a valid ProviderType; enabled retired rows can also still surface generic test actions. That creates states which a later read silently rewrites and actions which are guaranteed to fail.

The smallest coherent fix is to enforce retirement once at the storage mutation authority (create, setDefaultTarget, and any update that would make the row executable), then expose the same capability to shared action construction so test/default actions are omitted. Please keep decode/delete as the explicit exceptions. This is simpler than adding more renderer-specific guards and makes the retained registry entry serve exactly one compatibility purpose.

I did not run a local test suite; this review is based on the latest head, focused static tracing, the existing CI results, four independent @reviewer reviews, and a read-only ollama-cloud/deepseek-v4-flash high-effort review. AI-assisted review; I verified the reported control flow and discarded findings that were only artifacts of comparing this old branch head directly with current main.

中文评论

感谢把 Claude subscription 的退役做成端到端收口。核心方向是对的:只保留 provider 身份用于解码和删除旧连接,移除 OAuth/Runtime 实现,并在执行边界 fail closed。

剩余问题是 authority 没有完全统一:退休 provider 仍是合法 ProviderType,所以 mutation API 仍可新建该连接、把它设为默认,通用 action 层也仍可能提供测试入口;但读取时又会清空默认值,执行时必然拒绝。这会制造“写入成功、读回被改写”以及必然失败的操作。

更符合第一性原理和奥卡姆剃刀的方案,是在 storage mutation authority 一次性禁止退休 provider 的新建、设默认和重新启用,并让共享 action 构造消费同一 capability;仅把解码和删除保留为明确例外。这样不需要继续在各个 renderer 补零散判断。

本次未运行本地测试;结论来自最新 head 的静态追踪、现有 CI、四个独立 reviewer 和一次只读的 DeepSeek V4 Flash high-effort 审查。AI 辅助审查;我已人工复核控制流,并剔除了由旧分支与当前 main 直接比较产生的误报。

protocol: 'anthropic',
runtimeAdapter: { kind: 'claude-subscription' },
runtimeAdapter: { kind: 'unavailable' },
retired: true,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 — Reject retired providers at the catalog mutation authority. Keeping this registry entry is necessary for decoding existing rows, but it also keeps claude-subscription valid for CreateCatalogConnectionInput. ConnectionCatalogDocumentOwner.create() does not check isRetiredProvider, so an IPC/protocol caller can still create a brand-new enabled connection that can never execute. The same gap lets setDefaultTarget() return committed for a retired row, after which read() silently normalizes that default back to null. Please make decode/delete the explicit exceptions and reject create/default/re-enable mutations for retired providers in the storage owner; add focused tests for create and set-default rejection.

@Joob1n
Joob1n force-pushed the feat/retire-claude-subscription-oauth branch from ace2ca2 to 330cc83 Compare August 18, 2026 13:32
Maka could sign in with a Claude Pro/Max subscription and send inference
through it. Anthropic's Consumer Terms permit programmatic access to the
consumer Claude services only through an API key or explicit permission,
and this path had neither: it presented itself as Claude Code — borrowing
that client_id, its User-Agent, its beta header set and an `x-app: cli`
marker — to get requests accepted. The account carrying that risk is the
user's, not Maka's.

Remove the capability rather than gate it. `claude-subscription` keeps its
registry entry so a stored connection still decodes and renders, and is
marked `retired`, which is distinct from a provider that was never wired:
both have no Runtime adapter, but only one used to work.

Retirement is refused at each authority that could otherwise admit the
connection, so no single revert makes it sendable again:

- the auth contract hides every action, which is what makes the storage
  layer refuse a model fetch or a connection test
- the readiness gate reports `provider_retired` before the send is
  admitted, instead of letting it fail inside model construction
- the model catalog resolves every model to `provider_removed`, so the
  pickers stop offering them
- the interactive-login allow list and the Host wire enum no longer name it
- `getAIModel` and `resolveModelRuntime` throw as the last backstop

Settings explains the state instead of pointing at a sign-in that no longer
exists, and stops offering "set as default" and "test connection" for a
connection that cannot perform either. Deleting the connection is what
clears the credential this machine still holds.

The impersonation code goes with it: the cloaked request builder, the
Claude token endpoint and its client identity, the cloaked model-fetch
headers, and the subscription usage/quota path that needed that same
identity to read.

`RUNTIME_HOST_COMPATIBILITY_EPOCH` moves to 23: the OAuth login provider
enum and the account-usage operation both changed.

Generated-by: Claude Code
@Joob1n
Joob1n force-pushed the feat/retire-claude-subscription-oauth branch from 330cc83 to 66f94be Compare August 18, 2026 13:33

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest head closes the prior retirement gaps cleanly. The compatibility epoch is now 26; legacy rows remain decodable/deletable but cannot be created, enabled, or selected as defaults; default targets pointing at retired connections are released on read; Host admission rejects provider_retired before credentials or Session persistence; and the auth/model/runtime backstops prevent an override bypass. Current-main integration is clean and all live checks are green.

From first principles, provider retirement now has one enforceable storage/execution contract rather than relying on UI removal. The remaining producerless request_authorization_code / paste-code path can be deleted as a separate, tracked cleanup; it is no longer reachable through a live provider and does not block this retirement. Please also update the PR description's epoch note from 24→25 to the actual 25→26.

Approved. Reviewed with Codex using two independent reviewer agents and an external DeepSeek review; I verified the latest head, mutation/admission/model backstops, compatibility epoch, current-main merge, and live CI.

中文

最新 head 已完整闭合此前的退休缺口:compatibility epoch 现为 26;旧行仍可解码/删除,但不能新建、启用或设为默认;读取时会释放指向退休连接的默认目标;Host admission 在读取凭据或持久化 Session 前返回 provider_retired;auth/model/runtime backstop 也阻止 override 绕过。与当前 main 可干净合并,实时检查全绿。

按第一性原理,provider retirement 现在由统一的存储/执行契约强制,而不是依赖 UI 删除。剩余没有 producer 的 request_authorization_code / paste-code 路径可以作为独立、可跟踪的清理删除;它已无法通过现有 provider 到达,不阻塞本次退休。另请把 PR 描述中的 epoch 从 24→25 修正为实际的 25→26。

批准。本次由 Codex 配合两个独立 reviewer agent 和外部 DeepSeek 审查;我核验了最新 head、mutation/admission/model backstop、compatibility epoch、与当前 main 的合并和实时 CI。

@likun666661

Copy link
Copy Markdown
Member

复核最新 head 66f94be 后,核心退役链路已经基本闭合:retained credential 不再让 execution resolver 返回 ready,default/explicit Session admission 都会在 commit 前拒绝,retired default 会被释放,create/default mutation 也已收口;当前 CI 全绿。

还剩两个合并前建议修正的点:

  1. P2 — Command Palette 仍暴露必然失败的退休连接操作。 buildCommandPaletteCommands 只按 connection.enabled 过滤连接,所以升级后保留且仍为 enabled 的 claude-subscription 行仍会生成“设为默认”和“测试连接”命令(apps/desktop/src/renderer/command-palette-commands.ts:419-468)。前者会被新的 storage default-target gate 拒绝,后者会被 hidden auth action 拒绝;Settings 已经隐藏了这些操作,但命令面板仍提供死入口。请让共享 command construction 使用 retired/action capability 过滤,并补一个 retained-enabled connection 的回归。

  2. P3 — PR 描述中的 epoch 已再次落后于最新 rebase。 当前 diff 实际是 RUNTIME_HOST_COMPATIBILITY_EPOCH 25 → 26,描述仍写 24 → 25。实现是正确的,但发布/并行 breaking PR 协调依赖这里的事实,请同步更新描述。

request_authorization_code teardown 拆成后续 PR 的理由可以接受;建议把后续工作落成可追踪的 issue/PR,而不只保留评论承诺。

The command palette filtered per-connection commands on `enabled` alone,
so a retained retired connection — which stays enabled so its credential
remains visible and deletable — still offered "set as default" and
"test connection", both refused downstream by the storage default-target
gate and the hidden auth actions. The palette now checks the same
isRetiredProvider capability Settings uses, including the diag
test-default entry for a stale in-memory default. Regression covers a
retained-enabled retired row next to a live positive control; reverting
the loop filter turns it red.

Generated-by: Claude Code
Claude-Session: https://claude.ai/code/session_014ajaRxC4jydavY9nYUFj5J
@Joob1n

Joob1n commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

@likun666661 — both fixed in 6fc4447, and the teardown is now tracked.

P2, the palette. You were right, and it was the one shared surface the sweep missed: buildCommandList filtered on enabled alone, and a retained retired connection stays enabled by design — the row exists so the credential remains visible and deletable. It now checks the same isRetiredProvider capability Settings uses, in the per-connection loop and in the diag:test-default entry (the catalog releases a retired default on load, so that one guards the window where the renderer still holds the stale slug). The regression covers exactly the case you named — a retained, still-enabled retired row — next to a live non-default connection as the positive control, so an over-broad filter cannot pass it by dropping everything; reverting the loop filter alone turns it red. Desktop suite: 927/927.

P3, the epoch note. The description now states the actual diff, 25 → 26, verified against git diff origin/main before writing it this time.

The teardown promise is now #3219 rather than a comment: full file-by-file scope, what stays because the live device flows import it, and the epoch cost of the split.

@M4n5ter @hqhq1025 — your review requests are addressed as of this head (Host admission with a retained credential, the retired default target, the storage mutation gates, and the fixture replacement), and @Astro-Han has approved the retirement chain. Could you take another look when you have a moment?

Astro-Han
Astro-Han previously approved these changes Aug 18, 2026

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest delta closes the remaining Command Palette action surface for retained retired providers. It reuses the existing retirement authority, covers all three connection command paths, and includes a discriminating live-connection positive control. I found no new P0–P3 issue in this head.

The prior epoch, Host admission, retired-default, mutation-authority, and runtime/auth backstop findings remain resolved. The producerless authorization-code path is now explicitly tracked as separate follow-up work.

Reviewed with Codex using two independent review passes and DeepSeek V4 Flash as an external adversarial pass; I verified the latest delta and prior-finding state against this exact head and current main.

中文

最新 delta 已关闭 retained retired provider 在 Command Palette 中最后的 action surface。实现复用了现有 retirement authority,覆盖三类 connection command,并用 live connection 正向对照避免过度过滤。当前 head 未发现新的 P0–P3。

此前的 epoch、Host admission、retired default、mutation authority 和 runtime/auth backstop 问题仍保持已解决;producerless authorization-code 路径已明确拆为后续工作。

本次由 Codex 两轮独立审查,并使用 DeepSeek V4 Flash 做外部对抗审查;我核对了当前 head、最新 delta 与 prior findings。

@M4n5ter M4n5ter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

English

Requesting changes on the latest head.

The earlier epoch, default-target, general session-admission, mutation-authority, runtime-backstop, and Command Palette findings remain fixed. This pass found three non-duplicate residual paths:

  1. Removing oauth.account.usage.fetch makes previously issued Remote Host access files fail decoding, preventing the Host from starting. The known Remote Host population is almost nonexistent, so its practical reach is negligible, but the upgrade failure is deterministic for affected profiles.
  2. Configured subagent admission still considers retained retired connections available. Direct spawn and graph provisioning can consequently persist a child/operator that can never execute.
  3. The CLI /model projection still presents models belonging to the retired connection and only rejects them after selection.

The configured-subagent persistence gap independently warrants changes. Both remaining selection gaps can be closed by consuming the new canonical isRetiredProvider() authority rather than adding renderer- or caller-specific state.

The simplify audit found no additional merge blocker. The producerless authorization-code/paste-code path is already tracked by an existing review and is intentionally not duplicated here. Making retired connections fully read-only tombstones would be a reasonable follow-up simplification.

I verified these paths against head 6fc4447. Relevant targeted suites pass and all current GitHub checks are green; the missing retired-provider cases are not covered by those tests.

I’ll respond quickly to follow-ups on this PR, so please feel free to ping me directly.

简体中文

建议在最新 head 上 Request changes。

此前关于 compatibility epoch、default target、通用 session admission、mutation authority、runtime backstop 和 Command Palette 的问题仍保持已修复。本轮发现三个未与现有 review 重复的剩余入口:

  1. 删除 oauth.account.usage.fetch 会使此前签发的 Remote Host access file 解码失败,阻止 Host 启动。已知 Remote Host 用户几乎不存在,实际影响面可以忽略不计,但对命中的 profile 来说升级故障是确定性的。
  2. Configured subagent admission 仍把保留的 retired connection 视为可用,因此直接 spawn 和 graph provisioning 可以持久化一个永远无法执行的 child/operator。
  3. CLI /model 投影仍会展示属于 retired connection 的模型,直到用户选择后才拒绝。

Configured-subagent 的持久化缺口本身足以支持 Request changes。两个剩余选择入口都可以通过消费新的统一 isRetiredProvider() authority 来关闭,不需要增加 renderer 或 caller 专属状态。

简化审计没有发现额外 merge blocker。没有 producer 的 authorization-code/paste-code 路径已经由现有 review 跟踪,本次不重复评论。将 retired connection 进一步收敛为完全只读的 tombstone,可以作为后续简化。

我在 head 6fc4447 上验证了这些路径。相关 targeted suites 通过,当前 GitHub checks 全绿;但现有测试没有覆盖上述 retired-provider 场景。

对于这个 PR,我会尽快回复后续讨论;有需要请直接 ping 我。

Comment thread packages/runtime-host/src/protocol/operations.ts
Comment thread packages/core/src/provider-registry.ts
Three residual entry points from review, all consuming the canonical
isRetiredProvider() authority rather than caller-local state:

A previously issued Remote Host access file granting
oauth.account.usage.fetch failed decoding once the operation left the
protocol, deterministically blocking Host startup for that profile. The
grant is now released on decode — the same seam that migrates the
legacy transcript-query grant — because there is nothing to migrate a
retired operation to, and failing the whole file refuses a Host over a
capability it could not serve anyway.

Configured subagent admission judged a preset by enabled + model alone,
and a retained retired connection stays enabled by design, so a direct
spawn or graph provisioning could persist a child that can never
execute. provider_retired is now a first-class unavailability reason,
checked ahead of connection_disabled — there is no switch that repairs
it — refused by resolve() before anything persists, and surfaced with
its own copy in the desktop subagent settings list.

The CLI /model projection listed a retired connection's models and only
refused them after selection; it now filters them out with the same
authority.

No wire-contract change: the availability union lives in the runtime
package, not the protocol, so no epoch bump rides along. Each fix has a
regression that turns red when the guard alone is reverted; the runtime
suite's failing set is byte-identical to the same suite at the base
commit on this machine (environmental ripgrep/filesystem cases).

Generated-by: Claude Code
Claude-Session: https://claude.ai/code/session_014ajaRxC4jydavY9nYUFj5J
@Joob1n

Joob1n commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

@M4n5ter — all three closed in 8b45b5b, each through the canonical isRetiredProvider() as you asked, and none of them turned out to need protocol changes.

1 — the access-file upgrade failure. Real and deterministic, as you said. The fix reuses the seam the store already has for grants that outlived their operation: migrateStoredOperationGrants now releases a grant naming a retired operation, the way it already rewrites the legacy transcript-query grant — the difference being there is nothing to migrate oauth.account.usage.fetch to, so it maps to nothing. Failing the whole file would have refused Host startup over a capability the Host could not serve either way. Regression: an access file granting ['host.status', 'oauth.account.usage.fetch'] opens and authenticates with ['host.status']; reverting the release branch turns it red.

2 — configured subagent admission. The gap was that createConfiguredSubagentCatalog judged a preset by enabled plus the model list, and a retained retired connection stays enabled by design. provider_retired is now a first-class unavailability reason, checked ahead of connection_disabled — a disabled connection has a switch that repairs it, a retired one does not — so agent_list reports it and resolve() refuses before a spawn or graph provisioning persists anything. The desktop subagent settings list derives availability separately, so it gained the same branch and its own copy (登录方式已移除 · 请改用其他连接). Regressions on both layers with a live positive control; reverting either guard alone turns its test red.

3 — the CLI /model projection now filters retired connections at projectRuntimeHostModelChoices, with a disabled-connection case alongside so the filter is provably not over-broad.

No epoch bump rides along: the availability union lives in @maka/runtime, not in the protocol package, and the access-file change is decode tolerance for an already-removed operation — I checked packages/runtime-host/src/protocol for any of the new types before concluding that, given my record on this topic.

Suite state: the runtime suite's failing set on this machine is byte-identical between this head and the base commit (stashed, rebuilt, re-ran, diffed programmatically — the environmental ripgrep/filesystem cases), and the desktop suite is 930/930. On the read-only tombstone follow-up you sketched: agreed it is the natural next simplification once #3219 lands, and I would keep it out of this PR for the same revert-granularity reason as the teardown.

Joob1n added 2 commits August 19, 2026 14:01
…signature

Main's apache#2972 landed a test constructing createHostSessionEffectModel
with claudeDeviceId, which this branch removes with the provider that
needed it. The merge is otherwise clean; only the field goes.

Generated-by: Claude Code
Claude-Session: https://claude.ai/code/session_014ajaRxC4jydavY9nYUFj5J
@M4n5ter

M4n5ter commented Aug 19, 2026

Copy link
Copy Markdown
Member
English

Thanks for the detailed follow-up. I verified both 8b45b5b and the current-main integration fix in bded591: the three original root causes are addressed at the right authorities, the focused tests are discriminating, and the earlier merge-result compile failure is now closed. I also agree that none of these fixes requires another compatibility-epoch bump.

One user-facing selection path still remains. The Desktop preset list now reports provider_retired, but SubagentPresetEditor continues treating every enabled connection as usable:

  • usableConnections filters only on connection.enabled;
  • validConnection checks only selectedConnection?.enabled;
  • connectionOptions disables only connections where enabled is false.

A retained retired connection intentionally remains enabled, so the editor still lets users create and save a preset targeting it; the newly saved preset then immediately appears as provider_retired. Core admission now prevents this from persisting an unexecutable child, so the correctness failure is fixed, but the editor still offers a guaranteed-dead configuration.

Please exclude retired providers from the editor's selectable/valid projection using the same canonical helper. An existing retired selection can remain visible but disabled so the user can reroute it. A focused editor regression would distinguish this from the list-badge coverage already added.

The access-file thread is satisfied by the implementation. I’ll keep the combined selection thread open for this remaining editor path and re-check promptly after the update.

简体中文

感谢详细回复。我核验了 8b45b5b 以及 bded591 中对 current-main 集成问题的修复:原来三个问题都已在正确的 authority 上得到处理,新增测试具有判别力,之前合并结果中的编译失败也已经关闭。我同样认同这些修复不需要再次提升 compatibility epoch。

目前还剩一个用户可见的选择入口。Desktop preset 列表现在会正确显示 provider_retired,但 SubagentPresetEditor 仍把所有 enabled connection 当作可用:

  • usableConnections 只检查 connection.enabled
  • validConnection 只检查 selectedConnection?.enabled
  • connectionOptions 也只禁用 enabled 为 false 的 connection。

Retained retired connection 按设计仍保持 enabled,因此 editor 仍允许用户创建并保存一个指向它的 preset;该 preset 保存后会立刻显示为 provider_retired。核心 admission 已经阻止它持久化不可执行的 child,所以执行正确性问题已经修复,但 editor 仍在提供一个必然不可用的配置。

请使用同一个统一 helper,从 editor 的可选和有效 connection 投影中排除 retired provider。已有的 retired selection 可以继续显示为 disabled,方便用户改路由。建议增加一个 editor 层面的针对性回归测试,以区别于目前仅覆盖列表 badge 的测试。

Access-file thread 的实现已经满足要求。我会暂时保留合并后的 selection thread,等待这个 editor 入口关闭;更新后我会尽快复核。

Joob1n added 2 commits August 19, 2026 14:19
The trust-boundary step failed with insecure_endpoint_directory on a
tree whose merge introduced no endpoint or ACL delta, and the same main
commit passes the identical check on main. One rerun separates a runner
environment fault from something this branch causes.

Generated-by: Claude Code
Claude-Session: https://claude.ai/code/session_014ajaRxC4jydavY9nYUFj5J
…jobs

bded591 and 5da258d are byte-identical trees: the first failed only
windows_recovery (endpoint ACL) and passed the CLI Windows smoke, the
second passed windows_recovery and failed the CLI smoke with
INTERNAL_STARTUP_FAILURE during TUI setup. Every check has passed on
this tree at least once; this run is for one clean sweep.

Generated-by: Claude Code
Claude-Session: https://claude.ai/code/session_014ajaRxC4jydavY9nYUFj5J
@Joob1n

Joob1n commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

@M4n5ter @hqhq1025 — the three residual paths are fixed as of 8b45b5b (details in the comment above), and the branch has since merged current main — the only conflict was #2972's new DeepSeek auxiliary test constructing the effect model with claudeDeviceId, which this PR removes; the test keeps everything else. All 20 checks are green on 942e52e. The two single-check failures in this head's history were runner faults, established by running the identical tree twice: bded591 and 5da258d are byte-identical, the first failed only the windows_recovery ACL step and passed the CLI Windows smoke, the second the reverse — every check passed on this tree at least once before the clean sweep. Could you take another look?

@M4n5ter

M4n5ter commented Aug 19, 2026

Copy link
Copy Markdown
Member
English

Thanks — I confirmed that all 20 checks are green, and I agree that the two earlier Windows failures were runner flakes; they are now tracked separately in #3237. I also agree that 8b45b5b closes the three original residual paths.

However, this reply does not address the later Desktop preset-editor finding. On the current head 942e52e, SubagentPresetEditor still derives usableConnections, validConnection, and connectionOptions from connection.enabled alone. Because retained retired connections intentionally remain enabled, the editor can still create and save a preset that immediately becomes provider_retired.

No code has changed in that path since the finding was posted. I’m therefore keeping the selection finding open and cannot approve this head yet. Please apply the canonical retirement check to the editor’s selectable/valid projection while keeping an existing retired selection visible but disabled for rerouting.

简体中文

谢谢。我已确认当前 20 项检查全部通过,也认同此前两次 Windows 失败属于 runner flake;该问题现已由 #3237 单独跟踪。我也确认 8b45b5b 已关闭原先三个剩余入口。

不过,这条回复没有覆盖之后提出的 Desktop preset editor 问题。在当前 head 942e52e 上,SubagentPresetEditorusableConnectionsvalidConnectionconnectionOptions 仍然只根据 connection.enabled 判断。由于 retained retired connection 按设计仍保持 enabled,editor 依然可以创建并保存一个随后立即变成 provider_retired 的 preset。

该问题提出后,这条代码路径没有发生变化。因此我会继续保留 selection finding,目前还不能 approve。请在 editor 的可选及有效 connection 投影中使用统一的 retirement 判断;已有 retired selection 可以保持可见但禁用,方便用户重新选择连接。

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest delta correctly closes the three retirement gaps raised after the prior approval: released Remote Host grants migrate before validation, configured-subagent runtime admission rejects retired connections before child persistence, and CLI model projection filters retired choices. Deletion safety, protocol epoch 26, execution backstops, and all required checks also hold.

I am not re-approving yet because the existing M4n5ter thread identifies one remaining product projection: the Desktop Subagent editor still treats enabled as sufficient in usableConnections, validConnection, and connectionOptions. A retained enabled Claude connection can therefore be selected and saved into a preset that the runtime correctly refuses as provider_retired. I verified that current head still has this path, so I am leaving one deduplicated overall comment rather than repeating the inline finding.

AI-assisted review by Codex with three independent reviewer passes and OpenCode Go DeepSeek V4 Flash (high); I verified the post-approval delta, existing thread, current head, and live CI.

中文

最新增量已正确关闭上次批准后的三个缺口:旧 Remote Host grant 在校验前迁移;configured-subagent runtime admission 在持久化 child 前拒绝 retired connection;CLI model projection 过滤 retired choices。删除安全、protocol epoch 26、execution backstop 与必需 CI 也都成立。

暂不再次 Approve,因为 M4n5ter 已有线程指出剩余一个产品投影:Desktop Subagent editor 的 usableConnections、validConnection 和 connectionOptions 仍只判断 enabled。保留且 enabled 的 Claude connection 因此仍可被选择并保存成 runtime 必然以 provider_retired 拒绝的 preset。我已确认当前 head 仍存在该路径,因此只发去重后的整体评论,不重复 inline。

本次由 Codex、三个独立 reviewer 和 OpenCode Go DeepSeek V4 Flash high 辅助;已核对批准后的增量、已有线程、当前 head 与实时 CI。

The list badge said provider_retired, but the editor still judged a
connection by enabled alone in usableConnections, validConnection, and
connectionOptions, so a retained retired row could be selected and
saved into a preset the runtime admission is guaranteed to refuse. One
predicate — isSelectableSubagentConnection, enabled and not retired —
now feeds all of them plus the model derivations, and the option row
for an enabled-but-retired connection says why it cannot be picked
instead of graying out silently.

Generated-by: Claude Code
Claude-Session: https://claude.ai/code/session_014ajaRxC4jydavY9nYUFj5J
@Joob1n

Joob1n commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

@Astro-Han — the editor projection is closed in 985802b. One predicate, isSelectableSubagentConnection (enabled and not retired), now feeds usableConnections, validConnection, connectionOptions, and both model derivations, so the editor can no longer save a preset the runtime admission refuses as provider_retired. The option row for an enabled-but-retired connection says why it cannot be picked (登录方式已移除 · 请改用其他连接) instead of graying out silently — same pattern as the vanished-connection placeholder beside it. The predicate has direct regressions (retired-enabled, retired-disabled, live-enabled, live-disabled) in the presentation tests; desktop typecheck and suite green.

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest head correctly closes the Desktop Subagent-editor gap: one isSelectableSubagentConnection() predicate now excludes retired providers from usable models, validity, and options, with focused retained/disabled/live coverage.

The existing catalog-mutation authority thread is still only partially closed. Create/default/session admission now reject retired providers, but connection.catalog.update can take a retained disabled claude-subscription row and commit enabled: true; runtime then predictably rejects it as provider_retired. I am not duplicating that inline thread. The smallest coherent fix is to reject re-enabling retired rows at the storage mutation authority (or reject all ordinary updates if retained rows are decode/delete-only), with no revision change on rejection.

All live checks are green, but merge state remains blocked by unresolved review state.

Reviewed with Codex using two independent reviewer agents and OpenCode Go DeepSeek V4 Flash (high); I verified the exact delta, the prior Desktop finding, storage/runtime retirement invariants, focused tests, and live CI.

中文

最新 head 已正确修复 Desktop Subagent editor 缺口:统一的 isSelectableSubagentConnection() predicate 会在 usable models、validity 和 options 中排除 retired provider,并有 retained/disabled/live 聚焦覆盖。

但现有 catalog mutation authority 线程仍只部分关闭。create/default/session admission 已拒绝 retired provider;connection.catalog.update 仍可把 retained disabled claude-subscription row 提交为 enabled: true,随后 runtime 必然以 provider_retired 拒绝。我不重复发布该行内线程。最小一致修复是在 storage mutation authority 拒绝重新启用 retired row;若 retained row 只用于 decode/delete,则可直接拒绝所有普通 update,并保证 rejection 不增加 revision。

实时检查全绿,但 unresolved review state 仍使 merge state blocked。

本次由 Codex 配合两个独立 reviewer agent,以及 OpenCode Go DeepSeek V4 Flash(high)审查;我核验了精确增量、既有 Desktop finding、storage/runtime retirement invariant、聚焦测试和实时 CI。

M4n5ter
M4n5ter previously approved these changes Aug 19, 2026

@M4n5ter M4n5ter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

English

LGTM. The latest update closes the remaining subagent editor path, and the previously reported findings are resolved. All current checks pass.

简体中文

LGTM。最新更新已关闭剩余的 subagent editor 路径,之前提出的问题均已解决。当前检查全部通过。

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One new merge-blocking finding is attached inline.

The earlier hqhq1025 blockers around the compatibility epoch, retained-credential Session admission, and retired default targets are fixed on this branch. I did not duplicate the existing unresolved retired-provider update/re-enable thread.

This exact head also conflicts with current main, whose Runtime Host compatibility epoch is already 28. The required rebase must allocate the next distinct epoch for this wire removal, preserve both sides' authority changes, and receive a complete new-head review.

I reviewed the complete revision; required checks are green, build:test passed, and 228 focused local tests passed.

Codex-assisted review performed under the maintainer-approved review workflow.

// decoding, but nothing may author a new one: it could never execute, and
// reading the catalog back would immediately release it as a default.
// Decoding and deleting are the deliberate exceptions to that.
if (isRetiredProvider(input.connection.providerType)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Keep retained retired connections from aborting config restore

This guard is correct for ordinary authoring, but this revision deliberately retains existing claude-subscription connections and credentials so users can see and delete them. Config export includes every catalog connection, while a fresh-profile import saves connections sequentially. After any earlier live connections commit, the retained row reaches createConnection() and throws here, so the import exits before restoring settings, credentials, and memory, leaving a partial configuration.

This is the supported backup/restore path for the users affected by this retirement, not a forged input. Filter retired providers at import planning, count them as skipped, skip their credentials, and keep this storage guard. Add a full-bundle regression with a live connection before a retained retired one.

Config export carries every catalog connection, and this retirement
deliberately retains existing claude-subscription rows so the user can
see and delete them. A fresh-profile import saves connections one at a
time, so that row reached createConnection(), which refuses it — and the
refusal aborted the restore partway, after whichever live connections
had already committed and before settings, credentials, and memory. That
is the supported backup path for exactly the users this retirement
affects.

The storage guard stays: creating a retired connection is still refused,
because it could never execute. Import planning no longer hands it one —
the row is planned as skipped, and its credential is skipped with it,
since only a created or overwritten slug gets a secret written.

The regression restores a full bundle whose live connection is ordered
before the retained retired one, so an aborted import would look like a
partial success rather than a clean failure; reverting the filter turns
it red.

Reported by @hqhq1025.

Generated-by: Claude Code
Claude-Session: https://claude.ai/code/session_014ajaRxC4jydavY9nYUFj5J

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous config-restore P1 is fixed on this revision. Retired-provider connections are removed from the import plan before any connection save, their credentials remain unapplied, and the rest of the selected bundle continues normally. Exact-head build:test, 149 focused retirement/import tests, lint, format, and the Astryx surface inventory all passed.

I am leaving a COMMENT rather than approving because both hosted GitHub Actions suites are currently action_required, and the existing current-code update/re-enable thread remains unresolved. I found no new unique finding and did not duplicate that thread.

Codex-assisted review performed under the maintainer-approved review workflow.

@Joob1n

Joob1n commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

Could a committer approve the workflow runs on df59be7? Fork PRs need that click since the move into apache, and two runs are queued.

What changed since the last review, so the new head is not a black box:

@hqhq1025's P1 — config restore. You were right, and it was the worst kind of break: the users this retirement affects are exactly the ones whose backup carries a claude-subscription row, and the storage guard turned their restore into a partial one — connections saved up to that point, then nothing. Settings, credentials and memory never landed.

The guard stays, because creating a retired connection is still wrong. Import planning no longer hands it one: the row is planned as skipped, and its credential is skipped with it, since only a created or overwritten slug gets a secret written. The regression restores a full bundle whose live connection is ordered before the retained retired one — so a regression would look like a partial success rather than a clean failure — and reverting the filter turns it red.

The rebase you asked for. Current main is merged; nine conflicts, resolved individually rather than by taking a side:

  • chat-readiness.ts and its test: deleted on main by refactor(desktop): remove superseded local authority paths #3252, and our change to it was one copy string. Deletion accepted — the authoritative refusals live in isConnectionReady, resolveExecutionConnection and the Session admission, none of which moved.
  • claude-subscription-card.tsx: chore(ui): show signed-in mark in model provider list #3315 added an onLoginSuccess prop to the card this PR deletes. Deletion kept; provider-oauth-section.tsx takes main's shape with the claude branch removed, so the copilot panel keeps the new prop.
  • provider-registry.ts: both sides added a field, so both are kept.
  • connection-readiness.ts: main rewrote the ordering doc; the retirement rule is inserted at the position the code actually checks it, not appended.
  • provider-catalog-contract.test.ts: both sides added a describe block and both were cut mid-it by the conflict region, so ours is closed explicitly rather than borrowing main's closing braces.
  • astryx-surface-file-inventory.md: regenerated rather than hand-picked; 188 files.

Epoch is 30, not the 24→25 an earlier description claimed and not the 26 this branch carried before the rebase. Main reached 29 while this sat, and main also adopted a changelog comment format for that constant, which the new entry follows. Verified with git diff origin/main before writing this line, for reasons this PR's history explains.

Local state: build, typecheck, lint, format clean; the retirement contract suite is 9/9; the runtime suite's failing set is byte-identical to the same suite at the merge base on this machine (environmental ripgrep/PTY/filesystem cases), and eval's is the Docker preflight.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants