Skip to content

feat(route): make ReadyRouteCandidate immutable with sourced limit overrides#4562

Merged
Hmbown merged 1 commit into
mainfrom
agent/091-p1-immutable-route
Jul 19, 2026
Merged

feat(route): make ReadyRouteCandidate immutable with sourced limit overrides#4562
Hmbown merged 1 commit into
mainfrom
agent/091-p1-immutable-route

Conversation

@Hmbown

@Hmbown Hmbown commented Jul 19, 2026

Copy link
Copy Markdown
Owner

Summary

  • seal ReadyRouteCandidate behind private fields and read-only getters so a resolved route cannot be mutated after construction
  • represent context/output/tool-limit changes as ordered, sourced RouteLimitOverride requests applied by the resolver before the ready candidate is minted
  • preserve exact applied-override provenance and last-write precedence
  • report auth as ResolvedAuthSource::Unresolved during pure resolution; credential preflight remains outside the resolver
  • update TUI, Fleet, app-server, picker, diagnostics, and runtime call sites to consume the immutable contract

This is the bounded P1 route-authority slice from the v0.9.1 cutover plan. It changes construction/ownership, not provider selection policy.

Verification

  • cargo fmt --all -- --check
  • cargo test -p codewhale-config route --locked (76 passed)
  • cargo test -p codewhale-config --doc --locked (1 passed)
  • cargo test -p codewhale-tui --bin codewhale-tui --locked route_runtime::tests (12 passed)
  • cargo check -p codewhale-app-server --locked
  • cargo clippy -p codewhale-config -p codewhale-tui -p codewhale-app-server --all-targets --all-features --locked -- -D warnings
  • git diff --check origin/main...HEAD

Focused coverage includes unresolved auth JSON compatibility, all three limit fields, ordered repeated overrides, exact applied provenance, and existing auth variant wire spellings.

Current exact head: 4698f8b78c451a0e5cbabe73e0d46e13698cdefd.

…errides

Seal ReadyRouteCandidate behind private fields and read-only getters so
post-resolution limit mutation is impossible outside the resolver. Callers
request limit changes through RouteRequest::limit_overrides; the resolver
applies them in order before minting and records exact provenance.

Auth is reported as Unresolved at resolution time (credentials are never
inspected by the resolver). Route runtime plans Codex strip/roster, user
context, provider-reported Kimi context, and membership-plan floor overrides
as sourced requests instead of mutating a live candidate.

Verification: cargo fmt --check; cargo test -p codewhale-config route
(76); cargo test -p codewhale-config --doc (1); cargo test -p codewhale-tui
--bin codewhale-tui route_runtime::tests (12); cargo check/clippy -D warnings
for config, tui, and app-server; git diff --check.

Signed-off-by: Hunter B <hmbown@gmail.com>
Copilot AI review requested due to automatic review settings July 19, 2026 04:30
@cursor

cursor Bot commented Jul 19, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@claude

claude Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 2m 38s —— View job


Review: feat(route): make ReadyRouteCandidate immutable with sourced limit overrides

Summary

This is a clean, well-scoped refactor. ReadyRouteCandidate (crates/config/src/route/candidate.rs:180-315) is now genuinely immutable — private fields, read-only getters, pub(super) constructor, no Deserialize, plus a compile_fail doctest proving external mutation doesn't build. RouteResolver::resolve (crates/config/src/route/resolver.rs:107-232) applies RouteRequest::limit_overrides in order immediately before minting the candidate (resolver.rs:201-211) and records exact provenance via applied_limit_overrides — last-write-wins on repeated overrides for the same field, which is directly tested (resolver_applies_repeated_limit_overrides_in_request_order, resolver_records_exact_limit_override_provenance in route/tests.rs).

Provider/route safety (#2608): No regressions. The resolver still derives provider identity solely from explicit_provider/config, never from a wire-id prefix, and that invariant is unchanged by this diff — the new override plumbing only touches token-limit fields.

auth field semantics: ResolvedAuthSource::MissingResolvedAuthSource::Unresolved at resolver.rs:220-222 is a real behavior change, but it was already a hardcoded stub before this PR (never a real credential lookup), and no call site outside route/tests.rs matches on ResolvedAuthSource::Missing — this is a truth-in-labeling fix, not a functional regression.

route_runtime.rs two-pass resolution: resolve_route_candidate_with_context_metadata now resolves once to learn effective endpoint/limits, plans overrides (plan_limit_overrides, preserving the exact prior precedence order: Codex strip → Codex roster correction → configured → provider-reported → membership floor → catalog → fallback), then resolves a second time only if overrides are non-empty. Necessary given the immutability contract (no other way to mint a corrected candidate), and the resolver is a pure function of (offerings, request) so the two passes can't diverge on non-limit fields. Minor, non-blocking: this doubles the in-memory catalog scan on the override path, but the catalog is small and offline, so it's not a real perf concern.

Call sites (TUI, Fleet, app-server, picker, diagnostics, runtime threads): all mechanical fieldfield() conversions, correctly adding .clone() where a value now needs to move out of a borrow (e.g. endpoint().base_url.clone()). Spot-checked several RouteRequest { .. } construction sites for the new limit_overrides field — all 6 are accounted for.

Minor nit (non-blocking): crates/tui/src/fleet/worker_runtime.rs:203-205candidate.canonical_model().as_ref() is redundant since canonical_model() already returns Option<&ModelId>; the trailing .as_ref() is a no-op that compiles via auto-deref. Not worth a follow-up on its own.

Tests: Strong, targeted coverage — unresolved-auth JSON compatibility, all three limit fields, ordered/repeated overrides, exact provenance, and existing ResolvedAuthSource wire-spelling compatibility, plus the full existing test suite updated mechanically to use the new getters.

No correctness bugs, no route-safety issues, and no missing test coverage found.

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Hmbown
Hmbown merged commit 7bf1fac into main Jul 19, 2026
23 checks passed
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.

2 participants