Skip to content

feat(fleet): rebuild the dashboard's start and status logic - #162

Merged
outofcoffee merged 6 commits into
mainfrom
dashboard-start-status-rebuild
Sep 4, 2026
Merged

feat(fleet): rebuild the dashboard's start and status logic#162
outofcoffee merged 6 commits into
mainfrom
dashboard-start-status-rebuild

Conversation

@outofcoffee

@outofcoffee outofcoffee commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Rebuilds the fleet dashboard's start reporting, refresh ordering and tile rendering so a node has one description of its state rather than two that can contradict each other, and records the CLI's UX conventions in a spec of their own.

Summary

  • A start reports a fleet.StartPhase — what it is doing, when that began, when the next attempt is due — instead of a line of text. Each transition replaces the value outright, so a situation the start has moved on from cannot be left on a tile. This is what the reported defect was: instance no-capacity; retrying in 120s sitting beside a refresh reporting the node running.
  • fleet.RenderPhase(phase, now) is the only place a phase becomes text, and it reads the clock at draw time. A wait counts down, a boot counts up, and no rendered number can be left at a value a transition has passed. spinloop remote start renders the same phases to stderr, so the tile and the CLI cannot word one situation differently.
  • Every reading carries the time its own call returned (fleet.NodeResult.At, stamped in the fan-out), and the board draws a reading only when it was taken later than the one on screen. This replaces the fastGen/slowGen counters, which ordered rounds against each other but not against an action completing — so a round issued before a start finished and landing after it would repaint the node's pre-action state.
  • A reading that ages past three of its node's own intervals shows its age and drops to the unknown health tier, rather than being drawn identically to one just taken.
  • A node with an action in flight is read on the short interval whatever its kind, and returns to its own cadence once the action settles. Its neighbours keep theirs throughout.
  • One function, dashNodeView, produces both a panel's lines and its health tier from arguments alone. Every pairing of a start's phase against a reading is now enumerable in a test, including the one that produced this defect.
  • Tiles gained a coloured header bar, an in-flight verb gained a spinner, and the board gained a title bar of its own. The selected panel's border is the brand mint rather than the amber it shared with the attention health tier.
  • New cli-ux spec: one accent used only for the tool's own chrome and never for a state, a stdout a program consumes carrying nothing else, decoration only where there is a terminal to draw it on, errors that name the fix, help as a lowercase imperative phrase, British spelling, what a long operation must keep saying, confirmation before a destructive action, and the full-screen affordances a web page has no equivalent for.
  • cmd/spinloop/palette.go holds the colours and the spinner every surface draws from. The ten braille frames were declared in both fleet.go and dashboard_model.go, and the five state colours in both fleet.go and the dashboard's health switch; each is now one definition.

Implementation details

Why a value rather than a better-worded line. The quick fix in 03718af corrected the reported symptom by writing a line at the transition that had been missed. That is correct only while every write site covers every transition, and an omission produces no error and no test failure — the output is a well-formed line carrying an out-of-date value. A phase holds one value per start and each write replaces the previous one, so a superseded situation is not retained by construction.

Why timestamps rather than a per-node counter. A counter orders readings equally well, but carries no age, so showing how old a reading is would need a second field beside it. At supplies both the ordering and the age.

The read cadence is per node, not per group. The group busy-guard stays — no second round starts over one still in flight — but each node carries its own next-read time, which is how an acted-on cloud environment is polled every 2s while its neighbours stay on 60s. The round's context deadline remains its kind's interval, so shortening the cadence during an action does not shorten what a signed control-plane call is given to answer in.

internal/remote is untouched. remote.Start's existing progress and onState callbacks already supply everything the phases are built from; fleet.StartPhases maps that pair onto the phase stream, and both the dashboard and the CLI drive it.

Where the shared palette sits in the history. The rename that consolidated the colours and the spinner happened in place across three files, so the commit that introduces palette.go is the one carrying the dashboard work rather than the one adding the spec — splitting it the other way would leave the first commit unable to compile, and fleet.go's state colours colliding with the shared set.

One behaviour-visible wording change outside the dashboard. spinloop remote start's stderr lines are reworded, since they now render phases. Nothing parses them — the eval-able exports go to stdout and are unchanged — and the CLI tests that assert on them are updated.

…ogic

The dashboard draws two independent accounts of a node — the status lines a
start reports, and the last completed refresh — with no rule for which is
fresher or what to do when they contradict. 03718af fixed the line that
prompted this; the shape that produced it is untouched.

Proposes four changes: a start reports a StartPhase value rather than a
string, so supersession is structural; rendering is a pure function of the
phase and the current time, so no number can freeze; every observation
carries the time it was read, applied monotonically; and a node with an
action in flight refreshes on the fast interval whatever its kind.

Proposal only — no code changes.
@outofcoffee outofcoffee added the documentation Improvements or additions to documentation label Sep 3, 2026
Rewrites the proposal, design and task wording to state what the code does
rather than what it wants, knows, remembers or forgets. Also corrects the
comments added in 03718af, and a garbled sentence in dashActionProgress that
misstated the repaint interval.
The dashboard kept two descriptions of a node — the lines a start wrote and
the last refresh round — and compared them for neither recency nor
contradiction, which is how a capacity wait came to sit beside a refresh
reporting the node running.

A start now reports a `fleet.StartPhase` that each transition replaces
outright, rendered by one function of the phase and the current time, so a
wait counts down, a boot counts up, and a superseded situation cannot be left
on screen. `spinloop remote start` renders the same phases, so the tile and
the CLI cannot word one situation differently.

Every reading carries the time its own call returned, and the board draws one
only when it was taken later than what is on screen — which the per-group
counters could not do, since they ordered rounds against each other and not
against an action completing. A reading that ages past its node's cadence
shows its age and reads unknown, and a node with an action in flight is read
on the short interval until it settles.

A tile's first line is now a coloured header bar, an in-flight verb carries a
spinner, and the board has a title bar of its own; the selected panel's border
is the brand mint rather than the amber it shared with the attention tier.
What goes on stdout, how an error is worded, when a spinner is drawn and which
colour means what were each decided once and since copied from whichever
neighbouring command was nearest. That works while there is a neighbour to
copy, and gives a reviewer nothing to point at.

`cli-ux` records them: one accent used only for the tool's own chrome and
never for a state, a stdout a program consumes carrying nothing else,
decoration only where there is a terminal to draw it on, errors that name the
fix, help as a lowercase imperative phrase, British spelling, what a long
operation must keep saying, confirmation before a destructive action, and the
full-screen affordances a web page has no equivalent for. It is the CLI
counterpart to the site's design-language spec rather than a copy of it.

The survey behind it found one command wording that breaks the British
spelling rule, corrected here so the spec does not land already violated.
@outofcoffee outofcoffee changed the title docs(openspec): propose rebuilding the dashboard start/status logic feat(fleet): rebuild the dashboard's start and status logic Sep 4, 2026
Its nine requirements become `openspec/specs/cli-ux/spec.md`, the capability
every command's conventions are now recorded under.
@outofcoffee outofcoffee added enhancement New feature or request go Pull requests that update go code and removed documentation Improvements or additions to documentation labels Sep 4, 2026
@outofcoffee
outofcoffee merged commit 9e504f5 into main Sep 4, 2026
2 checks passed
@outofcoffee
outofcoffee deleted the dashboard-start-status-rebuild branch September 4, 2026 00:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request go Pull requests that update go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant