Skip to content

feat: price Claude Opus 5 + fix adaptive-thinking gate for 4.7/4.8/5 - #27

Merged
marcmantei merged 1 commit into
mainfrom
feat/opus-5-worker-default
Jul 29, 2026
Merged

feat: price Claude Opus 5 + fix adaptive-thinking gate for 4.7/4.8/5#27
marcmantei merged 1 commit into
mainfrom
feat/opus-5-worker-default

Conversation

@marcmantei

Copy link
Copy Markdown
Owner

Summary

  • Opus 5 (claude-opus-5) replaces Opus 4.8 as the worker default in production config (/data/spacebot/data/config.toml, already applied on lira 2026-07-29). Same $5/$25 pricing tier as Opus 4.5+ — verified live against platform.claude.com/docs/en/about-claude/pricing and via the Models API (GET /v1/models/claude-opus-5, created_at: 2026-07-24). Added a pricing regression test alongside the existing Opus 4.8 test (not replacing it).
  • Side finding while validating: supports_adaptive_thinking() only matched the literal "4-6" substring, so Opus 4.7, Opus 4.8, and now Opus 5 never got thinking: {type: "adaptive"} set on requests — they've been silently running with thinking disabled. Widened the gate to the full 4.6-generation-and-later family (checked against live Models API capability data — all of Opus 4.6/4.7/4.8/5 and Sonnet 4.6/5 and Fable/Mythos 5 show thinking.types.adaptive.supported: true). Added regression tests for the previously-missed models.

Test plan

  • CI: cargo test (I don't have a local Rust toolchain — could not run tests before opening this PR; both changes are small and mechanical — a new pricing match arm and an array-based substring gate — but please confirm CI is green before merging)
  • Live-validated claude-opus-5 inference end-to-end via the real Claude-Code-identity/OAuth code path (claude_api.call()) before wiring it as the default — see conversation context
  • Confirmed pricing ($5/$25) against the live Anthropic pricing docs, not guessed

Opus 5 replaced Opus 4.8 as the worker default (2026-07-29, same $5/$25
pricing tier). Adds it to the pricing match arm and its own regression test.

While validating, found supports_adaptive_thinking() only matched the
literal "4-6" string, so Opus 4.7, Opus 4.8, and now Opus 5 never got
thinking:{type:"adaptive"} set — silently running with thinking off this
whole time. Widened the gate to the full 4.6+-generation family (checked
against the live Models API capability data) and added regression tests
covering it.
@shipyard-ci

shipyard-ci Bot commented Jul 29, 2026

Copy link
Copy Markdown

PR Review

1. Summary

This PR adds pricing for Claude Opus 5 ($5/$25 tier) and refactors the adaptive-thinking gate from a hardcoded 4.6-only check to a fragment-list approach covering Opus 4.7/4.8/5, Sonnet 5, and fable/mythos-5. Both changes include regression tests.

2. Findings

P2 — Potential substring over-match in supports_adaptive_thinking

+        "opus-5",

The fragment "opus-5" is matched via model_id.contains(...). Unlike the pricing arm which uses starts_with("claude-opus-5"), this uses contains, so it will also match hypothetical future ids like opus-50 or opus-5-legacy. More importantly, "sonnet-5" and "opus-5" will match any future generation like sonnet-5-9 — which may or may not be intended. Given the pricing file uses starts_with for the same family, the inconsistency is worth a deliberate decision. Not blocking since the comment explicitly frames this as "checked by family/prefix rather than a single generation string" (i.e., forward-matching later generations is the intent).

P3 — Inconsistent matching semantics between the two files

params.rs uses contains, pricing.rs uses starts_with. For the same conceptual model families this divergence is easy to trip over later (e.g., a bare opus-5 id would be priced by the claude-opus-5 arm only if prefixed with claude-, but adaptive-thinking triggers on the fragment alone). Consider a shared helper or at least a note. Nit-level, not a defect in behavior.

P3 — Test name length

+    fn adaptive_thinking_detected_for_opus_5_and_the_rest_of_the_4_6plus_family() {

Very long test identifier; fine functionally but slightly unwieldy.

3. Security

No security concerns. No secrets, auth, injection, or untrusted-input handling introduced. Changes are pure pricing/config logic with test coverage.

4. Verdict

APPROVE

The changes are well-tested, the pricing arm ordering constraint is respected (claude-opus-5 precedes the generic claude-opus-4 parent implicitly since it's a distinct prefix), and both new tests assert correct values. The contains vs starts_with inconsistency is a minor maintainability note, not a bug in the diffed behavior.


✅ Verification gate (closed-loop Regelkreis · spacedriveapp#58 Phase 1)

An adversarial audit could not ground any blocking finding in the actual diff, so the blocking verdict was downgraded to APPROVE to avoid a false fix cycle (cf. PR spacedriveapp#57/spacedriveapp#48).

Audit

Per-finding audit:

P2 — Potential substring over-match in supports_adaptive_thinking
Quoted evidence exists in diff: + "opus-5", and the .contains(*f) usage:

+    ADAPTIVE_ONLY_FRAGMENTS.iter().any(|f| model_id.contains(*f))

The pricing file confirms the starts_with contrast:

+        m if m.starts_with("claude-opus-5")

However, the finding itself concedes: "Not blocking since the comment explicitly frames this as ... forward-matching later generations is the intent." The review author explicitly labels this as NOT blocking, and the FINDINGS metadata marks it P2 but the body text disavows it as a defect. The concern is speculative ("hypothetical future ids like opus-50") — no such id exists in the diff, and the code comment documents the contains-by-family design as intentional. This is a maintainability observation, not a grounded defect in diffed behavior. PHANTOM (speculative; author concedes non-blocking; intent documented as deliberate).

P3 — Inconsistent matching semantics — P3 nit, does not block.

P3 — Test name length — P3 nit, does not block.

No P1 or genuinely-blocking P2 finding survives. The single P2 is self-disavowed as non-blocking and rests on hypothetical future model ids not present in the diff.

VERIFIED_VERDICT: APPROVE


AI Review · Verdict: APPROVE · Diff-Score: 0.86
Routed as code_review (100%) → github_code_review_flow · View AI traces

@shipyard-ci

shipyard-ci Bot commented Jul 29, 2026

Copy link
Copy Markdown

✅ PR approved — waiting for a human merge.

human-authored PR — the manual ship gate applies by design. Converged auto-merge (policy 2026-07-03) covers platform-authored PRs only; merge when you're ready

View AI traces

@marcmantei
marcmantei merged commit d8dd74d into main Jul 29, 2026
@marcmantei
marcmantei deleted the feat/opus-5-worker-default branch July 29, 2026 09:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant