Skip to content

Date OpenAI undated ids listed beside their dated snapshot - #128

Merged
thossullivan merged 3 commits into
mainfrom
fix/openai-alias-snapshot-rows
Oct 24, 2026
Merged

thossullivan merged 3 commits into
mainfrom
fix/openai-alias-snapshot-rows

Conversation

@thossullivan

Copy link
Copy Markdown
Owner

Summary

The live refresh fails on main today:

refresh failed: deprecation alias gpt-5.4-nano for gpt-5.4-nano-2026-03-17 conflicts with a canonical model id

OpenAI rewrote 16 deprecation rows after 04:21 UTC on 2026-10-23 into alias | snapshot form. In 14 of them both ids are already separate entries in feeds/openai.json, so the merge refuses the row. The error names only the first.

This PR adds one narrow rule to mergeFeed: for OpenAI, when the row's id ends in -YYYY-MM-DD and the other id is exactly that id without the date, both entries get the row's lifecycle and neither is merged. Everything else the check guards still throws.

Live effect (--check --provider openai, keyless)

Exit 3 instead of 1. Changes the next refresh will carry:

  • gpt-5, gpt-5-mini, gpt-5-nano, gpt-5-pro: shutdown 2026-12-11.
  • gpt-4o-mini-tts: shutdown 2027-01-06.
  • Snapshots gain their row's dates: gpt-5.4-nano-2026-03-17, gpt-5.1-2025-11-13, gpt-4o-mini-transcribe-2025-12-15, and the realtime and audio snapshots.
  • New entry gpt-image-1.5-2025-12-16 (2026-06-02 to 2026-12-01).

Users of the bare gpt-5 family names get findings they don't get today; the page states those dates for those names.

Tests

  • 11 new tests in refresh/test/run.mjs on inline HTML with invented ids. 6 fail on the old code; the rest guard shapes that must still throw.
  • node refresh/test/run.mjs and npm test pass.

Not in this PR

A separate older bug: when two rows share an undated name with different shutdowns and neither id is in the feed yet, the merge folds one snapshot into the other and keeps the later date. Filed separately.

🤖 Generated with Claude Code

OpenAI rewrote 16 deprecation rows on 2026-10-23 from one id to
"alias | snapshot", for example "gpt-5.4-nano | gpt-5.4-nano-2026-03-17".
The parser makes the snapshot the record id and the undated name its
alias. In 14 rows both ids are already separate committed entries, so the
merge refused the row ("conflicts with a canonical model id") and the live
refresh failed.

That check stops a row from merging one committed model into another. It
still applies, except in one narrow case: for OpenAI, when the record id
ends in -YYYY-MM-DD and the other id is exactly that id without the date,
the row's lifecycle is copied onto both entries and neither is merged.
Two rows, or the undated id's own row, giving that entry different
lifecycles still throw. Other publishers, month-day snapshots, unrelated
aliases, and aliases owned by a third entry still throw.

Effect on the next refresh: gpt-5, gpt-5-mini, gpt-5-nano, and gpt-5-pro
gain the 2026-12-11 shutdown and gpt-4o-mini-tts 2027-01-06, as the page
states, and gpt-image-1.5-2025-12-16 is added as its own entry.

Implemented by an Opus subagent; reviewed by Claude.
Review found three ways the family rule could still assign a lifecycle
silently. The parser kept the later announcement for a repeated id and
carried the earlier row's aliases onto it, so an undated name took a date
from a row that never listed it; that now throws when the dropped row
names an alias the kept row does not. A row whose replacement is its own
family alias means OpenAI is repointing the alias, not retiring it, so the
family rule refuses it. A row that reaches its target through an alias is
now covered by the same lifecycle conflict check as a direct row, in
either order.

Implemented by an Opus subagent; reviewed by Claude.
@thossullivan
thossullivan force-pushed the fix/openai-alias-snapshot-rows branch from 8768925 to f646df4 Compare October 24, 2026 01:34
…alias

The alias conflict check compared every lifecycle field, so two rows that
agreed on dates and replacement targets but differed in a replacement note
or in option order failed the whole refresh. It now compares dates,
precision, the replacement, and the replacement options as a set.

The family rule refused a row whose replacement is the family alias, but
not one whose replacement is the alias's own newer snapshot, which is the
same signal that OpenAI is repointing the alias rather than retiring it.
@thossullivan
thossullivan merged commit e176244 into main Oct 24, 2026
6 checks passed
thossullivan added a commit that referenced this pull request Oct 25, 2026
When two OpenAI deprecation rows shared a bare name with different
shutdowns and neither snapshot was committed, mergeFeed folded the
earlier snapshot into the later one as an alias, and the earlier date
was lost. Feeding that result back in then threw.

Maintainer's rule: every dated snapshot keeps its own entry and its own
row's lifecycle, and the shared bare name follows the row with the
single earliest shutdown. An uncommitted bare name is attached to that
snapshot (and moves there if an earlier run put it on another snapshot
of the group); a committed bare entry takes that row's lifecycle, which
replaces the #128 behavior of stopping the run. A tie on the earliest
shutdown, a row that repoints the alias, and a bare-name row that
disagrees still stop the run.

A general check now stops the run when a row would fold in, through an
alias, an entry an earlier row of the same run dated with a different
lifecycle. That catches the same silent fold for non-snapshot rows
(gpt-fam-chat | gpt-fam) and month-day snapshots. Same-lifecycle rows
still merge. Live --check output for every provider and the
distributors is byte-identical to main.

Fixes #135.

Implemented by an Opus subagent; reviewed by Claude.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant