fix(server): prefer the newest model manifest edit across sources - #42
Conversation
- Bundle and cache now outrank an older remote manifest by updatedAt - Ship Claude Opus 5.5 as current and demote Opus 5 to legacy - Cover the stale-remote case with a live test
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bcdbc197cb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Effect.catchCause(() => Effect.succeed(null)), | ||
| ); | ||
| if (fetched === null) return manifest; | ||
| if (manifestUpdatedAtMs(fetched) < manifestUpdatedAtMs(manifest)) return manifest; |
There was a problem hiding this comment.
Apply the normal TTL after a stale successful fetch
When a release's bundled manifest is newer than the copy on main—the exact scenario this branch handles—this early return leaves fetchedAtMs unchanged even though the HTTP request succeeded. Provider health checks repeatedly trigger this refresh, so after the five-minute lastAttemptMs window each active server can download the same known-stale manifest again rather than observing the one-hour successful-fetch TTL, increasing request volume by up to 12× until main catches up. Record the successful probe time, or otherwise TTL-gate stale-but-valid responses, before returning the bundled manifest.
Useful? React with 👍 / 👎.
What Changed
The model manifest service now compares
updatedAtbetween the remote manifest and the current manifest (which may be a newer bundled copy or last-good cache) and keeps whichever edit is newest. Previously, a successful fetch always overwrote the current manifest, even if the remote copy was an older edit than the bundled one shipped with the release.This PR also updates the bundled
model-manifest.json: bumpsupdatedAt, addsclaude-opus-5-5as the current Claude chat default, and movesclaude-opus-5to legacy (dropping the bareopusalias so it resolves to 5.5). A test covers keeping a newer bundled catalog when the remote manifest is an older edit, and the internals doc reflects the corrected preference rule.Why
The refresh logic and the documented "newer bundle wins" behavior disagreed: the doc said a newer bundle can correct model data before the next fetch, but the code let any successful fetch clobber it. If
main's manifest lagged a freshly released bundle — for example when adding a new Claude model that only exists in the bundle — the release's corrections were silently reverted on the first successful refresh. ComparingupdatedAtacross sources instead of trusting fetch order fixes that, matching the existing rule that fetch time cannot establish which copy contains the newer edit.The Claude catalog update rides along because the new bundle needs a
updatedAtbump anyway to exercise the corrected preference order, and it demonstrates the exact failure case in practice.UI Changes
Not applicable — server-side manifest resolution only.
Checklist
Built with GLM (glm-5.3-flash) via opencode.