feat(bridge): the local bridge, and long jobs that outlive the tab - #319
Open
ivanmkc wants to merge 2 commits into
Open
feat(bridge): the local bridge, and long jobs that outlive the tab#319ivanmkc wants to merge 2 commits into
ivanmkc wants to merge 2 commits into
Conversation
…the tab Security comes first because a process that can run someone's authenticated AI agents is worth breaking into. Loopback bind, not 0.0.0.0 -- a bridge on café wifi is a remote shell with extra steps. A per-run token, printed once, compared in constant time. Origin validation on everything that changes state, because any page you visit can POST to 127.0.0.1; a token alone is not enough, since a page in the family's browser could have been given one. CORS is echoed only for origins already trusted, never "*". Driver detection is a version probe rather than a PATH lookup: a binary that cannot run is not an installed agent, and "installed but signed out" is the most common state and the one worth naming, because the fix is a command rather than an error to interpret. Prompts go on stdin, never in argv. Jobs are owned entirely by the bridge. No client-side runner, no checkpointing in the browser, no resume logic on the iPad -- an iPad that sleeps must not be able to lose someone's research, and the only way to guarantee that is for it never to have been responsible. The step budget is not a nicety: an agent asked to research trips can loop, and every loop spends someone's subscription. The client mirrors, renders a card from the moment a job is queued so the screen is never blank, and falls back from the event stream to polling -- a difference in latency, never in correctness. Discovery is opt-in. Probing a port nothing is listening on logs a console error in every browser and costs a request on every start, so the automatic probe happens only for someone who has used a bridge before; everyone else asks once from Connections. The offline e2e now asserts that nothing but that probe ever leaves the app's own origin. A message the bridge sends goes through the same approval shape as Gmail's send, and the iMessage path puts the body on stdin -- an apostrophe in "don't forget" would otherwise end the AppleScript string, and worse is possible. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three tests waited on a 20ms sleep and failed intermittently once the suite grew large enough to contend for CPU. A flaky test is a defect in the suite: it teaches people to re-run rather than to read. Adds Store.settled(), which resolves once every queued write has landed, and switches the affected tests to waiting on the actual condition. The seam is worth having beyond the tests -- the app can await it before closing, so a tick made on the way out is not lost. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 2, 2026
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.
The stack
localStoragefor the test suite on Node 25@ivanmkc/termchart-canvas; injectable interact transportEvery branch in the stack was checked out on its own and verified independently:
npm install,tsc --noEmitfor every package,npm testacross the workspace, and the offline e2e where it exists. All twenty are green — no branch depends on a later one to build or pass. Full viewer e2e (11 suites, 106 assertions) was run on #312 as the behaviour-preservation evidence for the extraction.Stack position: 9 of 10. Base:
lifeboard/07-calendar-mail.The optional local process, and with it the AI subscriptions the family already pays for — plus long jobs that outlive the tab.
Security comes first because a process that can run someone's authenticated AI agents is worth breaking into. Loopback bind, not
0.0.0.0— a bridge on café wifi is a remote shell with extra steps. A per-run token, printed once, compared in constant time. Origin validation on everything that changes state, because any page you visit can POST to127.0.0.1; a token alone is not enough, since a page in the family's browser could have been given one. CORS is echoed only for origins already trusted, never*.Driver detection is a version probe rather than a PATH lookup. A binary that cannot run is not an installed agent, and "installed but signed out" is the most common state and the one worth naming — the fix is a command the person can run rather than an error they have to interpret. Prompts go on stdin, never in argv.
Jobs are owned entirely by the bridge. No client-side runner, no checkpointing in the browser, no resume logic on the iPad: an iPad that sleeps must not be able to lose someone's research, and the only way to guarantee that is for it never to have been responsible. The step budget is not a nicety — an agent asked to research trips can loop, and every loop spends someone's subscription. The client mirrors, renders a card from the moment a job is queued so the screen is never blank, and falls back from the event stream to polling: a difference in latency, never in correctness.
Discovery is opt-in. Probing a port nothing is listening on logs a console error in every browser and costs a request on every start, so the automatic probe happens only for someone who has used a bridge before. The offline e2e now also asserts that nothing but that probe ever leaves the app's own origin.
Also here: a test-stability commit. Three tests waited on a 20 ms sleep and failed intermittently once the suite grew large enough to contend for CPU.
Store.settled()resolves once every queued write has landed, and the tests wait on the condition instead.Verified: CLI 314 tests (33 new, covering origin rejection, signed-out drivers, the step budget and cancellation), lifeboard 320, offline e2e 17/17.
🤖 Generated with Claude Code