Skip to content

fix(routing): define hard cost-cap behavior when cost evidence is unknown - #1344

Closed
abhisheksharma2411 wants to merge 135 commits into
lidge-jun:mainfrom
abhisheksharma2411:fix/1181-cost-cap-unknown-evidence
Closed

fix(routing): define hard cost-cap behavior when cost evidence is unknown#1344
abhisheksharma2411 wants to merge 135 commits into
lidge-jun:mainfrom
abhisheksharma2411:fix/1181-cost-cap-unknown-evidence

Conversation

@abhisheksharma2411

@abhisheksharma2411 abhisheksharma2411 commented Aug 9, 2026

Copy link
Copy Markdown

Fixes #1181.

Summary

limits.maxEstimatedCostUsd is documented as a hard per-request ceiling, but it never fires on the live routing path.

evaluatePolicyProfile only excludes a candidate when the estimate is a finite number (src/routing/evaluator.ts):

const overCostLimit = costLimit !== undefined
  && typeof estimatedCost === "number"   // an unknown estimate passes silently
  && Number.isFinite(estimatedCost)
  && estimatedCost > costLimit;

…while routeModel assembles cost evidence without usage (src/router.ts):

cost: costEvidenceForCandidate({
  provider: candidate.provider,
  model: candidate.model,
  limitUsd: profile.limits.maxEstimatedCostUsd,   // no `usage`
}),

costEvidenceForCandidate correctly returns { limitUsd, incomplete: true } with no estimatedUsd, so on the live path the estimate is always unknown and any candidate passes a cap the operator configured as hard. The existing coverage passes only because it supplies usage directly — exercising a path production does not take.

As #1181 notes, failing closed unconditionally is not safe either: with usage unwired it would reject every live candidate whenever a cap is set, and it would silently change the documented dry-run contract.

This adds an explicit, opt-in policy instead:

{
  "limits": {
    "maxEstimatedCostUsd": 0.25,
    "onUnknownCost": "exclude"   // "allow" (default) | "exclude"
  }
}
  • "allow" (default) — preserves current behaviour and the documented contract exactly. No existing deployment changes on upgrade.
  • "exclude" — the ceiling becomes genuinely hard: a candidate whose cost cannot be proven under the cap is ineligible.

Unknown-cost exclusions emit a distinct cost-limit-unknown code, so a trace distinguishes known above the cap from cost is unknown — the operator-facing distinction the issue asks for.

Kept deliberately separate from unknownEvidence.cost, which governs how an unknown-cost candidate is scored rather than whether the ceiling applies. A test asserts the two mechanisms stay distinguishable.

File Change
src/types.ts OcxRoutingUnknownCostCapMode; onUnknownCost on OcxRoutingProfileLimits
src/routing/profile.ts Validation ("allow" / "exclude") + normalization
src/routing/evaluator.ts Cap policy under unknown evidence; cost-limit-unknown exclusion
tests/cost-cap-unknown-evidence.test.ts New — 5 cases

Verification

The defect was reproduced with a failing test before the fix: with a $0.000001 cap and live-path evidence (no usage), the candidate was still eligible and selected.

Commands run against the dev base:

bun test tests/cost-cap-unknown-evidence.test.ts     → 5 pass, 0 fail
bun test tests/cost-scoring.test.ts \
         tests/routing-profile.test.ts               → 27 pass, 0 fail
bun x tsc --noEmit                                   → clean
bun scripts/test.ts                                  → 10134 pass, 7 skip, 0 fail

New test cases:

  1. Repro — live-path evidence carries no estimate, so the hard cap never fires (documents current behaviour)
  2. Fail-closedonUnknownCost: "exclude" excludes the candidate and emits cost-limit-unknown
  3. Default unchanged — with no onUnknownCost, behaviour is identical to today
  4. Mechanism distinctnessunknownEvidence.cost: "exclude" emits unknown-price, not cost-limit-unknown
  5. Inert without a caponUnknownCost has no effect when maxEstimatedCostUsd is unset

Coverage on touched files: src/routing/profile.ts 89.8% lines; every added line in src/routing/evaluator.ts covered. No GUI changes, so no screenshot applies.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed. — The new field is documented via JSDoc on OcxRoutingProfileLimits and its validation message. Happy to add a docs entry if you point me at the right file.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults. — No secrets or auth touched. The default is deliberately "allow" so an upgrade cannot silently make every live candidate ineligible; the stricter behaviour is opt-in.

Notes for review

  • Default is unchanged, so this is additive and non-breaking.
  • Wiring real usage into the live routing path is deliberately not attempted here — that is the larger change Routing: define hard cost-cap behavior when runtime cost evidence is unknown #1181 warns about, and it is separable from defining the policy.
  • Happy to rename onUnknownCost, or fold it into unknownEvidence, if you would prefer a single mechanism. I kept them separate because they answer different questions.

Wibias and others added 30 commits August 9, 2026 00:43
Implement the CL-00 protocol conformance runner with manifest loading, assertion DSL evaluation, shipped adapter/bridge execution paths, and eight negative controls for the initial five protocol suites.
Rebase onto dev CL-00 merge, remove Chat→Responses observation projection, apply source-protocol SSE [DONE] rules, synthetic provenance, MCP actions, and Chat tool_call_id selectors per final Protocol V1 authority.
Roadmap unit for replacing the vision sidecar's provider-name model list
(every openai + every anthropic row) with a real image-input capability
filter, an allowed-model list on the wire, and a compact delegation-style
card.

The load-bearing finding is that catalog inputModalities is not a truthful
capability signal: applyProviderConfigHints deliberately adds "image" to a
model listed in provider.noVisionModels, because Codex gates attachments
client-side and a text-only entry would block the image before the sidecar
could run. A blind model therefore advertises image input, so noVisionModels
membership is a hard disqualifier checked before the modality list it
rewrote.

Two audit rounds with an independent reviewer are recorded in 002; the
write gate rejects only models that can be PROVEN blind, never merely
unknown ones, so an operator can still point at a model the catalog has
never heard of.

No production code in this commit.
The vision sidecar picker offered every model whose provider was openai or
anthropic, which is both too wide and too narrow: it listed models that
cannot see, while hiding capability behind a provider name.

This adds the predicate that answers the real question. Two rules make it
non-obvious, and both are load-bearing:

- provider.noVisionModels marks models the proxy describes images FOR, and
  applyProviderConfigHints deliberately adds "image" to their advertised
  modalities so Codex does not block the attachment client-side. A blind
  model therefore advertises image input, so list membership is a hard
  disqualifier checked BEFORE the modality list it rewrote.
- Catalog rows routinely omit inputModalities (live /api/models carries none
  for openai or anthropic rows), so the predicate is a tri-state and unknown
  stays eligible. Collapsing undefined into false would empty the picker.

visionEligibleModelOptions is the suggestion list and is deliberately NOT
the write gate: absence from it must never imply rejection. gpt-5.6-luna and
claude-haiku-4-5 are guaranteed per enabled backend so a cold catalog still
offers a usable describer.

No caller yet; the management API and dashboard consume this in later
layers.

Plan: devlog/_plan/260809_vision_sidecar_model_filter/010
…regression

test(omp): close final lidge-jun#1109 path-contract review gap
Stop the PR quality gate from marking review-ready when hygiene still fails (as on lidge-jun#1324), and re-run the gate when sponsorship or exception labels change.
Document the observed ~15-20 RPM burst ceiling on opencode-zen (and cross-link it on opencode-free), and enrich opaque Zen 429s with guidance plus a parseable Retry-After so Codex clients can back off.
CodeRabbit: Zen may omit Retry-After / X-RateLimit headers on generic 429s; synthetic backoff is only added when upstream omits Retry-After.
Keep rename sources in sponsored-surface checks, load hygiene scripts from the PR base SHA, and exempt sponsorship only for write-capable repository permissions.
Yuxin-Qiao and others added 12 commits August 9, 2026 16:14
Address review findings on lidge-jun#1205:

- chatgpt-codex-connector P2: preserveReasoningContentModels only opts
  models into replaying reasoning that exists; MiniMax-M3 low effort maps
  to thinking disabled, so a fabricated placeholder could reach
  non-thinking histories. Add requiresReasoningPlaceholderModels
  (registry/derive/router/oauth/auth-cors plumbing, docs-site table)
  defaulting to the preserve list; minimax/minimax-cn seed [] to opt out.
  Custom preserve-only provider configs keep the lidge-jun#1193 fix via fallback.
- CodeRabbit minor: treat a falsy cache hit as a miss in the orphan-repair
  path (defense-in-depth; the write path already rejects empty strings).

Refs lidge-jun#1193
…outs durable

Address the remaining review findings on lidge-jun#1205:

- chatgpt-codex-connector P2: the orphan-repair fallback checked only
  requiresReasoningPlaceholderModels, so a requires-only custom entry could
  fabricate reasoning_content on a path the main assistant history would
  never emit it on. Gate the orphan placeholder on the preserve list too.
- chatgpt-codex-connector P2: Zhipu BigModel GLM ids are thinking-toggle
  models (low maps to disabled) AND preserve-listed, so the placeholder
  default could fabricate reasoning for non-thinking histories. Seed
  requiresReasoningPlaceholderModels: [] for zhipu-bigmodel, matching the
  MiniMax opt-out.
- chatgpt-codex-connector P2: OAuth reconcile deleted an explicit
  requiresReasoningPlaceholderModels: [] opt-out on every startup because no
  OAuth preset seeds the field. Keep the field out of OAUTH_RECONCILE_FIELDS;
  registry seeds still reach existing rows via enrichProviderFromRegistry.
- CodeRabbit minor: fix Korean spelling in the providers table.

Refs lidge-jun#1193
feat(lab): CL-02 immutable evidence ledger and projection
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Aug 9, 2026
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • wrong target branch (main); retarget to dev.

What to do

  • Retarget this PR to dev — all contributions go to dev.
  • Tick all four boxes in the PR description once you're done (currently 0/4).

Review readiness checklist

  • ⬜ All CI tests are green on my local testing.
  • ⬜ I pushed my PR to the latest dev commit.
  • ⬜ I resolved all correct Codex and CodeRabbit findings.
  • ⬜ My PR is ready for review.

0/4 boxes ticked.

Its title has been prefixed with [WRONG BRANCH].
This pull request was already a draft. Its draft status will be preserved after every issue above is resolved.
@abhisheksharma2411 Tick the boxes once your local CI is green, your branch is on the latest dev commit, and every correct Codex and CodeRabbit finding is resolved.

@github-actions github-actions Bot changed the title fix(routing): define hard cost-cap behavior when cost evidence is unk… [WRONG BRANCH] fix(routing): define hard cost-cap behavior when cost evidence is unk… Aug 9, 2026
@github-actions
github-actions Bot marked this pull request as draft August 9, 2026 08:32
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f05f0179-bc9f-4f58-9145-3dd564a13923

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

Unknown cost cap handling

Layer / File(s) Summary
Cost policy contract and normalization
src/types.ts, src/routing/profile.ts
Adds OcxRoutingUnknownCostCapMode, validates "allow" and "exclude", and preserves onUnknownCost in normalized profiles and revisions.
Unknown estimate evaluation
src/routing/evaluator.ts
Keeps known over-limit estimates under cost-limit. Unknown estimates use cost-limit-unknown and become ineligible only when the policy is "exclude".
Regression coverage
tests/cost-cap-unknown-evidence.test.ts
Tests default allow behavior, fail-closed behavior, separate scoring policy handling, and behavior without a configured maximum cost.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RoutingProfile
  participant RoutingEvaluator
  participant Candidate
  RoutingProfile->>RoutingEvaluator: Provide normalized cost limits
  RoutingEvaluator->>Candidate: Read estimated cost
  alt Known estimate exceeds cap
    RoutingEvaluator->>Candidate: Exclude with cost-limit
  else Unknown estimate and policy is exclude
    RoutingEvaluator->>Candidate: Exclude with cost-limit-unknown
  else Unknown estimate and policy is allow
    RoutingEvaluator->>Candidate: Keep eligible
  end
Loading

Possibly related PRs

Suggested reviewers: ingwannu, lidge-jun, wibias

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The code and tests implement the policy, but no documentation changes show consistent behavior across dry-run, live routing, and logs [#1181]. Update routing documentation and verify that the policy, explanation, and trace code are consistent across dry-run, live routing, and logs.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the routing change for unknown runtime cost evidence and hard cost-cap behavior.
Out of Scope Changes check ✅ Passed Changes in src/routing/evaluator.ts, src/routing/profile.ts, src/types.ts, and the regression test directly support issue #1181.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/cost-cap-unknown-evidence.test.ts`:
- Around line 1-21: Update the documentation and test names in the cost-cap
behavior tests to reflect the evaluator’s current behavior: unknown cost
evidence is allowed by default, while exclusion is opt-in. Remove obsolete
reproduce-before-fixing text that says the explicit exclusion test fails until a
fix lands, including the header and the statement near the second test, and
rename both tests to clearly describe their default-allow and opt-in-exclusion
behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 72e55f8e-291a-41d9-aa70-52a02f1ae7c9

📥 Commits

Reviewing files that changed from the base of the PR and between 121f1ad and 1862f1c.

📒 Files selected for processing (4)
  • src/routing/evaluator.ts
  • src/routing/profile.ts
  • src/types.ts
  • tests/cost-cap-unknown-evidence.test.ts

Comment on lines +1 to +21
/**
* Reproduction for issue #1181 — "Routing: define hard cost-cap behavior when
* runtime cost evidence is unknown".
*
* The hard ceiling `limits.maxEstimatedCostUsd` is documented as a hard
* per-request cap. In the live routing path it never fires, because
* `router.ts` assembles cost evidence WITHOUT usage:
*
* costEvidenceForCandidate({ provider, model, limitUsd }) // no `usage`
*
* `costEvidenceForCandidate` then returns `{ limitUsd, incomplete: true }`
* with no `estimatedUsd`, and the evaluator's cap check
* (evaluator.ts:307-310) requires `typeof estimatedCost === "number"`, so an
* unknown estimate silently passes a cap the operator configured as hard.
*
* The existing test in cost-scoring.test.ts only exercises the cap with
* `usage: USAGE` supplied — i.e. on a code path production never takes.
*
* Test 1 documents current (fail-open) behaviour and passes today.
* Test 2 asserts the behaviour the issue asks for and FAILS until the fix
* lands, per the reproduce-before-fixing rule.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the obsolete reproduce-before-fixing text.

Lines 19-21 and Line 105 state that the explicit exclusion test fails until a fix lands. The evaluator now implements this behavior, so the test passes. Rename the tests and update the header to describe default allow behavior and opt-in exclusion behavior.

Proposed documentation update
- * Reproduction for issue `#1181` — "Routing: define hard cost-cap behavior when
- * runtime cost evidence is unknown".
+ * Regression coverage for issue `#1181` — "Routing: define hard cost-cap behavior
+ * when runtime cost evidence is unknown".
...
- * Test 1 documents current (fail-open) behaviour and passes today.
- * Test 2 asserts the behaviour the issue asks for and FAILS until the fix
- * lands, per the reproduce-before-fixing rule.
+ * Test 1 verifies the default fail-open policy.
+ * Test 2 verifies opt-in fail-closed behavior.
...
-  test("REPRO: live-path evidence carries no estimate, so the hard cap never fires", async () => {
+  test("default allow keeps an unknown-cost candidate eligible", async () => {
...
-  test("EXPECTED (fails until fixed): fail-closed cap excludes unknown-cost candidates", async () => {
+  test("opt-in fail-closed cap excludes unknown-cost candidates", async () => {

Also applies to: 73-73, 105-105

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/cost-cap-unknown-evidence.test.ts` around lines 1 - 21, Update the
documentation and test names in the cost-cap behavior tests to reflect the
evaluator’s current behavior: unknown cost evidence is allowed by default, while
exclusion is opt-in. Remove obsolete reproduce-before-fixing text that says the
explicit exclusion test fails until a fix lands, including the header and the
statement near the second test, and rename both tests to clearly describe their
default-allow and opt-in-exclusion behavior.

…nown

`limits.maxEstimatedCostUsd` is documented as a hard per-request ceiling,
but it never fires on the live routing path.

`evaluatePolicyProfile` only excludes a candidate when the estimate is a
finite number (evaluator.ts), while `routeModel` assembles cost evidence
without usage (router.ts), so `estimatedUsd` is always `undefined` live and
any candidate silently passes a cap the operator configured as hard. The
existing coverage passed only because it supplied `usage` directly,
exercising a path production does not take.

Fail-closed unconditionally is not safe either: with usage unwired, it would
reject every live candidate whenever a cap is set, and it would change the
documented dry-run contract.

This adds an explicit, opt-in policy instead:

  limits.onUnknownCost: "allow" | "exclude"    (default "allow")

- "allow" preserves today's behavior and the documented contract exactly.
- "exclude" makes the ceiling genuinely hard: a candidate whose cost cannot
  be proven under the cap is ineligible.

Unknown-cost exclusions emit a distinct `cost-limit-unknown` code so a trace
distinguishes "known above the cap" from "cost is unknown", which is the
operator-facing distinction lidge-jun#1181 asks for.

Kept separate from `unknownEvidence.cost`, which governs how an unknown-cost
candidate is *scored* rather than whether the *ceiling* applies. The two
mechanisms now emit distinct codes and are covered by a test asserting they
stay distinguishable.

Fixes lidge-jun#1181

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@abhisheksharma2411
abhisheksharma2411 force-pushed the fix/1181-cost-cap-unknown-evidence branch from 1862f1c to c27cafb Compare August 9, 2026 08:35
@github-actions github-actions Bot added the intake: hygiene-blocked Deterministic PR hygiene checks failed label Aug 9, 2026
@Wibias Wibias closed this Aug 9, 2026
@abhisheksharma2411 abhisheksharma2411 changed the title [WRONG BRANCH] fix(routing): define hard cost-cap behavior when cost evidence is unk… fix(routing): define hard cost-cap behavior when cost evidence is unknown Aug 9, 2026
@Wibias

Wibias commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

Malfunctioned PR a swell as to broad. Please open a new smaller PR.

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

Labels

bug Something isn't working intake: hygiene-blocked Deterministic PR hygiene checks failed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Routing: define hard cost-cap behavior when runtime cost evidence is unknown

7 participants