Skip to content

Rust parity · slice 12 — absorb main fixes: Fable 5.1 (0.025x, #74) + Opus 5.5 (0.05x, #102) cache-read + Gemini match parity #76

Description

@ropdias

Part of #27 · parity v1, slice 12 (after slice 11, #89). Absorbs:

  1. Fable 5.1 / Mythos 5.1 cache-read multiplier. Port of Cache-read multiplier is wrong on Claude Fable 5.1 / Mythos 5.1 (0.025x, not 0.1x) #74 (bash fix: fix: bill cache reads at 0.025x on Claude Fable 5.1 / Mythos 5.1 #75).
  2. Opus 5.5 cache-read multiplier. Port of Cache-read multiplier is wrong on Claude Opus 5.5 (0.05x, not 0.1x) #102 (bash fix: fix: bill cache reads at 0.05x on Claude Opus 5.5 #103).
  3. Gemini detection parity. A gap from slice 4 (Rust parity · slice 4/11 — input parsing (serde-typed Session) #82 / feat: input parsing — serde-typed Session (parity v1 · slice 4/11) #47), found in the audit and fixed here so slices 5–11 don't need a re-stack. See the section below.

Context

The parity stack (#43→#54) branched from main at 15ea443 (#41). After that, #74 changed a statusline behavior on main: Claude Fable 5.1 / Mythos 5.1 bill cache hits at 0.025x base input, and every other model stays at 0.1x (pricing). The bash oracle now renders read(0.025x) and a different eq / saving % on those models. Then #102 added a third price: Claude Opus 5.5 bills cache hits at 0.05x. The Rust port still hardcodes 0.1, so it has fallen behind the oracle.

What tokenline.sh does now (after #75 and #103)

  • The single jq pass also reads (.model.id // "") as the last field, so the existing indices are unchanged.
  • It sets claude_read_mult (a string, default "0.1") from the lowercased model.id or the lowercased display_name, first match wins:
    • (fable|mythos)[- ]5[-.]1([^0-9]|$) → "0.025". Matches: claude-fable-5-1, claude-mythos-5-1[1m], Fable 5.1 (1M context). No match: claude-fable-5, claude-fable-5-10.
    • opus[- ]5[-.]5([^0-9]|$) → "0.05". Matches: claude-opus-5-5, claude-opus-5-5[1m], Opus 5.5 (1M context). No match: claude-opus-5, claude-opus-5-50.
  • In the non-Gemini branch of compute_turn_breakdown, read_mult="$claude_read_mult". Labels 0.025x / 0.05x / 0.1x. Write stays 1.25x (5m) / 2x (1h), output stays 5x, and Gemini is untouched.

What to change in rust/

  • input.rs:
    • add #[serde(default)] pub id: String to Model;
    • add the Claude cache-read multiplier to Session (value + label, e.g. an enum Standard / Opus55 / Fable51), with the same matches and precedence as bash.
    • Match by hand: regex is not a dependency (serde, serde_json, jiff only). Unit tests for the matching and non-matching ids and names above.
  • economics.rs:
    • the non-Gemini tuples (5m and 1h) hardcode (0.1, "0.1", …); take the read value/label from the session (0.025 / 0.05 / 0.1);
    • tests for 5m and 1h, including read_label == "0.025x" / "0.05x" and the eq / saving math.
  • ratelimits.rs: its test helper session() builds Session field by field, so add the new field there.
  • tests/golden: add a Fable 5.1 fixture and an Opus 5.5 fixture (with model.id). Capture its golden output from the fixed bash (main after fix: bill cache reads at 0.025x on Claude Fable 5.1 / Mythos 5.1 #75) and check byte-exact parity.

Gemini detection parity

  • Bash (the oracle): [[ "$model" =~ [Gg]emini ]] matches the substring Gemini or gemini only.
  • input.rs today: display_name.to_lowercase().contains("gemini") matches any casing, so GEMINI or gEMINI is classified as Gemini. That switches the multipliers and drops the rate-limit line, where bash would not.
  • Real hosts send Gemini 2.5 Pro, so this is a byte-parity gap rather than a user-visible bug. v1's contract is still byte-exact with bash.

To do:

  • input.rs: is_gemini = name.contains("Gemini") || name.contains("gemini").
  • Unit tests: Gemini 2.5 Pro and gemini-2.5-pro → true; GEMINI and Opus 4.8 → false.
  • Golden: a fixture with an all-caps name, captured from bash.

Sequencing

This is slice 12. It lands after slice 11 as its own PR into rust-parity-v1. Don't amend the open slice PRs: injecting commits into the stacked branches shifts their tips and forces a re-stack of everything above them. Merge #75 and #103 first, so the golden output comes from the corrected oracle.

Done when

cargo test passes, and the Fable 5.1 and Opus 5.5 golden fixtures are byte-identical to tokenline.sh output for the same payload.

Activity

  1. added
    area:statuslinetokenline.sh render path (model/context/cache, economics, rate limits)
    effort:SOne file + check (~1h)
    on Sep 15, 2026
  2. changed the title [-]Rust parity: port the Fable 5.1 / Mythos 5.1 cache-read multiplier (#74)[/-] [+]Rust parity · slice 12 — absorb main fixes: Fable 5.1 cache-read 0.025x (#74) + Gemini match parity[/+] on Sep 15, 2026
  3. changed the title [-]Rust parity · slice 12 — absorb main fixes: Fable 5.1 cache-read 0.025x (#74) + Gemini match parity[/-] [+]Rust parity · slice 12 — absorb main fixes: Fable 5.1 (0.025x, #74) + Opus 5.5 (0.05x, #102) cache-read + Gemini match parity[/+] on Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:statuslinetokenline.sh render path (model/context/cache, economics, rate limits)effort:SOne file + check (~1h)refactor

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions