Skip to content

Studio: overwrite guard survives the raw/JSON cache collision; telemetry line reports in-session evictions (LAB-227) - #289

Merged
2 commits merged into
mainfrom
fix/gh-contents-no-store
Oct 29, 2026
Merged

2 commits merged into
mainfrom
fix/gh-contents-no-store

Conversation

@mbreiser

Copy link
Copy Markdown
Contributor

Summary

Two Studio fixes found during the LAB-227 private-repo test on ww18 (2026-10-29, Production 59a377b).

  1. Overwrite guard could miss an existing file (every repo). GitHub returns the same ETag for the raw (application/vnd.github.raw) and the JSON form of one contents URL. After a raw read (?p= open, promote, pattern previews via GH.runBytes), Chrome revalidated the cached raw entry for the later JSON GH.run on the same URL, got a 304 and served the raw YAML; res.json() threw, data was null, and resolveRepoSaveTarget (and the silent-fork guard) concluded the file did not exist. A changed protocol then overwrote the repo copy with no "Overwrite?" prompt. Reproduced in the page: the default-cache JSON request returned version: 3…, the same request with cache: 'no-store' returned JSON with sha. Fix: GH.run sends cache: 'no-store'. runBytes keeps caching, since nothing JSON is ever stored now, the collision cannot happen in either order.
  2. Telemetry "dropped N" read as in-run loss. The ring block header's dropped is the controller's lifetime eviction count since its boot; a Studio connected hours later logged "dropped 1929" with gaps 0. The drainer now keeps the counter at its first block of a ring incarnation (droppedBase, reset on a fresh ring and by reset()) and exposes droppedSession; the once-a-minute stats line reads "evicted this session N (controller lifetime M)". telemetry_dump summaries and soak stats carry the new field; the raw dropped stays for the record. docs/development/telemetry-logging-reference.md documents it.

Linear: LAB-227 (findings posted there).

Tier

next candidate — together with #285, #286 and #288. The overwrite is recoverable from git history, so not a hotfix.

Release notes

Studio

  • Saving over an existing repo protocol always asks first. A browser-cache collision could skip the "Overwrite?" prompt after opening or promoting a protocol; it no longer can.
  • The telemetry stats line now separates in-session evictions from the controller's lifetime counter, so a large number after connecting to a long-running controller no longer looks like data loss.

Test plan

  • pixi run test — 72/72 files
  • tests/test-studio-github.js: run() sends cache: 'no-store'; a non-JSON body yields data: null without throwing
  • tests/test-arena-telemetry.js: first block with dropped 1929 → droppedSession 0; next block 1931 → 2; reset() clears the baseline
  • On ww18 after the candidate is on Next: open a repo protocol via ?p=, change it, Save → "Overwrite …?" prompt appears

🤖 Generated with Claude Code

mbreiser and others added 2 commits October 29, 2026 15:45
…uard miss)

GitHub returns one ETag for the raw and the JSON form of a contents URL; after
a raw read (?p= open, promote, previews) Chrome revalidated the cached raw body
for the later JSON request and served it on the 304, so res.json() threw and
every existence check (resolveRepoSaveTarget, the silent-fork guard) answered
"no": a changed protocol overwrote the repo copy with no "Overwrite?" prompt.
Found on ww18 during the LAB-227 private-repo test, 2026-10-29. GH.run now
sends cache: 'no-store'; runBytes keeps caching since nothing JSON is stored.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… counter

The ring block header's `dropped` is the controller's eviction count since its
boot, so a Studio connected hours later logged "dropped 1929" with gaps 0 and
read as in-run loss (ww18, 2026-10-29). The drainer now keeps the counter at
its first block of a ring incarnation (`droppedBase`, reset on a fresh ring)
and exposes `droppedSession`; the once-a-minute stats line leads with
"evicted this session N (controller lifetime M)". `telemetry_dump` and the soak
stats carry the new field; the raw `dropped` stays for the record.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mbreiser added a commit that referenced this pull request Oct 29, 2026
…sion; telemetry line reports in-session evictions (LAB-227)
mbreiser added a commit that referenced this pull request Oct 29, 2026
Candidate for Production. PRs: #286, #285, #288, #289.

Release-Manifest: releases/2026-10-29.json
mbreiser added a commit that referenced this pull request Oct 29, 2026
…sion; telemetry line reports in-session evictions (LAB-227)
mbreiser added a commit that referenced this pull request Oct 29, 2026
Candidate for Production. PRs: #286, #285, #288, #289.

Release-Manifest: releases/2026-10-29.json
@mbreiser mbreiser closed this pull request by merging all changes into main in 62dbbe7 Oct 29, 2026
@mbreiser
mbreiser deleted the fix/gh-contents-no-store branch October 29, 2026 21:13
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