◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/API & SDK
⬡ API & SDK/2026-07-10Advanced

My Nightly Batch Was Quietly Running on a Bigger Model — Declaring Per-Task Model Ceilings and Enforcing Them

Scatter model names through your code and a cheap batch job will eventually run on an expensive model. Here is a manifest that declares a ceiling per task, a deny-by-default resolver, and a morning audit that catches drift — with working code.

Claude API128Cost DesignModel Selection3Operations21Governance

✦ Premium Article

I opened the billing page and my hand stopped on the trackpad.

Same number of nightly jobs as the month before. The number was 1.7x higher. When you run scheduled work unattended across several sites as a solo developer, a jump like that is not a billing anomaly. It is a signal that something in the design broke.

It took a few minutes to find. A small auxiliary task had been refactored six months earlier to pull its model name from a shared constant. That constant was named for the interactive default, and someone — me — later pointed it at a stronger model. Nobody lied. The judgment that this particular task was fine on a cheap model simply had never been written down anywhere the code could see.

The per-model entitlements and spend alerts that landed for Enterprise in July 2026 close exactly this hole at organizational scale. The same idea works at solo scale, and you can build it yourself. What follows is the implementation I run today across the four Dolice Labs sites.

Treat a model name as a contract, not a setting

To the code, model="claude-opus-4-8" is a configuration value. To operations, it is a statement about how much this task is permitted to spend per invocation.

Contracts scattered across config files get silently rewritten by refactors. So I split the two concerns.

Each task declares the minimum capability it needs and the maximum cost it tolerates. A resolver decides which concrete model satisfies those bounds.

With that split, rewriting a shared constant cannot lift a task above its ceiling. And a task that genuinely needs a stronger model now requires editing the manifest — which means the change shows up as a reviewable diff.

The manifest: what gets declared

Keep the declaration small. Anything larger stops being maintained.

FieldMeaningExample
ceilingStrongest model this task may usesonnet-5
floorBelow this, do not run at allhaiku-4-5
max_output_tokensPer-call output cap4096
daily_usdApproximate daily budget for this task alone0.80
on_ceiling_missBehavior when the request exceeds the ceilingdegrade / fail

The floor exists because silent downgrades are the worst failure mode in this system. Errors announce themselves. A weaker model producing plausible-looking output can go unnoticed for weeks. I wrote about that failure in depth in A Silent Drop to a Weaker Model Is Scarier Than an Error.

Here is a real manifest.

# config/model_entitlements.yaml
_ladder:            # cheapest first. the resolver trusts only this ordering
  - haiku-4-5
  - sonnet-5
  - opus-4-8
 
_aliases:
  haiku-4-5: claude-haiku-4-5-20251001
  sonnet-5:  claude-sonnet-5
  opus-4-8:  claude-opus-4-8
 
tasks:
  news_ticker_summarize:
    ceiling: haiku-4-5
    floor: haiku-4-5
    max_output_tokens: 1024
    daily_usd: 0.15
    on_ceiling_miss: fail
 
  article_draft:
    ceiling: sonnet-5
    floor: sonnet-5
    max_output_tokens: 8192
    daily_usd: 2.50
    on_ceiling_miss: degrade
 
  weekly_seo_synthesis:
    ceiling: opus-4-8
    floor: sonnet-5
    max_output_tokens: 4096
    daily_usd: 1.20
    on_ceiling_miss: degrade

_ladder is explicit so that nothing has to infer price ordering from a model string. Inference like that will betray you the moment a naming convention shifts. When a new model ships, a human edits those three lines. That is the only entry point.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦A 120-line deny-by-default policy layer that resolves each task to the cheapest model satisfying its declared ceiling and floor
✦A morning audit script that reconciles declared ceiling, resolved tier, and the model the API actually billed
✦Clear criteria for when to degrade-and-continue versus fail hard, and why cost drift and capability drift deserve opposite treatment
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⬡ API & SDK2026-10-16
Sending 40 Rows From Make to the Claude API Stops at Row 14 — Avoiding the 429 With One Subtraction
Why a no-code automation that sends spreadsheet rows to the Claude API one by one hits a 429 partway through, and how to fix it. Seven spacing strategies tested on a mock bucket, a formula for the Sleep delay, and settings that stop failed rows from vanishing.
⬡ API & SDK2026-10-13
Haiku 5.5 is cheaper per token, but your bill jumps fivefold past about 77k Haiku 4.5 tokens
On the price sheet, Haiku 5.5 costs roughly a tenth of Haiku 4.5. But the same text becomes about 30% more tokens, and a prompt over 100k tokens is billed at five times the rate. Here is the boundary in Haiku 4.5 terms, a script I actually ran, and the changes that return a 400.
⬡ API & SDK2026-10-08
Before Sonnet 4.5 retires, one script to find the arguments that will return a 400
claude-sonnet-4-5 retires on November 30. Swapping the model ID is not enough: budget_tokens, prefill, and tool_choice will fail. Here is a scanner built from the official migration guide, its real output, and the order I would fix things in.