← the Atlas

ChromeBar identity rail (srv · client · kaval)

feature · budding ·implemented ·

The consolidated connection + build/commit readout in the ChromeBar. A2 shipped it as two coinciding columns (srv = server, pty = the in-process pty-host); B2 overtook that shape — renaming pty → kaval as a separate spawned daemon and adding a client-bundle column — so the live rail is three columns (srv · client · kaval). This note keeps A2's original design rationale, annotated where B2 took over.

The UI design for A2’s deliverable #3 — the consolidated connection + build/commit readout in the ChromeBar, shipped with A2 in #1063 as packages/client/src/ui/IdentityRail.tsx. Built in its final two-column shape from day onesrv (the server you’re connected to) and pty (the pty-host serving your terminals) — even though in A2 they’re the same process. That coincidence is the point: it’s the live proof A2’s identity plumbing works, and it means Phase B only has to let the columns diverge, never re-lay-out. The original prototype was rendered with the live dark-theme tokens from packages/client/src/index.css against the real ChromeBar.tsx layout.

The design call — two explicit columns in A2

The R-4 plan originally deferred the daemon chip to B, reasoning that an always-green “daemon connected” chip would lie about an absent daemon. But in A2 the pty-host isn’t absent — it’s in-process. So a two-column srv · pty readout where the columns coincide is the literal truth, not a lie. Better still, it’s the cleanest acceptance signal A2 could have:

The rail sits in the left identity cluster where the bare WebSocket dot used to be: it’s identity + liveness, not an action, so it groups with the logo, not the right-hand control cluster.

Wiring — three hops

The rail is thin presentation, but the data takes three hops, because the client speaks kolu-server’s surface (over the WebSocket), not the pty-host’s:

pty-host contract — ptyHostSurfacekolu-serverclient — IdentityRail.tsxsystem.version → { contractVersion, pid, startedAt, identity? }server.info → { commit (srv), ptyHost? (relayed identity) }srv ● <commit> · pty ● <commit> <staleKey> ≡ in-process fetched once at boot (in-process = always fresh)the existing one-shot server.info
A2 adds identity to the pty-host's system.version; kolu-server reads it once at boot and relays it onto its existing server.info (beside its OWN commit); the rail renders both columns from one client fact.
srvpty
liveness dotWebSocket status (the consolidated dot)in-process (A2) → daemon handle (B)
commitserver’s KOLU_COMMIT_HASHidentity.navigableCommit
buildidentity.staleKey (closure hash)
sourceserver.info (existing)system.version → relayed

Only pty carries a build: the staleKey is the @kolu/pty-host closure hash, and it’s the only thing whose staleness matters across a restart — the server always restarts on deploy, so it has no “survives” staleness, just a commit.

Three constraints baked in: (1) identity is optional on the wire so a B-phase older daemon isn’t force-restarted just to add a diagnostic (PTY_HOST_CONTRACT_VERSION unbumped); (2) currentBuildId/currentCommitHash re-export as values, not import type — the regression that collapsed the typed client to unknown; (3) the commit href is the full SHA (display slice(0,7)), matching the recovered 3fd7ea6 renderer and avoiding ambiguous-prefix edge cases.

States

In A2 the rail’s live axis is the WebSocket connection (the dot it absorbed). pty follows srv because it’s the same process — when the link is down the client can’t know anything, so pty reads unknown, not a false green.

StatePhaseRendering
Connectedlive in A2WebSocket open · srv ≡ pty · both commits + build resolved · the ≡ in-process tag
Connecting / reconnectinglive in A2re-handshaking — both dots pulse amber, identity dimmed until the first yield (srv connecting… · pty —)
Disconnectedlive in A2srv red; pty unknown (grey) — honest: with the link down we can’t claim pty state
Update pendinglive · B3.4kaval build ≠ the build the server would spawn → amber ⬆ update via the read-site kavalStale(expected, reported, state) derivation, comparing the server’s buildInfo.expectedKaval.staleKey against the connected daemon’s reported daemonStatus.identity.staleKey (B3.4, #1353 ). The nudge #1034 over-fired now fires only here.
Daemon deadlive · B2kaval handle closed → red (daemon dead — restart). Pairs with the honest degraded canvas; never the empty-canvas lie. B3.2 (#1337) added the inline Restart kaval button.

No “dev / no-commit” state exists — kolu and kaval run only under nix, which always bakes both the server’s KOLU_COMMIT_HASH and kaval’s KAVAL_BUILD_ID / KAVAL_COMMIT_HASH. There is no off-nix fallback to render.

Decisions (resolved with the maintainer)

This design revised the parent plan’s “daemon chip lands in B” stance: the identity + connection rail consolidates in A2 (honest, in-process), and B adds only the divergence semantics to the existing pty column.