Skip to content

fix(web): no fleet, no fleet band - #49

Merged
karngyan merged 1 commit into
mainfrom
fix/fleet-band-no-relay
Aug 10, 2026
Merged

fix(web): no fleet, no fleet band#49
karngyan merged 1 commit into
mainfrom
fix/fleet-band-no-relay

Conversation

@karngyan

Copy link
Copy Markdown
Contributor

The state that produced the wrong advice

The fleet gap band on Sessions says this browser holds no key for the fleet and
that pairing again from any machine on it will fix that. On a machine with no
relay, every clause is false — there is no fleet, no others to be missing, and
nothing to pair against — and it is the first screen a new user opens.

The band is fed by FleetGaps, which only an expansion produces, so it can only
appear on a tab that has learned a relay origin from its daemon's welcome. What
the fleet never did was give that origin back. A tab open across a flue relay leave — or a relay.json deleted by hand and the daemon restarted under it —
reconnects to a welcome naming no relay at all, and on main it keeps:

  • the last expansion's gaps snapshot, for the life of the tab, so the band goes
    on describing a fleet that no longer exists; and
  • the origin itself, so the discovery tick reads a directory through a machine
    that has left the relay, once a minute and on every focus, for an answer that
    can only be its own daemon's 404. (Measured: four reads in the three minutes
    the new test covers.)

The fix

localWelcome now reads the fact it was already being sent and ignoring. A
welcome with no relay on it — absent, or status: 'off', which
client/protocol.ts says are one statement — is this machine saying it is on
none. The origin is dropped, the once-per-epoch expanded flag goes with it so
a machine that joins a relay again builds against the new origin, and the gaps
snapshot is dropped because it describes a fleet that is not there. noteGaps
refuses a late one while no relay is known, so an expansion still in flight when
the welcome lands cannot put the band back up.

A relay that is merely connecting is a relay — configured, dialling, about to
name an origin — so it is untouched. The slots already built stay exactly where
they are: this machine leaving a relay says nothing about whether the other
machines are still on it, which is the same reason adoptRemotes is additive.
No new fetch and no new wire field: the signal was already on every welcome.

What the band says in each state

State Band
No relay configured, this load or since (welcome.relay absent or off) Nothing. One machine and no relay is a whole state, not a gap; the invitation to set up remote access belongs to the Remote screen, which can carry it out.
Relay configured, no fleet key, no ceremony to this browser's name (the loopback tab) Unchanged from #46: "This tab is talking to one machine and holds no key for the fleet… A machine hands its own browser one as soon as it has joined a relay — check flue status on this machine, then reload."
Relay configured, no fleet key, but a machine this browser paired with Unchanged: that machine hands one over on its next welcome, "as soon as that machine answers".
Relay configured, fleet key held, machines with no certificate to present to Unchanged: clear this site's storage here, or pair again from anywhere else.

On the loopback-with-relay case specifically: enrolThisBrowser answers a bare
boolean, so 409 (this machine holds no fleet key), 502 (the relay would not
answer) and 404 (an old daemon) are indistinguishable by the time anything
downstream sees them. Wiring that distinction means changing enrolment's return
type, the FleetOptions.enrol seam and FleetGaps — a larger change than this
one, through the trust path — so the band keeps #46's sentence, which is already
the enrolment story rather than pairing advice and is true of all three. Left
for a follow-up if that state turns out to be common.

Test evidence

Four new tests; three fail on main.

The report, at the screensessions.test.tsx, takes the band down when
its machine leaves the relay
. Fails on main with the band still up:

+ Received:
   and holds no key for the fleet, so it cannot see the rest of it. A machine
   hands its own browser one as soon as it has joined a relay — check
   flue status on this machine, then reload.

The pointless readsfleet.test.ts, stops reading once the machine has
left the relay it named
: expected "vi.fn()" to be called 1 times, but got 4 times.

The rejoinexpands again for a machine that joins a relay a second time:
expected 2 times, but got 1 times on main, because expanded was never
lowered.

The snapshotforgets the fleet when the welcome says this machine is not
on a relay
: fleet.gaps() is non-null on main.

Plus one guard that passes before and after, pinning the state in the report
outright: says nothing at all about a fleet on a machine with no relay
welcome with no relay, one machine, no band, and the session still listed.

$ cd web && pnpm vitest run
 Test Files  59 passed (59)
      Tests  1170 passed (1170)

$ pnpm run lint
$ tsc --noEmit      # clean

styles.build.test.ts (the real vite build, guarding the Tailwind prose
scanner) is in that run and green — the new comments were written with it in
mind. No Go touched.

🤖 Generated with Claude Code

The sessions screen's gap band tells a reader their browser holds no key
for the fleet and that pairing again from any machine on it will fix
that. On a machine with no relay every clause of that is false: there is
no fleet, no others to be missing, and no machine to pair against — and
it is advice that cannot be followed and would fix nothing if it were.

The band is fed by FleetGaps, which only an expansion produces, so it
could only ever appear on a tab that had learned a relay origin. What it
never did was give that origin back. A tab open across a `flue relay
leave`, or a relay.json deleted by hand and the daemon restarted under
it, reconnects to a welcome naming no relay at all — and kept the last
expansion's counts for the life of the tab, plus a discovery tick asking
a directory through a machine that had left, once a minute and on every
focus, for an answer that can only be its own daemon's 404.

So the fleet reads the fact it was already sent and ignored: a welcome
with no relay on it — absent, or `off`, which the protocol says are one
statement — is this machine saying it is on none. The origin goes, the
once-per-epoch expansion flag goes with it so a machine that joins again
builds against the new origin, and the gaps snapshot goes because it
describes a fleet that is not there. noteGaps refuses a late one for the
same reason, so an expansion still in flight cannot put the band back up.

The slots already built stay: this machine leaving a relay says nothing
about whether the others are still on it, which is the same reason
adoptRemotes is additive.

What each state says now. No relay, this load or since: nothing — one
machine and no relay is a whole state, and the invitation to set up
remote access belongs to the Remote screen, which can carry it out. A
relay and no fleet key with no ceremony to this browser's name: the
enrolment story from #46, unchanged — enrolThisBrowser answers a bare
boolean, so 409 (this machine holds no fleet key) and 502 (the relay
would not answer) cannot be told apart here, and `check flue status on
this machine` is the honest sentence for both. A relay, no fleet key,
and a machine this browser paired with: that machine is about to hand
one over, unchanged. A relay and machines with no certificate to present
to: unchanged.

Tests: two at the fleet, two at the screen. The screen's `takes the band
down when its machine leaves the relay` is the one that reproduces the
report — it fails on main with the band still up, its text quoted in the
diff — and `stops reading once the machine has left the relay it named`
fails at four directory reads where it wants one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@karngyan
karngyan merged commit 97cb210 into main Aug 10, 2026
1 check passed
@karngyan
karngyan deleted the fix/fleet-band-no-relay branch August 10, 2026 19:41
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