Skip to content

feat(ui): read substrate statuses as words, and chart the actor mix - #2706

Draft
Charlesthebird wants to merge 9 commits into
mainfrom
Charlesthebird/substrateStatusText
Draft

feat(ui): read substrate statuses as words, and chart the actor mix#2706
Charlesthebird wants to merge 9 commits into
mainfrom
Charlesthebird/substrateStatusText

Conversation

@Charlesthebird

@Charlesthebird Charlesthebird commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

🤖 written by Claude (start)

Changelog

Substrate actor states read as words rather than wire constants, and a bar above the Actors table shows the mix — a segment per actor, with a legend of every state a controller can report.

Closes #2702

Testing

  1. cd ui && VITE_API_MODE=mock yarn dev, open /substrate.
  2. Actors shows Crashed and Deleting, not ACTOR_STATE_*.
  3. The bar has eight segments matching the eight rows, parked ones last; hover it for the breakdown.
  4. Search for 7f21 — the bar and its caption narrow with the table.
  5. Switch themes and check both.

Additional Notes

Status borders measured 1.28–2.83:1 against their own fill, so the tokens moved to at least 3:1 in both themes. That touches every antd component reading colorSuccessBorder and friends, not only this page.

The Workers table is untouched: its Actor column is empty on every cluster (#2709), so a bar there would read as an idle fleet while pods are running.


🤖 written by Claude (end)

The controller names the actor states it knows and falls back to the protobuf
constant for the rest, so `ACTOR_STATE_CRASHED` reached the page unread. The
substrate page now turns any constant-shaped status into a word and drops the
enum-name prefix that only repeats the column header.

Crashed and failed get their own tone rather than sharing amber with the
transitions, and the "Actors by status" tags are chips now, so the summary is
coloured the same way the table is.

Fixes #2702

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Nicholas Bucher <behappy54321@gmail.com>
@github-actions github-actions Bot added the bug Something isn't working label Sep 4, 2026
Charlesthebird and others added 3 commits September 4, 2026 13:35
The fixtures held three of the nine states a controller can report, so the idle
tone was drawn by nothing and `ACTOR_STATE_CRASHED` was the only wire constant
under test — enough for a humaniser that special-cased that one word.

Deletion no longer shares the transition colour. It is the one in-flight state
that does not come back, and an actor reading the same shade as one taking a
snapshot is an actor nobody looks at twice.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Nicholas Bucher <behappy54321@gmail.com>
The tiles say how many actors are running; nothing said what the rest were
doing, and with the table paged there was nowhere to find out — a reader on
page one of 410,110 could not learn that most of them had crashed.

Each card now carries a bar above its table, a segment per actor or pod,
coloured by status and grouped with everything parked at the end. The legend
lists every state a controller can report, so `Crashed` is a thing a reader
knows can happen before one does.

The tab icon is the kagent mark, and `branding.faviconUrl` lets a distribution
replace it alongside the name and logo it could already replace.

Status borders were 1.28-2.83:1 against their own fill, which is a decorative
edge rather than a boundary. Every tone now measures at least 3:1 in both
themes, and the idle chip takes the strong border token rather than the app's
hairline.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Nicholas Bucher <behappy54321@gmail.com>
`ateapi.Worker` carries no actor reference — the binding is recorded on the actor,
in `ActorStatus.worker_assignment` — so every worker came back with an empty actor
and the workers table reported an idle pod for each one, whatever was running on it.
Introduced in #2697, which dropped the fields when v0.0.25 removed the singular
assignment from `WorkerStatus` and put nothing in their place.

Joined in the service, where both whole lists are in hand: the reads that reach a
browser are pages, and a join across two pages matches only where they overlap.

A worker holds several actors under v0.0.25 and `SubstrateWorker` has room for one,
so the lowest actor id on the pod wins. Lowest rather than first seen because
ate-api returns actors unordered — by arrival the cell would name a different actor
on each poll. Carrying the whole set needs a repeated field in system.proto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Nicholas Bucher <behappy54321@gmail.com>
@Charlesthebird Charlesthebird changed the title fix(ui): read substrate statuses as words, not wire constants feat(ui): show the substrate's actor and worker mix, and name the actor on each worker Sep 4, 2026
@github-actions github-actions Bot added enhancement New feature or request and removed bug Something isn't working labels Sep 4, 2026
Charlesthebird and others added 3 commits September 4, 2026 16:10
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Nicholas Bucher <behappy54321@gmail.com>
The workers bar coloured its segments from the loaded actor page, so searching,
sorting or paging the *actors* table recoloured the *workers* one — a pod whose
actor fell off the page read as `Not loaded`, a status no cluster reports. The
controller has both whole lists when it builds the response, so it carries the
status on the worker entry and the page joins nothing.

Also from review:

- Every actor is offered to the join, not only those the atespace scope kept. A
  worker running an actor whose template lives elsewhere reported itself idle.
- A live actor on a shared pod wins over a parked one; the lowest id breaks ties.
- A failed read no longer renders as "No actors in this scope", and the legend
  stays when a scope empties so the table beneath it does not jump.
- The per-actor ceiling is 80, which is the widest row that fits the card.
- An extension's PNG favicon is no longer served under an SVG type, and the
  created link carries the marker so a second call retargets it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Nicholas Bucher <behappy54321@gmail.com>
`ActorStatusLabel` fell back to the wire constant for a state it had not been
taught, and callers sort on what it sends while showing what they sorted — so
`ACTOR_STATE_DELETING` filed itself under A and read as "Deleting" beneath a
heading that says sorted by status.

Every value is named now, including ones added after this build. The test walks
`ActorState_name` rather than a list, so a new state fails here instead of
reaching a reader as a protobuf symbol.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Nicholas Bucher <behappy54321@gmail.com>
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Sep 4, 2026

@Charlesthebird Charlesthebird left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This automated review is a first pass. A manual review will follow once the blockers are addressed.


Reviewed the branch. Nothing blocks merge. The review below was written against a larger branch that also carried the controller fix; that work came out in 62182d90 and moved to #2709, so only the findings against the UI still stand.

Found and fixed — four, already pushed
  • A failed read rendered as "No actors in this scope", showing a broken backend as a healthy empty cluster (ffe57a7c).
  • The legend vanished when a scope emptied, moving the table under whoever was reading it — the thing the comment above it argues against (ffe57a7c).
  • The bar asked for 1077px at its 120-segment ceiling and spilled outside a ~990px card; the ceiling is 80, which is 717px (ffe57a7c).
  • An extension's PNG favicon was served under type="image/svg+xml", and the fallback link lacked the marker so a second call appended a second icon (ffe57a7c).

What I checked

✅ Every status border measures at least 3:1 against its own fill — worst case 3.01:1 light, 3.02:1 dark, up from 1.28:1 and 1.96:1.
✅ Green and red stay apart under deuteranopia, protanopia and tritanopia (text ΔE 47–87), and no status is carried by colour alone: every chip and legend key is labelled.
✅ The actors bar needs no controller change — it reads actorStatusCounts from the summary, and humanizeEnum covers the wire constants the shipped controller still sends for CRASHED and DELETING.
✅ The fixtures match the shipped controller: raw constants for those two states, words for the rest.
✅ Nothing under go/ or proto/git diff against the merged main is empty for both.

Left for #2709

The Workers card has no bar. workerFromProto sets no actor fields, so actor_id is empty on every cluster and a bar there would draw an idle fleet while pods are running. The issue carries the diagnosis, the data-model question and this dependency.


🤖 written by Claude

The worker→actor mapping is a controller bug with its own cause (#2697) and its
own issue (#2709), and fixing it raises a data-model question — a v0.0.25 worker
holds several actors while `SubstrateWorker` has room for one — that belongs to
whoever owns substrate rather than to a UI change.

The workers bar goes with it. `workerFromProto` sets no actor fields at all, so
`actorId` is empty on every cluster: the bar would read as an idle fleet while
pods are running, which is worse than not drawing it.

What stays needs no controller change: the actors bar reads the summary's own
counts, and a wire constant is still read as a word by the page.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Nicholas Bucher <behappy54321@gmail.com>
@Charlesthebird Charlesthebird changed the title feat(ui): show the substrate's actor and worker mix, and name the actor on each worker feat(ui): read substrate statuses as words, and chart the actor mix Sep 4, 2026
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Sep 4, 2026
@Charlesthebird

Copy link
Copy Markdown
Collaborator Author

A bar above the Actors table, one segment per actor, coloured by status with everything parked sorted to the end. The legend lists every state a controller can report, so Crashed is visible at zero rather than only once one happens. Hovering gives the full breakdown.

The Workers card has no bar: its Actor column is empty on every cluster (#2709), so one there would draw an idle fleet while pods are running.

substrate-light substrate-dark

🤖 written by Claude

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[UI] Substrate page should show statuses as user-facing text instead of uppercase enums

1 participant