Skip to content

Show teammates' shared machines on the Machines page and in a document's machine menu, and run their builds there - #789

Merged
lavaman131 merged 1 commit into
mainfrom
shared-machines-visible
Nov 5, 2026
Merged

lavaman131 merged 1 commit into
mainfrom
shared-machines-visible

Conversation

@lavaman131

Copy link
Copy Markdown
Collaborator

Show teammates' shared machines, read-only, on the Machines page and in a document's machine menu, and run their builds there

Summary

Teammates' shared machines are now visible, read-only, on the Machines page and in a document's machine menu (the cloud or laptop icon left of Build plan). A person whose document runs on a teammate's machine can see that machine, whether it is online and its release, and anyone who can edit a document can choose it for that document. Choosing it sends the chooser's Build plan, Build, Retry, Run and manual rebuild there, and that document's Planner too where the rules below allow. Fixes #769.

What changed

API. GET /api/connector/machines returns the person's own machines and, after them, the machines teammates share with them. Own rows are as before plus teammate: false. A teammate's row is { id, label, teammate: true, owner: { login, avatarUrl }, repositories, connected, lastSeenAt, version, service }: no autoRun, reauthorize, finishing, favorite, shared or createdAt, and no host, system or agent either. GET /api/documents/:id/machine lists them the same way (plus runsPlanners), for a caller who can push to the document's repository. POST /api/documents/:id/machine accepts one for anyone who can edit the document. /label, /favorite, /shared and /revoke still answer a non-owner 404.

Who sees what. A machine appears to a teammate only when it is shared, not revoked, its owner can push to a repository the teammate can push to, and the machine serves that repository. A reader of a document sees none. The owner sees their own machine once, as theirs. Both surfaces list shared machines wherever Chopin has machines, with AGENT=off or a harness that does not run Planners on machines too; only the Planner behaviour needs HARNESS=remote. The push check needs the machine's own GitHub token, which machines hold with or without durable credentials (under AUTH_MODE=local it is the owner's live login, so the machine is listed while its owner is signed in).

One push check. Default machine's check (reach, asked with the machine's own token, held 10 minutes for a pass and a minute for a refusal) sits behind ownerReaches(); the listing, the menu, reachShared(), a build's queueing and its claim all use it. reach is keyed by who was asked and the repository, and a check already in flight for a key is shared, so overlapping polls ask GitHub once. The Machines page's check of the person's own push access, for a repository a machine has checkouts of, uses the same map and windows (viewer:<user id> as the key).

Machines with no checkouts. A connected machine serves a repository by serves(), so a protocol 5 machine with no checkouts, which clones what it needs, serves every repository its owner can push to. An offline machine is listed, disabled, whatever it has checked out.

The Machines page's repository rule. The Machines page no longer depends on the person's projects, and for a machine that serves every repository it asks GitHub nothing per repository: it intersects two lists of what can be pushed to among GitHub App installations, as the repository picker lists them, every page of every installation, with no bound on how many repositories either side has. The person's own list is held a minute. The machine owner's is listed with the machine's own token, held 10 minutes (a minute after a failure) once per owner for everyone who looks, and asked once however many people and polls ask at once, so the owner's GitHub rate limit pays a few listings every 10 minutes however many teammates have the page open and however often it refreshes; before, up to 50 of each teammate's repositories were each asked with the owner's token every minute (about 3000 calls an hour for one teammate), and a repository past the first 50 the person could push to was missing until they worked in it. A machine that needs its GitHub authorization renewed is listed to no teammate, as the menu already did. Only the repositories a machine has checkouts of are asked one at a time, eight at a time: the owner's with the machine's token as above, and the person's, for one their installations do not list, with their own login once, kept 10 minutes for a pass and a minute for a refusal in the same reach map under their id, with the answers that have run out forgotten as a listing begins. The document's own menu names its repository and asks about it alone.

Builds there. Choosing a teammate's shared machine now sends the chooser's builds, retries, runs and manual rebuilds to it; the teammate-machine refusal and the greyed-out Build plan are gone.

  • Identity. The agent runs on the owner's machine with the owner's GitHub account, so its pull requests are the owner's. A build records the person who asked as user (what the run, the Build tab's "started by" and Stop use) and, when the machine is not theirs, the owner as machineOwner. The agent is told in read_implementation and read_rebuild, and in its prompt, to end each pull request body with a note that names who requested it and on whose machine it was built. The Build tab names whose machine a build ran on.
  • Binding. Only the connection a build was queued on may claim or report it (runner(build) is the owner of that connection: machineOwner, else user; the owner checks in connector.ts, pickBuild, reportBuild and queueRebuild use it). A build is queued, offered (wait_for_work) and claimed only while the machine is shared, its owner can push to the repository (the same ownerReaches()), and the person still chooses it. A queued build that stops qualifying ends saying so; a build already running finishes, like a Planner mid-reply. The run-id claim stays logical, as AGENTS.md says.
  • Refusals. An offline chosen machine says it is offline. One that is online but no longer takes the work (unshared, or its owner can't push) fails with machine-unavailable and its words, never on another machine. Nothing moves a person's build to a machine they did not choose.
  • Automatic work. LIVE_BUILD rebuilds and LIVE_SPIKES prototypes still run only on their builder's own machines, with that machine's own opt-in; candidates() is untouched and a teammate's machine is added only by requested() for work a person asks for. A pressed rebuild may go to the chosen shared machine; an automatic one never does.

Consent copy. Sharing is now the owner's consent to teammates' Planners, builds and runs on the machine, as the owner. The sharing switch (the Shared column tooltip and a phone's row), the pairing card ("Let teammates run their work on this machine"), the add-a-server setting ("Run teammates' work"), docs/chopin-cli.md, docs/local-launcher.md, docs/implementation-lifecycle.md, docs/local-experiments.md, docs/agent-connection.md, docs/hosted-agent.md and AGENTS.md say so, and no longer say "Builds stay yours".

Choosing and the creator's rule. Anyone who can edit a document may choose a listed machine, and the creator's own choice still decides where its Planner runs (#771). Another editor's choice counts only while the creator chose no machine, and it takes the Planner from Default machine only where its machine can run it for them at that moment (online, started with a Planner command, and for a teammate's machine its owner able to push). Then an attached Planner stops, one that was only asked is withdrawn and loses its credential, and only the chosen machine is asked.

  • A choice that cannot run it yet (a machine with no Planner command, an offline one, one whose owner can't push) is stored and stops nothing: the Planner keeps running where it is, a reply under way included. The machine takes the document once it can run it and no Planner is attached on Default machine; a machine merely coming online never interrupts an attached Planner, as a creator's own machine coming online does not. Replacing one's own choice still stops the Planner on the machine chosen before.
  • Derived, not remembered. Whether a stored choice places the Planner is derived from the stored choices on every ask (placedChoices(), placesElsewhere()); the old in-memory taken record of an earlier takeover is gone. After a restart or deploy Default machine is therefore not asked for a document whose stored choice places it on a machine that can run it, whichever of the two connects first. Until the chosen shared machine's owner has passed the push check (made when it connects or someone opens the document), Default machine may be asked first and is withdrawn once the chosen machine is known to be able.
  • The creator's choice (their own machine or Default machine) takes it back: the Planner on the editor's machine stops and the choices that were placing it, even one whose machine had not been asked yet, are cleared. An editor's choice made while the creator's own stood is only stored, for their own builds and runs, and counts again once the creator chooses no machine.

Builds and runs follow each person's own choice.

Unsharing. The machine leaves teammates' lists and their choices of it are cleared. A Planner it was running for a teammate finishes its reply, question or run first, then stops; a queued build it has not claimed ends; a build already running finishes. A document is given to Default machine, and marked in use, only where the machine ran its Planner, had been asked to or was placing it by that choice, or where someone uses the document; any other document, a build-only choice of a machine with no Planner command included, just loses the choice and starts nothing on its creator's machine.

Web. A "Shared with you" table (rows on a phone) of name, owner, release, repositories and state with no star, switch, rename, disconnect or setup; "Shared by " in the machine menu, offline ones disabled; the sidebar count, the favorite and Automatic read the person's own machines alone; the machine button shows for a person whose only machines are a teammate's. An owner's avatar that fails to load no longer leaves their name out of line with its column.

Storage. MachineStore.shared() lists unrevoked shared machines. choose() accepts a machine another person shares (memory and PostgreSQL). BuildRequest gains an optional machineOwner; stored builds without it read as before.

Tests

  • Server, planner-dispatch.test.ts: the listing and its shape, owner-only fields left out, not shared and owner-can't-push hidden, a reader and a person who can't push see none, offline listed, unsharing and revoking remove it, owner-only routes refused to a non-owner, a machine with checkouts listed when GitHub can't list installations, a machine with no checkouts listed for a person who opened nothing, for every repository both can push to however many (250 over three pages, the shared one last; and the only one both can push to coming after the person's first fifty), GitHub asked for the owner's repositories once however many people and polls (two people, 20 polls each, again after a minute and after ten) and once per owner rather than per machine, a refusal of that list held a minute, a machine needing its GitHub authorization renewed not listed, a machine whose owner can only pull hidden, shared machines listed with Planners off; the menu; choosing (any editor, the creator, a reader refused, an unshared or unreachable machine refused as not there); the Default conflict (attached default, asked-but-unattached default, would-have-attached default, choice cleared, creator's choice not displaced, creator takes it back with their own machine or Default, and with Default from a choice no machine had been asked for yet); a choice of a machine with no Planner command, or an offline one, leaving an attached Default Planner (mid-reply too) and an asked one running, then the machine taking over once it is back and that Planner has ended; a restart over the same storage asking only the chosen machine whichever machine connects first, and Default machine again once it goes away; the creator's own choice ahead of the editor's after a restart; a Run going to the chosen machine, refused offline and refused with machine-unavailable.
  • Server, tasks/routes.test.ts: Build, Retry, Run and rebuild on a chosen shared machine and their identity (user, machineOwner, run user, requestedBy, buildMachines, Stop); negatives: not chosen, unshared (queued build offered nothing, refused and ended), owner lost push, machine online but unavailable, wrong connection claiming or reporting, a second machine of the owner, a machine nobody chose, sharing flag off while the choice stands, running build finishing after unsharing, automatic rebuild never going there.
  • Connector: the three prompts carry the requestedBy line. Web: Build plan not greyed for a teammate's machine, the new refusal words, the consent copy, the read-only table.
  • Browser (e2e/planner-picker.e2e.ts, HARNESS=remote): the Machines page for a teammate (listed read-only, owner-only routes refused, a reader sees none, offline then unshared), the same on a phone, an editor choosing a teammate's machine with the creator then deciding and Build plan enabled, the phone sheet, and a shared machine with no checkouts listed for someone who opened no document, offered and chosen in the menu, with Build plan queuing the build on the owner's machine for the chooser.
  • e2e/machines.e2e.ts and e2e/github.ts: the machines e2e's two tests that share a machine ("the machines table", which shares studio, and "server or VM", which pairs a machine shared by its key) now sign in as someone who can push to a repository of their own, octo-org/relay-<id>, which the fake GitHub shows only to them. A machine shared with teammates is listed, and offered in a document's machine menu, to everyone who can push to a repository its owner can, so one shared in octo-org/score showed up to every other test's person on the same server for as long as it lasted, and for good where a test never stopped sharing it, whatever order and beside whichever tests these ran: "server or VM" got studio as a second row and timed out, and machine-choice's menus and its machine button saw an extra machine. A machine they share is now listed to nobody else, so the tests no longer depend on each other, and the revoke "server or VM" needed at its end is gone.
  • Server, planner-dispatch.test.ts, added for review: the person's own push check is asked once across five listings and again after a minute (a refusal) or ten minutes (a pass); overlapping listings ask GitHub once for the person's list and once for the machine's owner's; the repositories a machine has checkouts of are checked a few at a time and not asked again on the next poll; unsharing starts nothing on Default machine for a document whose Planner the machine never ran (a choice of a machine with no Planner command, nobody has the document open), and still hands one to Default machine when the machine ran its Planner or was about to, or when someone has the document open (with the desk's connector already waiting, told at once).
  • Mutation checks on the final tree: nine one-line breaks of the new listing code are each caught by planner-dispatch.test.ts: the two lists cut at 50 (5 tests fail), the owner checked once per repository again (7), the lists never held nor shared (4), no de-duplication of a list in flight (1), the owner's list held only as long as a person's (1), kept per machine instead of per owner (1), a machine needing its authorization renewed still listed (1), a failed list held as long as a pass (1), and a failed list never held (1). The earlier mutation checks (the reach cache, the one-at-a-time checks, the unshare guards and the Default takeover) were not rerun.

Screenshots to attach

All under /tmp/chopin-shots-769/ (taken from the Playwright run, viewed and reviewed):

  • /tmp/chopin-shots-769/machines-shared-no-checkout.png: the Machines page for a person with no project, "Shared with you" with a machine that has no checkout, its owner in line with its column, release, repository and state (retaken on the final tree)
  • /tmp/chopin-shots-769/machines-shared-with-you.png: the same for a person who works in the repository
  • /tmp/chopin-shots-769/machines-shared-with-you-phone.png: the same on a phone
  • /tmp/chopin-shots-769/shared-machine-menu.png: a document's machine menu with "Shared by casey" and a connected cloud machine
  • /tmp/chopin-shots-769/shared-machine-chosen.png: after choosing it, the button and Build plan enabled
  • /tmp/chopin-shots-769/shared-machine-build-plan.png: a chosen no-checkout machine with Build plan enabled
  • /tmp/chopin-shots-769/shared-machine-building.png: after Build plan, "Building 0 of 1"
  • /tmp/chopin-shots-769/shared-machine-phone-sheet.png: the phone's machine sheet
  • /tmp/chopin-shots-769/add-server-settings.png: the add-a-server setting with the new sharing copy

Risks

  • Reading a teammate's machine costs a machine-token GitHub check per (machine, repository) pair for the document's repository and for the repositories a machine has checkouts of, cached 10 minutes for a pass and a minute for a refusal, for offline machines too (it can refresh an offline machine's token). The Machines page also lists the machine owner's installations once per owner every 10 minutes (a minute after a failure) and each person's own every minute while any teammate shares a machine that serves every repository: a call for each installation and each page of a hundred repositories, so a person or an owner with many installations or repositories spends that many listings a minute (person) or every 10 minutes (owner). Checking is bounded by 100 pages of installations and of repositories in each. The held answers live in memory, are bounded by their expiry, and are forgotten with a restart.
  • The person's own push check only decides what the Machines page lists: someone who gains push on a repository their installations do not list sees the machine within a minute, and one who loses it can see the row for up to 10 minutes. Choosing a machine, and every document action, check access again.
  • A teammate losing push is noticed within 10 minutes, or when the machine's connector next asks. A running build is not stopped for it.
  • A chosen machine that could run the Planner but had not yet been asked when its owner stops sharing still hands the document to Default machine (its choice was placing it); one that could not run it, and whose document nobody uses, no longer does.
  • A build on a teammate's machine produces pull requests from the owner's account; the note the agent is told to add depends on the agent following it.
  • A machine that serves every repository lists every repository both can push to, with no bound, so the repositories of its row (and the response, which every open Chopin tab polls every few seconds) grows with that count, one name each. The table shows two names and a count; the phone row truncates. Lists read at most 100 pages of installations and of repositories in each, as the GitHub client's own listings do, so a repository past that is not found for a machine with no checkout of it; the document's own menu is not limited this way.
  • The owner's list is read with the token of whichever of their shared machines asked first; a machine that needs its GitHub authorization renewed is left out of every teammate's list, as the menu already did, so another machine of the owner is used.
  • A Planner attached on Default machine keeps the document while an editor's chosen machine is up, so the chosen machine takes it only after that Planner ends (unused for 30 minutes, stopped, or its machine disconnecting).
  • Two editors who chose different machines that can both run the Planner are each asked for theirs after a restart, as the stored choices carry no order; a live choice clears the other's, but a choice on a machine that was offline at the time stays, and whichever backend attaches first holds the document.
  • The creator choosing a machine that cannot run Planners (no Planner command) still leaves the document without a Planner, as before (Let a document's creator's machine choice decide where its Planner runs #771).
  • bun test needs GIT_CONFIG_GLOBAL=/dev/null when commit.gpgsign is set globally (one test's scratch repository). machine-choice.e2e.ts's "sidebar machine menu closes on browser history navigation" flakes when run in parallel repeats (2 of 4) and passes 6 of 6 one at a time; it flaked the same way on origin/main before this change.

Fixes #769

Assistant-model: Claude Sonnet 5.5
Assistant-workflow: goal (run c69c9dc3-c0be-49f1-a9f3-3afbfabdbf57)
Assistant-duration: 6h13m converged, estimated 90m
Assistant-verification: bun test passed on the final tree: 7071 pass, 0 fail, 22 skipped without a database, run with GIT_CONFIG_GLOBAL=/dev/null because the global commit signing breaks one test's scratch repository
Assistant-verification: bun test apps/server/src/storage/postgres not rerun: no storage file changed in this repair (the 126 passes on PostgreSQL 17.11 reported earlier were on the tree before it)
Assistant-verification: mutation checks passed against the final listing code (routes.ts has not changed since): nine one-line breaks of the new listing code were each caught by planner-dispatch.test.ts: the two lists cut at 50 (5 tests failed), the owner checked once per repository again instead of by list (7), the lists never held nor shared (4), no de-duplication of a list in flight (1), the owner's list held only as long as a person's (1), the owner's list kept per machine instead of per owner (1), a machine needing its authorization renewed still listed (1), a failed list held as long as a pass (1) and a failed list never held (1); the earlier mutation checks of the reach cache, the one-at-a-time checks, the unshare guards and the Default takeover were not rerun
Assistant-verification: bun run fix passed: no changes after the last edit, 71 lint warnings, as before
Assistant-verification: bun run types passed: whole workspace and e2e
Assistant-verification: bun run ci passed: dprint, oxlint, tokens, design contract, design record and design check
Assistant-verification: GIT_CONFIG_GLOBAL=/dev/null bun run e2e planner-picker machines machine-choice passed twice on freshly created databases, on the final server and e2e code (the only change since is one assertion added to planner-dispatch.test.ts): 57 passed, 0 failed, none retried, in parallel on four PostgreSQL 17 databases of one server at port 55440 with the client built from this checkout; machines, then machine-choice, then planner-picker one after another on one database, the opposite of their alphabetical order: 9, 35 and 13 passed; machines with --repeat-each=3 twice on that database: 27 passed each time, and "the machines table" alone, then "server or VM" alone, on a database the first had used: both passed; the machines suite with its e2e files as they were before this repair failed in "server or VM" with studio as a second teammate row; a run of the three suites on a database an earlier full run had used failed four machine-choice "populated sidebar" cases whose documents of the same titles were still in the sidebar, which no fresh database has
Assistant-verification: screenshot viewed: /tmp/chopin-shots-769/machines-shared-no-checkout.png, retaken on the final tree (the Machines page of tessa, who has no project: "Shared with you", a Cloud group, Build box, casey, chopin 0.0.24, octo-org/score, Connected); the other screenshots under /tmp/chopin-shots-769/ were taken before this repair, which changes no web code
Co-authored-by: Alex Lavaee lavaman131@github.com

machines-shared-no-checkout

machines-shared-with-you

shared-machine-menu

shared-machine-building

…in a document's machine menu, and run their builds there

A machine its owner shares with teammates could run a teammate's Planner, but GET /api/connector/machines listed only the signed-in person's machines, so the Machines page and a document's machine menu never showed that machine, whether it was online, or let anyone pick it. A document that said "Planner: starting on <machine>" named a machine its people could not look at.

The list now includes the machines teammates share (`teammate: true`, with `owner: { login, avatarUrl }` and `repositories`), and the person's own rows say `teammate: false`. A teammate's row leaves out everything only the owner sees or sets: autoRun, reauthorize, finishing, favorite, shared, createdAt, and also the host, system and agent. A machine that is not shared, one that is revoked, one whose owner cannot push, and one that serves nothing the person can push to never appears; the owner-only routes (/label, /favorite, /shared, /revoke) still answer a non-owner 404. The owner's own shared machine is listed once, as theirs. It is listed wherever Chopin has machines, with or without Planners on machines: checking a repository takes the machine's own token, which under AUTH_MODE=local is the owner's live login.

The push check is Default machine's: `reach` now holds each answer by who was asked and the repository, behind ownerReaches(), asked with the machine's own token, held 10 minutes for a pass and a minute for a refusal, and asked once however many listings ask at once. A connected machine serves a repository by serves(), so a protocol 5 machine with no registered checkouts, which clones what it needs, serves every repository its owner can push to; an offline machine is listed, disabled, whatever it has checked out. The Machines page no longer depends on the person's projects, and for a machine that serves every repository it asks GitHub nothing per repository: it intersects two lists of what can be pushed to among GitHub App installations, as the repository picker lists them, every page of every installation, with no bound on how many repositories either side has. The person's own list is held a minute. The machine owner's is listed with the machine's own token, held 10 minutes (a minute after a failure) once per owner for everyone who looks, and asked once however many people and polls ask at once, so the owner's GitHub rate limit pays a few listings every 10 minutes however many teammates have the page open and however often it refreshes; before, up to 50 of each teammate's repositories were each asked with the owner's token every minute (about 3000 calls an hour for one teammate), and a repository past the first 50 the person could push to was missing until they worked in it. A machine that needs its GitHub authorization renewed is listed to no teammate, as the menu already did. Only the repositories a machine has checkouts of are asked one at a time, eight at a time: the owner's with the machine's token as above, and the person's, for one their installations do not list, with their own login once, kept 10 minutes for a pass and a minute for a refusal in the same `reach` map under their id, with the answers that have run out forgotten as a listing begins. The document's own menu names its repository and asks about it alone.

The Machines page gets a "Shared with you" table (a list of rows on a phone) with name, owner, release, repositories and state, and none of the star, switch, rename, disconnect or setup; an owner's avatar that fails to load no longer leaves their name out of line with its column. A document's machine menu lists the same machines under "Shared by <owner>", offline ones disabled. The sidebar count, the favorite and Automatic read the person's own machines alone, and the machine button shows for a person whose only machines are a teammate's.

Anyone who can edit a document may choose a listed machine for it, and the creator's own choice still decides where its Planner runs (#771). Another editor's choice counts only while the creator chose no machine, and it takes the Planner from Default machine only where its machine can run it for them at that moment (online, started with a Planner command, and for a teammate's machine its owner able to push): a Planner attached on the creator's machine stops, one that was only asked is withdrawn and loses its credential, and only the chosen machine is asked. A choice that cannot run it yet is stored and stops nothing: a Planner under way, a reply included, is never interrupted by it, nor later by that machine coming online, and the machine takes the document once it can run it and no Planner is attached on Default machine. Whether a stored choice places the Planner is derived from the stored choices on every ask, not from what the process remembers of an earlier takeover, so after a restart Default machine is not asked for a document whose stored choice places it on a machine that can run it, whichever of the two connects first. The creator choosing their own machine or Default machine takes it back: the Planner on the editor's machine stops and the choices that were placing it, even one whose machine had not been asked yet, are cleared; an editor's choice made while the creator's own stood is only stored, for their own builds and runs, and counts again once the creator chooses no machine. The owner stopping sharing takes the machine off teammates' lists, clears their choices of it, and lets a Planner it is replying with, asking about or running finish first. A document is given to Default machine then only where the machine ran its Planner, had been asked to run it or was placing it by that choice, or where someone uses the document; any other document just loses the choice, so a build-only choice starts no Planner on its creator's machine.

Choosing a teammate's machine now also sends that person's Build plan, Build, Retry, Run and manual rebuild there. The agent runs on the machine with its owner's GitHub account, so sharing is the owner's consent to that, and the copy says so on the sharing switch, the pairing card and the Machines page, in docs/chopin-cli.md and in the other docs that stated the old boundary. A build records the person who asked as `user`, and the owner as `machineOwner` when the machine is not theirs; only the connection it was queued on may claim or report it, and it is queued, offered and claimed only while the machine is shared, its owner can push to the repository (the same ownerReaches() check) and the person still chooses it. A queued build that stops qualifying ends saying so; a build already running finishes, like a Planner mid-reply. A choice whose machine is online but no longer takes the work fails with `machine-unavailable`, never on another machine; an offline one says it is offline. The lifecycle run is recorded as the person's, the Build tab names whose machine it ran on, and the coding agent is told, in `read_implementation` and `read_rebuild` and in its prompt, to end each pull request body with a note saying who requested it, because the pull request is the owner's. Rebuilds and prototypes Chopin starts by itself (LIVE_BUILD, LIVE_SPIKES) still run only on their builder's own machines with that machine's own opt-in, never on a teammate's.

The machines e2e's two tests that share a machine ("the machines table", which shares studio, and "server or VM", which pairs a machine shared by its key) now sign in as someone who can push to a repository of their own, `octo-org/relay-<id>`, which the fake GitHub shows only to them. A machine shared with teammates is listed, and offered in a document's machine menu, to everyone who can push to a repository its owner can, so one shared in octo-org/score showed up to every other test's person on the same server for as long as it lasted, and for good where a test never stopped sharing it, whatever order and beside whichever tests these ran: "server or VM" got studio as a second row and timed out, and machine-choice's menus and its machine button saw an extra machine. A machine they share is now listed to nobody else, so the tests no longer depend on each other, and the revoke "server or VM" needed at its end is gone.

Fixes #769

Assistant-model: Claude Sonnet 5.5
Assistant-workflow: goal (run c69c9dc3-c0be-49f1-a9f3-3afbfabdbf57)
Assistant-duration: 6h13m converged, estimated 90m
Assistant-verification: bun test passed on the final tree: 7071 pass, 0 fail, 22 skipped without a database, run with GIT_CONFIG_GLOBAL=/dev/null because the global commit signing breaks one test's scratch repository
Assistant-verification: bun test apps/server/src/storage/postgres not rerun: no storage file changed in this repair (the 126 passes on PostgreSQL 17.11 reported earlier were on the tree before it)
Assistant-verification: mutation checks passed against the final listing code (routes.ts has not changed since): nine one-line breaks of the new listing code were each caught by planner-dispatch.test.ts: the two lists cut at 50 (5 tests failed), the owner checked once per repository again instead of by list (7), the lists never held nor shared (4), no de-duplication of a list in flight (1), the owner's list held only as long as a person's (1), the owner's list kept per machine instead of per owner (1), a machine needing its authorization renewed still listed (1), a failed list held as long as a pass (1) and a failed list never held (1); the earlier mutation checks of the reach cache, the one-at-a-time checks, the unshare guards and the Default takeover were not rerun
Assistant-verification: bun run fix passed: no changes after the last edit, 71 lint warnings, as before
Assistant-verification: bun run types passed: whole workspace and e2e
Assistant-verification: bun run ci passed: dprint, oxlint, tokens, design contract, design record and design check
Assistant-verification: GIT_CONFIG_GLOBAL=/dev/null bun run e2e planner-picker machines machine-choice passed twice on freshly created databases, on the final server and e2e code (the only change since is one assertion added to planner-dispatch.test.ts): 57 passed, 0 failed, none retried, in parallel on four PostgreSQL 17 databases of one server at port 55440 with the client built from this checkout; machines, then machine-choice, then planner-picker one after another on one database, the opposite of their alphabetical order: 9, 35 and 13 passed; machines with --repeat-each=3 twice on that database: 27 passed each time, and "the machines table" alone, then "server or VM" alone, on a database the first had used: both passed; the machines suite with its e2e files as they were before this repair failed in "server or VM" with studio as a second teammate row; a run of the three suites on a database an earlier full run had used failed four machine-choice "populated sidebar" cases whose documents of the same titles were still in the sidebar, which no fresh database has
Assistant-verification: screenshot viewed: /tmp/chopin-shots-769/machines-shared-no-checkout.png, retaken on the final tree (the Machines page of tessa, who has no project: "Shared with you", a Cloud group, Build box, casey, chopin 0.0.24, octo-org/score, Connected); the other screenshots under /tmp/chopin-shots-769/ were taken before this repair, which changes no web code
Co-authored-by: Alex Lavaee <lavaman131@github.com>
@lavaman131
lavaman131 force-pushed the shared-machines-visible branch from 4a86349 to af373a1 Compare November 5, 2026 06:49
@lavaman131
lavaman131 merged commit 9fb72d4 into main Nov 5, 2026
24 checks passed
@lavaman131
lavaman131 deleted the shared-machines-visible branch November 5, 2026 06:55
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.

Show teammates' shared machines, read-only, on the Machines page and in the document's machine menu

1 participant