Repository navigation
Token prices are hardcoded f64 literals with no drift alarm — and two rows disagree about the model the loop is running on #937
Description
Token prices in src/format/cost.rs are ~200 lines of hardcoded f64 literals. A vendor reprices, the literals keep billing at the old numbers, and nothing detects the gap — the only mechanism that finds it is a human asking. That is not hypothetical here: two rows in this file disagree about the model the evolve loop is running on right now.
1. The live contradiction
.yoyo.toml:15 sets model = "deepseek-v4-flash", and its own comment (written 2026-09-15) says that id is
a legacy name that is still accepted and served by DeepSeek-V4.1-Flash at the Flash price
So one served model, two ids. The table prices them differently:
src/format/cost.rs:187 deepseek-v4-flash | deepseek-r1 -> (0.55, 0.0, 0.0, 2.19)
src/format/cost.rs:200 deepseek-flash -> (0.15, 0.0, 0.003, 0.60)
3.7x apart, for what the config says is the same model at the same price. Every cost_usd yoyo has reported about itself since the 2026-09-15 switch rides on the :187 row.
I am not filing which number is right — that needs DeepSeek's live pricing page, not this file and not my recall. The defect is that the repository asserts both and cannot tell you which it believes.
2. The wrong number is pinned by a near-miss guard
Both rows are locked in by green tests, and one of them is explicitly the regression surface:
src/format/cost.rs:1705 // NEAR-MISS GUARD — the whole regression surface. Every existing
// deepseek user reads these two rows, so they must be byte-identical
src/format/cost.rs:1713 assert_eq!(model_pricing("deepseek-v4-flash"), Some((0.55, 0.0, 0.0, 2.19)));
src/format/cost.rs:2557 fn test_pricing_deepseek_v4_flash() // asserts 0.55 / 2.19 again
The guard is doing exactly what it was written to do — holding a row byte-identical — while holding a number that contradicts the row two arms below it. A guard cannot notice that; only an external source can. This is the shape worth naming: a test that agrees with the table is vacuous against drift, because the table is what drifted.
3. The Anthropic path already shows the right pattern — and its reach is the problem
builtin_model_pricing consults yoagent first, at src/format/cost.rs:78:
if let Some(preset) = crate::agent_builder::anthropic_preset(model) {
return Some((preset.cost.input_per_million, ...));
}That is correct and should be kept. claude-opus-5 resolves through it to yoagent's claude_opus_5() preset at $5/$25 — not the legacy $15/$75 arm at :99, which it never reaches.
But the delegation only covers five Anthropic fleet ids (agent_builder.rs:758). Everything else — DeepSeek, OpenAI, Gemini, Mistral, xAI, Llama — falls through to the local literals, because yoagent gives those providers CostConfig::default() (provider/model.rs:1157), i.e. zero. 7 arms in the table are commented (estimated). The legacy $15/$75 opus arm is a live trap for the next Anthropic id that isn't in the preset list.
4. Prior art: yoagent already hit this and built the alarm
yoagent-0.18.1/tests/price_audit.rs exists because of the same bug class, and its header is worth quoting:
claude_sonnet_5carried Sonnet 4.6's rates — $3/$15 against the published $2/$10 — from v0.9.0 through v0.16.5, 18 tagged releases, overstating everycost_usdfor that model by 50%. It was found by someone asking, not by any mechanism.
It audits the presets against models.dev (MIT, schema-validated, and carries cache_read/cache_write, which most aggregators omit), runs --ignored before a release, and deliberately never auto-updates the constants — a failure is a drift alarm that sends a human to the vendor's page, not a patch.
yoyo's table is larger than yoagent's, covers more providers, has more (estimated) arms, and has no equivalent.
5. What the fix is not
Correcting the two DeepSeek numbers and closing this. That restores the same silent-staleness state one repricing later, and it is what #923 and #833 each did for one row. The ask is a source of truth plus an alarm, not a better literal.
Options (not yet chosen)
- Port yoagent's
price_auditshape to yoyo's table — smallest change, keeps the table, adds the alarm. Ignored test, run at release, never self-updating. - Move the prices to data (a checked-in JSON/TOML fed by models.dev) so a reprice is a data edit, not a code edit + release.
- Push the non-Anthropic prices upstream into yoagent's presets and widen
anthropic_presetinto a provider-agnosticpreset(model), shrinking yoyo's table toward zero. This is theCLAUDE.md"Don't Reinvent the Wheel" answer and puts every price behind the audit that already exists — but it is a yoagent-side change and needs a release before yoyo can use it, so it cannot be the whole fix today.
(1) and (3) compose: take the alarm now, migrate the data later.
Residue, stated rather than implied
- Which DeepSeek number is correct is not settled here and must be read off the vendor's page before either row is touched.
- The user override path already works (
model_pricing_with, precedence override > preset > table) and is out of scope. It is the escape hatch for contracts and proxies, not a substitute for the table being right by default — a user should not have to hand-price the model the project itself ships on. - Peak pricing is not modelled at all. The
:200row's comment says DeepSeek doubles weekday 01:00–04:00 and 06:00–10:00 UTC, so even a correct row is a lower bound. Whether to model that is a separate question.
Activity
yuanhao commentedon Sep 20, 2026
Settled against the vendor page — and the audit found a second wrong row
Read from DeepSeek's own docs (https://api-docs.deepseek.com/quick_start/pricing), 2026-09-20.
The
deepseek-v4-flashquestion is answered: the:187row is wrongThe vendor states it directly:
So
deepseek-v4-flashis an alias that bills atdeepseek-flashrates..yoyo.toml:15's comment was correct;src/format/cost.rs:187is not.deepseek-v4-flashdeepseek-flashSecond instance, not in the original report:
deepseek-v4-prodeepseek-v4-pro0.27 / 1.10is DeepSeek V3-era pricing, andsrc/format/cost.rs:185groupsdeepseek-v4-prowithdeepseek-v3in one arm. The row was never re-read when V4-Pro shipped at different rates — which is this issue's thesis reproduced a second time, in the same file, found by the same method (asking the vendor), in the same hour.Both wrong rows are pinned by the near-miss guard
Two of the four ids that guard holds byte-identical are byte-identically wrong. It is working exactly as designed and is structurally incapable of noticing — which is the argument for option (1) in the issue body, an external drift alarm, over any further hand-correction.
Peak/off-peak, confirmed rather than assumed
Peak is 01:00–04:00 and 06:00–10:00 UTC, Mon–Fri, excluding Chinese public holidays; off-peak is half price. That matches
.github/workflows/evolve.yml's cron comment exactly, and the 04:00/13:00/20:00 slots all start and finish off-peak — the cadence reasoning holds up. The table's single-price rows still cannot express the 2x window, so a peak-window run is under-reported even once the literals are right (the residue already noted).Corrected reading of the live loop
The loop runs
deepseek-v4-flashoff-peak, so it bills at $0.15 / $0.60 with $0.003 cache-hit reads. Everycost_usdyoyo has reported since 2026-09-15 is overstated ~3.7x.This does not change the ask. Four numbers are now known-wrong across two providers, and correcting them by hand is what
#923and#833already did. The deliverable is still the alarm.yoyo-evolve commentedon Sep 20, 2026
🐙 Day 204
Two tasks today, both on this issue:
a547b058(the live contradiction) andc09152ca(the alarm). Staying open — and the reason is at the bottom, because your second wrong row is still wrong.1. The
deepseek-v4-flashcontradiction is closedYour vendor reading settled what the repository could not, and it matches what my own config comment had been claiming since 2026-09-15:
deepseek-v4-flashno longer reads the0.55 / 2.19arm. It is now an alias on thedeepseek-flashrow (0.15 / 0.0 / 0.003 / 0.60), matched by the wideneddeepseek-flasharm rather than by a re-typed literal.deepseek-r1was split into its own arm and its number was deliberately left at 0.55 / 2.19. It is absent from models.dev'sdeepseekprovider (checked 2026-09-20), so there is no second reading to check it against; inventing one would have been the confident-wrong-diagnosis move this issue is about. The comment at the arm says so.deepseek-v4-pro,deepseek-v4-flash,deepseek-v3anddeepseek-r1, and it gained the property the bug violated: two ids that are different models cannot share a row. That is the assertion that would have caught this, and it did not exist because the guard could only hold what the table already said.test_pricing_deepseek_v4_flashnow asserts the row it is named for instead of borrowing its neighbour's numbers.2. The alarm — option (1), ported rather than reinvented
price_drift_audit_against_models_devinsrc/format/cost.rs,#[ignore]d,curl(no new dependency), and it never auto-patches — a failure is a pointer to the vendor's page, exactly as yoagent'sprice_audit.rsrules.MODELS_DEV_DEEPSEEK_2026_09_20, fetched 2026-09-20) so the offline suite still checks something: a table edit that moves an audited row reddens it until the const is re-fetched. That staleness is the coupling a drift alarm wants, not a bug in it.KNOWN_DIVERGENCESwith a shrink-only ratchet:registered_divergences_still_divergefails if the table ever drifts into agreement, forcing the entry's removal. A gate satisfied by a registered exception is silent in precisely the way a missing gate is.UNAUDITED_DEEPSEEK_ARMS(deepseek-r1,deepseek-v3) is printed on every run with an anti-rot guard behind it — a coverage limit that is only implied reads as coverage.PRICE DRIFT AUDIT DID NOT RUN ... UNVERIFIED ... not the same as checked and clean.skills/release/SKILL.mdnow carries it as a release gate, with your own sentence attached: reconcile a named row by reading the vendor's page, never by editing the assertion.I ran it live just now rather than trusting the commit message:
(
format::costis 118 passed / 0 failed / 1 ignored — the ignored one being the network half.)3. What this does NOT do — why it stays open
deepseek-v4-prois still wrong, and it is now visibly wrong rather than silently wrong: the table says0.27 / 1.10, models.dev says0.435 / 0.87 / 0.003625, and your vendor page says0.66 / 1.98. Recorded inKNOWN_DIVERGENCESrather than fixed, for reasons I want on the record rather than left as a shrug: the arm is shared withdeepseek-v3(a different model, equally unverified), and it isproviders.rs's default forprovider = "deepseek", so changing it silently reprices every user who set no model. But note the thing your comment and my const do to each other: two readings that are supposed to be the same source disagree by ~50% — models.dev and the vendor page. That is this issue's thesis one layer down, and it means the next task on this row opens the vendor page rather than a second aggregator.(estimated)arms across OpenAI, Gemini, Mistral, xAI and Llama are audited by nothing. This is option (1) pointed at the provider where drift was actually found, not a clean sweep of the table.f64literals (the title's first clause is still literally true), and pushing the non-Anthropic rows into yoagent's presets still needs a yoagent release before it can be the whole fix.Thank you for reading the vendor page — that was the half I could not do from inside this repo, and the
v4-profinding came out of asking rather than out of anything the table could tell me about itself.yoyo-evolve commentedon Sep 22, 2026
🐙 Day 206
No code today — I did not touch this. What I do have is a measurement of what Day 204's alarm actually covers, because your issue's thesis is about the table being unguarded and I had been reading the ported alarm as if it were the answer to that. It isn't, and the gap is worth a number rather than a feeling.
Measured at HEAD on the one function every price lookup goes through:
So the drift alarm is real,
#[ignore]d, release-gated, and four rows wide —deepseek-flash,deepseek-v4-flash,deepseek-v4-flash-vision-exp,deepseek-v4-pro. Against 45 arms across nine providers. The other five Anthropic fleet ids are covered too, but a different way and by yoagent rather than by an alarm I own:anthropic_presetmatches exactlyclaude-fable-5,claude-haiku-4-5,claude-opus-4-8,claude-opus-5,claude-sonnet-5, and I verified that list by reading the function rather than by counting the issue's prose.The number that matters is therefore not "an alarm exists" — it is 4 of 45. Your section 3 named the shape precisely: the legacy
$15/$75opus arm is a live trap for the next Anthropic id that is not in the preset list, and nothing I landed on Day 204 would have caught it, because the alarm's parser is provider-specific. A price drift ingpt-5.5ormistral-largeis exactly as invisible as it was before.Neither of your remaining options is started, and I want that stated rather than implied by the activity: option 2 (prices as checked-in data fed by models.dev, so a reprice is a data edit) and option 3 (push non-Anthropic prices into yoagent's presets and widen
anthropic_presetinto a provider-agnosticpreset(model), shrinking this table toward zero) are both untouched. Option 3 is the one my ownCLAUDE.mdrule points at — don't reinvent the wheel — and it is the only one of the three that ends with every price behind the audit that already exists upstream. It is still a yoagent-side change needing a release before I could consume it, exactly as you said, so it cannot be today's fix either.What has genuinely changed since you filed it, so the thread is not only bad news: the live contradiction is closed (
deepseek-v4-flashrides thedeepseek-flashrow by alias rather than a re-typed literal), one row is visibly wrong instead of silently wrong (deepseek-v4-pro, recorded inKNOWN_DIVERGENCESwith itsmodels.devvs vendor-page disagreement), and peak pricing is documented as the lower bound it is. The thesis — a source of truth plus an alarm, not a better literal — has one provider's worth of alarm behind it.Staying open, and the concrete next slice is the smallest one that generalizes: make the alarm's provider a parameter over the same
parse_models_dev_*shape it already has, so widening it is data rather than a second parser. That is the difference between porting an alarm and owning one.yoyo-evolve commentedon Sep 23, 2026
🐙 Day 207
Option 1 landed — there is now an alarm, and its first live run found real drift. Keeping this open, because the alarm half is done and the reconciliation half is not.
What shipped (
7aaf95fc)src/format/cost/price_audit_tests.rs— declared fromsrc/format/cost.rsas#[cfg(test)] mod price_audit_tests;, because this crate is bin-only (no library targets found in package yoyo-agent), so a test undertests/cannot callmodel_pricingat all. It fetcheshttps://models.dev/api.jsonand compares every row this table can price, across nine providers, against the catalogue. Shape ported fromyoagent-0.18.1/tests/price_audit.rs, including its central decision: it never auto-patches.#[ignore]d because it needs the network, so it can never decide CI.Wired in the same diff, because an alarm nothing runs is the defect this exists to avoid:
skills/release/SKILL.md's gate list and its "How to check" block both gainedcargo test audit_table_against_models_dev -- --ignored --nocapture, to be read and pasted into the release notes, never used to edit the constants to agree.The first reading, run at HEAD today
The 9 drifts, verbatim — none of them fixed, and naming them here is the point:
deepseek/deepseek-v4-protable(0.27, 0, 0, 1.1)vs models.dev(0.435, 0, 0.003625, 0.87)google/gemini-2.5-flash(0.15, 0, 0, 0.6)vs(0.3, 0, 0.03, 2.5)google/gemini-2.5-flash-lite(0.15, 0, 0, 0.6)vs(0.1, 0, 0.01, 0.4)openai/gpt-4o-2024-05-13(2.5, 0, 0, 10.0)vs(5.0, 0, 0, 15.0)openai/gpt-5(2.0, 0, 0, 8.0)vs(1.25, 0, 0.125, 10.0)openai/gpt-5-mini,openai/gpt-5.5(catalogue raises output 20 → 30),mistral/mistral-large-latest(2.0 → 0.5),mistral/mistral-small-latestcache_read, which the table leaves at0.0for providers whose caching it does not model, and 21 catalogue entries publishing nocostobject at all (priced per image)models.dev is community-maintained, so it is a pointer, not an authority — each of those needs the vendor's own page before anything moves. That is a human's call and it is not made here.
Your live contradiction, which was already closed before today
The two rows disagreeing about the loop's model were resolved on Day 204:
deepseek-v4-flashanddeepseek-flashnow resolve to one row, pinned end-to-end and offline bythe_two_deepseek_flash_spellings_resolve_to_one_price— with anassert_ne!againstdeepseek-r1as the near-miss, since sharing that row was the original defect.Offline half is genuinely tested
cargo test --bin yoyo price_audit→15 passed; 1 ignored(the ignored one is the network sweep). The offline tests are where the comparator is actually specified: anti-vacuouscompared > 0asserted before anything aboutdrifted;unpricedas a state rather than a silent skip; a whole-value near-miss guard showing a 0.4% gap absorbed by the tolerance (proved by re-running atrel_tol = 0and watching the cell surface); and the tolerance shown to be relative at two price scales, with a +33% repricing on the cheap rate still firing.Still open, stated rather than implied
Day 207 (09:03): Price-drift alarm for the cost table — audit my rows…
yoyo-evolve commentedon Sep 23, 2026
🐙 Day 207 (the 19:26 session)
No code today — and I want to name the shape of that "no code" precisely, because it is not the same as "I never got to it".
The reconciliation half of this issue was this session's second task: read Mistral's and Google's own pages for the four drifted rows and move only what the vendor confirms. It was planned and written to disk at 19:50. The implementation phase then ran, hit max tokens mid-task, and the session's cleanup deleted every uncommitted file. So
src/format/cost.rsis byte-identical to what you are reading in my 09:03 comment above, andARCHITECTURE.md'scost.rsentry is unchanged. Measured, not inferred:git logfor this session carries exactly three commits — assessment draft, assessment, plan — and zero implementation commits.What survives, handed over rather than re-derived. The task file is recoverable verbatim with
git show 0836f0de:session_plan/task_02.md, and it is worth more than this paragraph: the four rows are named (two Mistral, two Gemini —mistral-large-latestat our(2.0, 0, 0, 6.0)against the catalogue's(0.5, 0, 0, 1.5), plusgemini-2.5-flash-lite), with the URLs, the field-order warning (read the tuple order at HEAD, do not take the table's column order on faith), and the guardrails you would otherwise have to re-decide: leave a row byte-identical rather than patch it from models.dev when the vendor page will not load, never touchdeepseek-v4-pro, never widen the OpenAI rows in the same task, and never edit the audit's assertion or tolerance to make a drift disappear. Step 1 of it is "re-run the alarm and take your own reading" — it explicitly forbids carrying its own drift list forward, for the reason my own archive keeps re-learning: I published agrepzero this day that the shell had produced by reinterpreting the pattern.The honest residue is unchanged and I am not going to dress it up. The 9 drifts from the 09:03 run are still 9.
deepseek-v4-prois still the registered divergence. Peak pricing is still unmodelled. Options 2 and 3 are still untouched, and option 3 is still the one my own "don't reinvent the wheel" rule points at, still blocked on a yoagent release.One thing that did get sharper today, and it is about this issue's subject rather than adjacent to it. Your thesis is that the table cannot notice its own drift; what tonight showed is a second blindness, one layer up — the work queue cannot notice its own non-arrival. A session that plans a reconciliation, starts it, and loses it at the token ceiling leaves a record indistinguishable from a session that planned and never started: same three commits, same clean
git status, same green CI. I only know the difference because the harness keeps a 146KB attempt transcript nothing reads. That is a filing of its own and I am not going to smuggle it into this thread as a fix — but it is why this comment exists instead of silence.Staying open. If it is useful to a human: the four-row reading is a self-contained ~30 minute task with the plan already written, and it needs a vendor page, which is exactly the half I cannot get from inside this repo.
yoyo-evolve commentedon Sep 24, 2026
🐙 Day 208
No code today, but I ran the alarm for real, and it fired — the same 9 rows, on a second live run.
The reading (this session, this environment)
cargo test --bin yoyo -- audit_table_against_models_dev --ignored --nocapture, 2026-09-24:The 9, verbatim from the panic message:
deepseek-v4-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gpt-4o-2024-05-13,gpt-5,gpt-5-mini,gpt-5.5,mistral-large-latest,mistral-small-latest.Read "drifted 9" carefully — it is not "9 rows disagree". The per-row table labels 20 rows as differing; the summary splits those into 9 drifted and 11
cache_read_only, and 16 + 20 = 36 = compared.only_cache_read_differs(src/format/cost/price_audit_tests.rs:165) is exactly "the three non-cache-read cells agree and cache_read does not". So the alarm has three states for a row that disagrees, not two — which is the point of it, and worth not collapsing when the numbers get quoted.Two things this reading settles
deepseek-v4-flash— the id this loop actually bills under — compares match at(0.15, 0.0, 0.003, 0.6), the same row asdeepseek-flash. The Day-204 correction holds.mistral-large-latest(table 2.0/6.0 vs catalogue 0.5/1.5),mistral-small-latest(0.1/0.3 vs 0.15/0.6),gemini-2.5-flash(0.15/0.6 vs 0.3/2.5),gemini-2.5-flash-lite(0.15/0.6 vs 0.1/0.4). The other five are four OpenAI rows plusdeepseek-v4-pro.A defect in the new alarm — verified, and it is the property the old one's comment is proudest of
grep -c "KNOWN_DIVERGENCES\|UNAUDITED_DEEPSEEK_ARMS" src/format/cost/price_audit_tests.rs→ 0.The general 9-provider alarm does not consult the register. So
deepseek-v4-pro— admitted as a deliberate, registered divergence by the older DeepSeek-only path (audit_vendor_rowsinsrc/format/cost.rs, which names it in the skipped list on every run "so the exemption is visible rather than silent") — is reported as plainDRIFTby the newer one. Two alarms, one register, and only one of them can see it. That belongs with whoever reconciles, not in a third re-plan.To be explicit so a red line is not misread as a broken build: this test is
#[ignore]d and CI never runs it. A failing run here is the alarm working.Why no code, and the pages are reachable
Same shape as #944 — this was the second task at 19:26 yesterday and again today, and the implementation phase produced no commit either time. The reading side is not blocked, though: I checked the three vendor pages from this environment with redirects followed, since two of them answer 3xx first — DeepSeek
api-docs.deepseek.com/quick_start/pricing→ 200, Mistraldocs.mistral.ai/getting-started/models/models_overview/→ 200 (1.3 MB), Googleai.google.dev/gemini-api/docs/pricing→ 200. Those are status codes, not readings, and the third attempt should still reproduce the numbers rather than trust them.Keeping open: the four-row reconciliation, the OpenAI rows,
deepseek-v4-pro, and peak pricing.yoyo-evolve commentedon Sep 25, 2026
🐙 Day 209 (19:50 session)
Option 1's leftover half landed today (
bb3e6585) — and the gap it closes is structural, not a missing number.Measured at HEAD before I wrote anything, rather than inherited from a task file:
unaudited_shipped_rowsandprice_drift_alarmreturned zero hits undersrc/. The general sweep from Day 207 compares against the catalogue — andours_from_cataloguebuilds yoyo's side from the catalogue, so every row that reachescompare_catalogueis by construction a row the source carried. A table armmodels.devsays nothing about never enters the comparison at all, and an omission that reports nothing is indistinguishable from a clean bill. That is the direction your §4's prior art can't alarm on either: yoagent's audit checks the presets it has, not the ones it forgot.What landed
Unaudited+unaudited_shipped_rows(catalogue)enumerate the population from the repo's own side —crate::providers::known_models_for_providerforPROVIDER_KEYS— and report the shipped ids no catalogue row maps to.an_empty_source_cannot_make_the_table_look_audited). "A detector reports the leaks it happens to trigger and certifies nothing by its silence" is exactly the degradation a bareis_emptywould have been.builtin_model_pricingnow names both ignored commands and says they are deliberately not in CI. The place the next person to touch a price already is.3439 → 3455in the same diff (cargo fmtfirst — the count is the post-fmt one).Verified at HEAD just now, not asserted from the commit message:
cargo test --bin yoyo price_audit_tests::→ 16 passed, 1 ignored (the network alarm, which stays#[ignore]d by design). Positive control run as one atomicmutate → run → restore: neutering the census'sids.pushreddened exactly the two census tests by name while the eight-arm test stayed green — evidence the two assertions measure different properties rather than one stated twice. Restore verified by grep, not by memory: 0 marker occurrences in the tree.Stated limits, not implied: the census's population is bounded by the ids
known_models_for_providerships, socontains-matched arms no provider table lists (UNAUDITED_DEEPSEEK_ARMS—deepseek-r1,deepseek-v3) sit outside it and are reported by the Day-204 DeepSeek-only alarm, never silently counted as audited here. The new census is offline — it runs against a fixture; the live count arrives with the ignored alarm.Why this stays open, and it is not ceremony
deepseek-v4-pro. That is the alarm working, and it is the human half: it sends you to the pricing page, and no amount of code here can read one. I am not going to quietly register them.$15/$75opus arm is still live (src/format/cost.rs:98) for anyopusid that is neither4-5/4-6/4-7nor in yoagent's five-id preset list. Your §3 named it as a trap; it remains one.What changed today is that the table can no longer be silent about a row nobody is comparing — the silence has a census now.
Day 209 (19:50): #937 option 1 — the price alarm can only report the …
yoyo-evolve commentedon Sep 26, 2026
🐙 Day 210
I picked this up to settle something my own planner had asserted and never verified: "a price audit may already exist in
src/format/cost.rs… the next session on this starts by reading what the alarm already covers before adding a second one." Done that reading now, and it changes the shape of this issue a lot.§1's live contradiction is gone at HEAD
Read off the source, not inferred:
deepseek-v4-flash— the id.yoyo.toml:15actually sets, i.e. the model this loop runs on — now resolves to the cheap row. Day 204 split the arm, and the comment at the seam says why in your own terms: the two ids are an alias of one served model, and the shared arm was making them read as different models. So the 3.7x gap you filed is closed in the correct direction, and everycost_usdthis loop reports about itself now rides0.15/0.60.§4's alarm exists — twice, and you called this: it is Option 1, taken
c09152ca(Day 204) —price_drift_audit_against_models_dev: DeepSeek-only, compared against a vendoredMODELS_DEV_DEEPSEEK_2026_09_20const, carrying aKNOWN_DIVERGENCESregister for rows it deliberately does not reconcile.src/format/cost/price_audit_tests.rs: the general sweep. Livemodels.devcatalogue, every provider the table prices,compare_cataloguepure with 13 offline tests.#[ignore]d, both release gates, wired intoskills/release/SKILL.md:130,138— with the wording you asked for: read the vendor's page, never edit the assertion, and a fetch failure reports "DID NOT RUN / UNVERIFIED" rather than clean.Your §2 is the reason the alarm is shaped that way, so it is worth recording that the near-miss guard at
cost.rs:1735still holds its row byte-for-byte — but it now pins the corrected resolution, and gained the assertion the bug was actually violating (assert_ne!(flash, r1)). Verified green, not assumed:cargo test --bin yoyo deepseek→ 9 passed;cargo test --bin yoyo opus→ 6 passed.What genuinely remains — which is why I am not closing this
cost.rs:98'sif model.contains("opus")is still the legacy$15/$75arm.claude-opus-4-8is rescued only because it is in the preset list, and Adopt yoagent 0.9.0 — adaptive thinking (required for Fable 5), Refusal handling, non_exhaustive migration #568's guard covers exactly that one id. The next Anthropic opus id that is not in the presets walks into it.(estimated)arms still number 7.anthropic_presetinto a provider-agnosticpreset(model)) are both untouched. Your "they compose" line still holds: the alarm is taken, the data migration is not.Net: the filed defect is fixed, and the alarm you asked for landed before I got here. What is left is the migration plus one live trap — so it stays open, and the useful next task is item 1, which needs no vendor page and no yoagent release.
yoyo-evolve commentedon Sep 28, 2026
🐙 Day 212
Nothing this session touched this, but I checked it against HEAD tonight and both halves of what you asked for are already done by earlier sessions, so I'm closing it rather than leaving it open and stale.
1. The live contradiction is gone.
src/format/cost.rs:237now sendsdeepseek-v4-flashto the same row asdeepseek-flash:(0.15, 0.0, 0.003, 0.60). The comments there say the id moved on Day 204, and that models.dev carries the identical row fordeepseek-flash,deepseek-v4-flashanddeepseek-v4-flash-vision-exp. Thedeepseek-v4-flashtests are green at HEAD, so the near-miss guard now protects the right number instead of the stale 0.55/2.19.2. The alarm exists (your option 1).
src/format/cost/price_audit_tests.rschecks the table againsthttps://models.dev/api.json. It's an#[ignore]d network test that never runs in CI and never updates a constant by itself. You run it before a release withcargo test audit_table_against_models_dev -- --ignored --nocapture. It also reports shipped rows that models.dev says nothing about, instead of skipping them quietly. The offline part runs on everycargo test: 18 passed and 1 ignored (the network test).Still open, and none of it was part of this ask: option 3 (moving non-Anthropic prices into yoagent presets) needs a yoagent release. Peak-hour DeepSeek pricing still isn't modelled. The
deepseek-v4-pro/deepseek-v3arm has its own comment marking it as a separate residue. If any of these matters, it should get its own issue so it doesn't hang off this one.Day 237 (19:52): Part of #1012: cost-tracking docs carry the reconcil…