Repository navigation
Conversation
…LOST, LAB-303 S1)
ww18 2026-10-15 (step 5.7): the bridge was hard-killed 10 s into a closed-loop epoch.
The Studio only showed "bridge disconnected"; the run carried on with no frames.
- fictrac-bridge-client.js: an OPEN socket that closes without disconnect() asking
is a loss. Fail closed first (apply off, pending index dropped, every pending
request settled at once via _failPending: log export rejects "bridge lost", level
ack waiters resolve null), then emit 'lost' {code, apply, logFile, ...}.
disconnect() clears _ws before close() so a requested close is never a loss; a
superseded socket closing after a reconnect no longer clobbers the new one.
New logFile (log_control_ack `file`) and lastLoss getters.
- arena-runner-g6.js: onBridgeLost(info) stops the run when a closed loop was
applying, when a step acts through the bridge (sequenceUsesBridge), or when the
run's record lives on it (runSequence {bridgeRequired}); otherwise a test run
continues with a bridge-lost event. The stop is the ordinary stop() (the serial
link is fine: real STOP, LED off, loop disarmed); summary.bridgeLost, not a fault.
- arena-session.js: routes 'lost' to the runner, re-emits 'bridgelost'
{info, action, running}; stale-cache runner fallback = stop().
- run-log.js + studio-runlog-adapter.js: outcome BRIDGE_LOST, bridge-lost transcript line.
- arena_studio.html: persistent #runAlert (role=alert), recorded runs pass
bridgeRequired, BRIDGE_LOST is not auto-committed, a push refuses a log_export whose
name is not this run's bridge file (a restarted bridge serves another file), soak
ends with reason bridge-lost; cache tokens bumped on the changed scripts.
Tests: client 183, runner 529, session 106 (incl. end to end: real client + runner +
session + run log, socket killed mid epoch -> BRIDGE_LOST in < 2 s, pending export
fails fast, STOP sent), run-log 39, adapter 34; suite 60/60; studio-smoke 0 failures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ww18 2026-10-15: a tab opened at ?p=<key> stayed empty until a refresh. initFromUrl AWAITED the course repo's GitHub patterns/ listing (registerRepoSharedPatternPreviews, no timeout; a course repo is configured by default) before loading the protocol. Reproduced headless: with api.github.com held the page was still empty at 9 s and loaded 0.1 s after that request failed. - initFromUrl starts the shared + colocated preview listings without awaiting them. - That is only safe because preview byte sources no longer rely on "last write wins": new js/studio-preview-sources.js keeps them in layers (builtin < shared < colocated < uploaded) and every write names its layer, so a late shared listing can no longer override a protocol's colocated patterns (the replay path already had that race). Overlapping shared listings: the newest request wins. - tests/test-studio-preview-sources.js: precedence in all 24 arrival orders, layer replacement, Map-like reads, and source guards — everything initFromUrl awaits before the ?p= load is allowlisted (fails on main's Studio, as it should). test-studio-github.js: the lazy-token check follows the new source forms. Headless (GitHub held): ?p= loads in 0.09 s; previews wire once the listing answers; no console errors. Suite 61/61. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…3 S1) - bridgeRequired only for a recorded run the bridge is actually logging (bridge.logging), not merely connected for the scope. - A log_control_ack slower than the 800 ms wait still names the run's bridge file: the run subscribes to 'loglevel' once and takes `file` for THIS run. - disconnect() and a connect() over a still-closing socket settle every pending request (export, level ack) themselves; the superseded socket's late onclose is ignored, so it must not be the only place they settle. - The bridge-lost alert no longer hardcodes `pixi run bridge` (wrong on the ROS 2 graph): "restart the bridge program on the rig PC". Tests: client 187 (two new reconnect cases), studio-shell 81 (wiring checks). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
Author
|
Review fixes pushed (0b1d212), from the 2026-10-17 code review of this PR:
Tests: client 187/187 (two new reconnect cases), studio-shell 81/81, full suite 61/61 files. This PR is constituent 1 of candidate |
mbreiser
added a commit
that referenced
this pull request
Oct 18, 2026
… this branch's own S1/S2 #257's bridge-loss model replaces 484b187's: the client emits 'lost' only for an OPEN socket closing unasked (failed connects and superseded sockets are not losses), fails every pending request first, and the RUNNER decides via onBridgeLost (apply on / bridge-routed steps / bridgeRequired). The runner's bridgeInUse getter and the 'disconnect'-involuntary path are removed; so is the fault('bridge_lost') outcome path (BRIDGE_LOST now comes from summary.bridgeLost). Connector delta on top of #257 (its PR body's "item 3"): - _failPending settles connector_command waiters (outcome 'unknown'); - sequenceUsesBridge counts connector plugin instances (BiasPlugin, ScanImagePlugin) as bridge-routed steps, so a loss in an open-loop ITI before a camera step stops the run; - the bridge-lost alert says devices driven through the bridge were not told to stop. Also: the run-end bag close in onRunFinished referenced an undeclared `bridge` (a ReferenceError swallowed by its catch — the fix was a no-op); it now uses Studio.session.bridge, closes before the upload, and only for this run's log. initFromUrl takes #257's fire-and-forget listings over the grace-period race; patByteSource keeps rc5's background-lane version with the layered preview map. Tests: root suite 70/70 files (runner 544, client 199, session 106, adapter 34, run-log 39, studio-shell 82, preview-sources 23). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…edes-1bab85 # Conflicts: # arena_studio.html
Contributor
Author
|
Not a separate constituent of candidate |
…' into claude/hopeful-archimedes-1bab85
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes finding S1 from the LAB-303 Plan B Windows test on ww18 (2026-10-15, step 5.7): the FicTrac bridge was hard-killed 10 s into a closed-loop epoch, the Studio showed only "bridge disconnected" in the bridge panel, and the run carried on with no frames. Bridge 3.4 dying mid-run takes the same path. Now a bridge loss during a run is loud, and the run stops when it needs the bridge.
How it works (connection ownership stays in the classic substrate):
js/fictrac-bridge-client.js: an open socket that closes withoutdisconnect()asking is a loss. The client fails closed first: apply goes off, the pending frame index is dropped, and every pending request settles at once through one_failPending('bridge lost'). Log export now rejects with "bridge lost" instead of waiting out its 15 s timeout, and level-ack waiters resolve to null. Only then does it emit'lost'{code, apply, logFile, …}.disconnect()now clears_wsbeforeclose(), which is how a requested close is told apart from a loss; a superseded socket closing after a reconnect no longer clobbers the new one. New getters:logFile(fromlog_control_ack) andlastLoss.js/arena-runner-g6.js:onBridgeLost(info)stops the run in three cases:sequenceUsesBridge; today that is FicTrac). This catches a loss in an open-loop ITI before a closed-loop epoch.runSequence({bridgeRequired}), which the Studio passes for recorded runs; those are already gated on the bridge at start).It stops through the ordinary
stop(): the serial link is healthy, so the display gets a real STOP, the LED goes off and the loop is disarmed. It recordssummary.bridgeLost. This is not a controller fault, so no post-mortem runs. Otherwise a test run that needs nothing from the bridge continues and records abridge-lostevent.js/arena-session.js: routes'lost'to the runner and re-emits'bridgelost'{info, action, running}. If a stale cached runner has noonBridgeLost, an active run is stopped withstop()instead.js/run-log.js,js/studio-runlog-adapter.js: new outcomeBRIDGE_LOST(CONTROLLER_FAULTstill wins if both happen) and a transcript line⚠ BRIDGE LOST (code 1006) — closed loop was running — run stopped.arena_studio.html:#runAlert(role="alert", ✕ to hide, cleared at the next run). The shared#bannerauto-hides after 8 s, which is easy to miss at a rig.BRIDGE_LOSTruns are not auto-committed: the bridge that wrote the file is gone.log_exportwhose filename is not this run's bridge file. A bridge restarted after a crash serves a different file, and before this change the push would have committed it under this run's name.bridge-lost.CLAUDE.md: a short "Bridge-loss lifecycle" rule next to the controller-fault one, and a "never await a remote call before the
?p=load" rule.Item 3 of the brief (pending
connector_commands fail fast).connector_commandexists only on the spike #254 (4dd129f), not onmain. As agreed, this PR provides the one fail-fast place (_failPending) and the bridge-dependence hook (sequenceUsesBridge). The ~4-line delta for #254 (settle connector waiters in_failPending, add the connector plugin names tosequenceUsesBridge, add a "stop the BIAS recording by hand" line to the alert, and a test) is written up in the LAB-303 session notes. The browser cannot reach BIAS without the bridge, so after a loss BIAS keeps recording whatever the runner does; the alert is where that gets said.S2 (
?p=link loaded only after a refresh): fixed (second commit).initFromUrlawaitedregisterRepoSharedPatternPreviews(), a GitHub Contents API listing of the course repo'spatterns/with no timeout, before it loaded?p=. A course repo is configured by default, so this happened on every bench. Headless check withapi.github.comheld: the page was still empty after 9 s and loaded within 0.1 s once that request failed. (The registry fetch was alreadycache: 'no-store', so caching was not the cause.)initFromUrlnow starts the shared and colocated preview listings without awaiting them.js/studio-preview-sources.jskeeps them in layers (builtin < shared < colocated < uploaded) and every write names its layer. A late shared listing therefore can't override a protocol's own_patterns/; the replay path already had that race. When shared listings overlap, the newest request wins.?p=loads in 0.09 s, previews wire once the listing answers, and there are no console errors.Found in passing (not touched):
ArenaSession.runSequencedoes not forwarddegPerFrameordefaultCouplingto the runner (since #211). A legacy plugin-configgain: -1.8protocol therefore runs at coupling +1 in the Studio. Proposed as its own follow-up.Tier
Next candidate.
Release notes
Studio
BRIDGE_LOST. A test run that needs nothing from the bridge keeps going, and its log notes that bridge logging stopped.?p=…) opens right away, even when GitHub is slow. It used to wait for the course repo's pattern list from GitHub and could stay empty until you refreshed. Pattern thumbnails still appear as soon as GitHub answers.Test plan
pixi run test: 61/61 files. New checks:sequenceUsesBridge)FicTracBridgeClient(fake socket) + realArenaRunner+ArenaSession+ run log, socket killed mid closed-loop epoch →BRIDGE_LOSTin < 2 s, pending export fails fast, STOP senttest-studio-preview-sources23: precedence in all 24 arrival orders, plus a guard that only same-origin calls are awaited before the?p=load. It fails onmain's Studio, as it should.pixi run studio-smoke: 0 failures (layout at all widths, 5 themes + axe, replay interlock).api.github.comheld:?p=and?p=&rig=&mode=load in < 0.1 s; preview sources wire once GitHub answers.?p=link on the rig PC (should load at once). Start a closed-loop run,taskkill /Fthe bridge mid-epoch. Expect the red alert, the run to stop with outcomeBRIDGE_LOST, the display to stop, and ⇪ Push log to refuse after a bridge restart.🤖 Generated with Claude Code