Skip to content

Release DL — stage-batch22 — 5-PR hold-bucket reassessment (v0.51.140)#2990

Merged
nesquena-hermes merged 14 commits into
masterfrom
release/stage-batch22
May 27, 2026
Merged

Release DL — stage-batch22 — 5-PR hold-bucket reassessment (v0.51.140)#2990
nesquena-hermes merged 14 commits into
masterfrom
release/stage-batch22

Conversation

@nesquena-hermes
Copy link
Copy Markdown
Collaborator

Release DL / v0.51.140 — stage-batch22 (5 PRs from re-examined hold bucket)

This is a deliberate "actually-reviewable" pass through the hold bucket. All 5 PRs were previously labeled hold but on second read are bounded enough to ship without architectural input.

PRs merged

PR Author Net LOC What it does
#2964 Isla-Liu +417/-2 Models cache fingerprint excludes credential-rotation fields (deny-list of 14 keys). Fixes the 14-min churn that effectively killed the 24h cache. RCA t_16551f61.
#2986 ai-ag2026 +112/-16 _message_window_for_display() anchors paginated window on newest renderable row (skips tool-only tail).
#2902 george-andraws +131/-1 _smdRendererWithoutUnderscoreEmphasis() wraps smd renderer so agent_response and foo_bar stay literal while streaming (matches settled renderer).
#2899 v2psv +75/-22 _loadOlderMessages() switches to cumulative msg_limit=current+30 tail load with suffix-continuity guard + legacy-page fallback.
#2970 AlexeyDsov +91/-11 renderMessages() perf: caches renderMd/_renderUserFencedBlocks output, caches visible-message scan, scopes question→assistant lookup to render window, lazy Prism highlight.

Verification

Hold-bucket re-classification rationale

Each of these was previously hold-labeled with a different actual blocker than "needs deep review":

This batch sits on top of v0.51.139 (Release DK).

v2psv and others added 14 commits May 25, 2026 01:01
_loadOlderMessages() previously fetched older messages with the legacy
index-cursor page (msg_before=_oldestIdx&msg_limit=30) and prepended
the page to S.messages. After #2716 the backend always runs the full
append-only merge for /api/session?messages=1 — the same merge as a
larger msg_limit on the same call — so we can ask for a larger
authoritative tail window directly instead of stitching pages on the
client.

Behavior

* Default request shape becomes msg_limit=currentLoaded+30. The newly
  exposed head of the response is what the user sees as 'older
  messages'. No new query parameters.
* msg_before remains supported by the backend and is retained in the
  client as a race-fallback path: if the returned tail no longer has
  the currently displayed messages as a suffix (because the session
  appended new messages mid-flight, or merge filtered something), the
  client issues the legacy msg_before page and prepends it instead.
  This preserves correctness under concurrent appends.
* Suffix-continuity uses the existing _sameTranscriptMessage helper,
  which tolerates timestamp drift and content-array reshapes.
* Existing race guards (loadingSessionId, S.session.session_id, and
  the _messagesGeneration snapshot from #1937) are reapplied after
  the fallback await.

Tests

Updated four static-string assertions in the existing scroll/viewport
tests to track the new mutation site (S.messages = nextMessages) and
the new msg_limit=requestedLimit shape, while still asserting that
msg_before remains in the body for the race-fallback path.

  pytest -q
    tests/test_older_history_viewport_preservation.py
    tests/test_parallel_session_switch.py
    tests/test_issue1937_endless_scroll_jumpstart_race.py
    tests/test_session_tail_payload.py
  -> 52 passed
  node --check static/sessions.js -> ok

Notes

Originally part of PR #2835. That PR was closed because of an
architectural conflict with #2716 on a different file (api/models.py
metadata-only path). #2716 left static/sessions.js untouched — this
change applies cleanly on post-#2716 master with no rebase work.
…h.json fingerprint

auth.json is rewritten by credential-pool/OAuth token refresh roughly every
14 minutes. _models_cache_source_fingerprint() hashed it via mtime/size
(#1699 _models_cache_file_fingerprint), so every token refresh churned the
fingerprint and the 24h /api/models cache was effectively dead -- the hot
GET /api/session?resolve_model=1 path paid a cold ~11.5s rebuild every few
minutes (RCA t_d127953d residual #2, t_16551f61).

Add _auth_store_semantic_fingerprint(): content-hash auth.json with a
DENY-list of known credential-rotation-only keys (access/refresh token,
expiry, per-credential status/telemetry, request_count, save updated_at)
stripped. Deny-list (not allow-list) is deliberate -- any unknown field, or
a real provider/endpoint/model-set change (active_provider, a new
credential_pool entry, base_url, source, label, auth_type, the providers{}
block, ...) stays in the fingerprint and still correctly busts the cache.
Conservative fallbacks: missing file -> marked; unreadable/corrupt ->
stat-based fallback (never less safe than pre-fix). config.yaml keeps the
cheap stat fingerprint (deliberate edits, no timer churn).

Bidirectional invariant regression test (non-tautological -- the
end-to-end churn test flips RED when the auth_json axis is reverted to
stat-based): token-only churn keeps fingerprint byte-identical AND keeps a
valid disk cache loadable; active_provider change / new credential_pool
entry / changed base_url each flip the fingerprint AND reject the stale
disk cache. Measured: 5/5 cold rebuilds per 5 refresh cycles -> 0/5.

Tests: 9 new pass; 28 adjacent (#1699/#1633/display-resolver) pass;
54 models_cache/fingerprint suite pass.
# Conflicts:
#	CHANGELOG.md
@nesquena-hermes nesquena-hermes merged commit 83862e6 into master May 27, 2026
3 checks passed
@nesquena-hermes nesquena-hermes deleted the release/stage-batch22 branch May 31, 2026 04:23
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.

6 participants