Skip to content

[bug] One laptop still shows as three machines — nothing has claimed an id yet, the stale names can never be claimed, and the Terminals page does not fold by machine #393

Description

@serge-ivo

#379 shipped the right mechanism. Two things stop it clearing what is already on the owner's
account, and one of them will still be true after every machine upgrades.

Live state, measured on the owner's account today (post-#379 deploy)

GET /v1/terminals/nodes
  Mac                      connected=true   lastSeen=2026-08-07 23:23  v=0.4.35  16 instances  machineId=None
  RLs-MacBook-Air.local    connected=false  lastSeen=2026-08-07 09:20  v=0.4.35  16 instances  machineId=None
  RLs-MacBook-Air          connected=false  lastSeen=2026-07-16 22:28  v=0.4.18   2 instances  machineId=None
  Sergeys-Mac-mini.local   connected=true   lastSeen=2026-08-07 23:23  v=0.4.22   4 instances  machineId=None

The first three are one laptop. Every row has machine_id: NULL, so foldNodesByMachine has
nothing to fold and aliasNodesFor can prove nothing — which is exactly the fail-closed behaviour
#379 designed, working as intended.

1. Nothing has claimed an identity yet, because the machine is on an old CLI

Published: 0.4.40. Installed and running on that laptop: 0.4.35, which contains zero
occurrences of machineId in its bundle. So no registration has ever carried an id, and none will
until the CLI is upgraded and pags up restarted.

Not a bug — but worth stating plainly somewhere the user sees it, because from the console the fix
looks like it did nothing. A runner that predates the feature is indistinguishable, in the UI, from
one that has it and found nothing to merge.

2. After the upgrade, the two stale rows still will not merge — by construction

machine.ts's own docstring says it:

"on the first run the machine can only claim the hostname it already registered under, which
changes nothing. The moment the network renames it, the list holds BOTH names"

The name history starts at upgrade time. A fresh machine.json on that laptop will hold
{ id, names: ["Mac"] }, so claimMachineNames stamps the Mac row and nothing else.
RLs-MacBook-Air.local and RLs-MacBook-Air were minted before any id existed and no machine will
ever claim them — they are permanent residue, listed forever as offline machines the user
recognises as their own laptop.

That is the correct trade (guessing costs the guarantee), and it leaves the account needing a
manual escape that does not exist.

There is no way to forget a node. No route, no control. The only listed options are to leave
them or edit D1 by hand.

3. The picker folds; the Terminals page does not

GET /v1/instances/:id/runner-node folds correctly — instances.ts:421 calls
foldNodesByMachine, with the reasoning in a comment.

GET /v1/terminals/nodes does not: grep machine_id workers/api/src/routes/terminals.ts
returns nothing, and groupTerminalNodes groups by the runner_node string. That endpoint feeds
both the Terminals page and the "Runs on" tiles in RunnerPanel (machinesToShow
/v1/terminals/nodes), which is the surface the duplicates were reported on.

So once ids do exist, the two surfaces will disagree: the picker's dropdown shows one machine and
the tiles beside it still show three.

Do

  • Fold /v1/terminals/nodes by machine_id — reuse foldNodesByMachine; the rows already
    carry the column. Without this the fix is invisible on the page it was reported from.
  • "Forget this machine" — an explicit, confirmed user action (never a cron, never a
    delete-on-stale sweep: a laptop that is merely closed must not lose its pins). Needs a route
    and a control on the Terminals page. Guard the case where the node still holds pins or
    coding_sessions.runner_node rows — offer to repoint them, or refuse and say why.
  • Say when a runner is too old to be identified. The node rows carry runner_version; a
    row whose version predates machine-id support could read "this machine's CLI is 0.4.35 —
    upgrade to merge it with its other names"
    , which turns an invisible no-op into an
    instruction.

While we are here: "one pags up per machine" is not what is enforced

The 409 lives at the RelayDO and is keyed per (instance, node NAME) — a second runner on the
same hostname for the same instance is rejected unless --force. With three hostnames, the
platform sees three machines and nothing ever conflicted. Node rows are also never deleted on
disconnect, so each identity persists after its name stops being used. Both behaviours are correct
in isolation; together they are why one laptop can occupy three slots and no guard notices.

Follows #379 (the mechanism) and #380. Evidence gathered live via the MCP server + REST on the
owner's account, 2026-08-08.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions