Uh oh!
There was an error while loading. Please reload this page.
feat(cli): status-aware tracebloc home screen - #244
Conversation
Rework the bare-`tracebloc` home screen from a stateless command list into a status-aware landing screen that opens with where you actually stand — signed in? is this machine's secure environment live? — then the commands. Two separate, never-fused axes: sign-in (you) and the secure environment (the machine), because the client heartbeats with its own credential. A green "· Online" prints ONLY when the environment is live locally (chart present + jobs-manager Ready) AND positively confirmed heartbeating to tracebloc; a heartbeat we can't confirm degrades to "· running", never a false green. States: not-signed-in / online / running-not-heard-from / offline / no-environment. Detection is best-effort and bounded so bare `tracebloc` (run constantly, previously zero-I/O) stays snappy: probes run concurrently, each with its own short timeout, all capped by a ~1.5s overall budget; any error/timeout degrades to the softer state and the screen still renders. Logged out does zero cluster/backend I/O. An unreachable cluster caps at the probe timeout (~1.2s) instead of the OS default. Reuses existing seams: config sign-in + cached email, the data commands' namespace binding + release discovery, delete.go's `tb`-alias ownership check, and node allocatable for the compute parenthetical (no `resources` command on develop). `<inv>` echoes the invoked binary name (tb/tracebloc); the doctor path shown is the real `cluster doctor`. HEARTBEAT CREDENTIAL: read via the signed-in user token (ListClients), the only path that exists — there's no login-free in-cluster-client credential path, so the environment line is confirmable only while signed in (documented in realHeartbeat). Adds ui.CheckLine/CrossLine/WarnLine for the locked ✓/✗/⚠ status glyphs. Tests are table-driven and cluster-free: every state, the honesty fallback (can't-confirm → running, not Online), and the timeout/degrade path (a slow probe still renders fast with the softer state). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…, starting state Code-review follow-ups on the status-aware home screen: - A provisioned machine is never told "no environment / run the installer". A reachable cluster that doesn't host this release (wrong kube-context, or the client runs on a cluster this kubeconfig doesn't point at) now degrades to a NAMED offline when a client name is cached — the "runs elsewhere" case the sibling data commands explain via binding.explain — instead of the no-environment lie. Only a machine that was never provisioned shows no-env. - The budget-timeout degrade keeps the remembered name. resolveHomeModel now reads the cached client name up front (new rememberedName seam) and fills it whenever the probe surfaced none — including the bctx.Done() path — so a context-ignoring kubeconfig exec-credential plugin (aws eks get-token, etc.) that outlives the render degrades to a named offline, not no-env. - The offline copy is honest for BOTH causes (stopped/unreachable AND reachable-but-release-not-here): "· can't reach it from here — run <inv> cluster doctor", not the bare "offline". - Degraded workload gets its own state + line (homeStarting: "· starting up, not ready yet"), distinct from the live-but-unconfirmed-heartbeat "running, but tracebloc hasn't heard from it" (heartbeat is never consulted when the workload isn't Ready). Both still point at cluster doctor. - Header comment: the budget bounds the RENDER (wall-clock), not the probe goroutines — a context-ignoring probe can outlive it; the buffered channels just keep it from blocking. Invariants held + mutation-proven: no green Online without localLive + beatOnline (honesty), and detection never hangs (collector bails on the budget without probe cooperation). New/changed tests below; make ci green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LukasWodka
commented
Jul 12, 2026
👋 Heads-up — Code review queue is at 40 / 30 Above the WIP limit. The team convention is to review existing PRs before opening new work. Open PRs currently in Code review (oldest first):
Pull from review before opening new work. (This is a nudge from the kanban WIP check, not a block.) |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…copy (Bugbot) Two review findings on the status-aware home screen: - Budget expiry could drop completed probe results: when bctx.Done() and a buffered result are ready in the same select, the pick is random — a probe that finished just as the budget fired could be discarded for the softer default (live release rendering as offline/no-env). The collector (now collectProbes, extracted for a deterministic test) drains both buffers non-blocking on Done, so only probes that truly haven't reported degrade. - The running line said "tracebloc hasn't heard from it" for BOTH heartbeat answers. That claim is only earned when the backend positively reported not-online (offline/pending); a mere couldn't-confirm (backend unreachable, timeout) now says "couldn't confirm it's connected to tracebloc" instead of asserting a backend view we never obtained. homeModel carries confirmedNotOnline so the render stays pure. Both fixes mutation-proven (drain removal and flag collapse each fail their new tests); make ci green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
… (Bugbot) With no active client cached, realProbeEnv's discovery fell back to the kubeconfig's default namespace and then the cluster-wide scan (binding.allowScan() is true when the binding isn't applied) — so on a shared cluster a colleague's release could render as YOUR live environment, full data menu included. The data commands only run that scan behind a visible retarget note and an explicit user action; the home probe passes p=nil, so even that disclosure was silently dropped, and §7.5's rule (a miss must never silently retarget to some other client) applies doubly to a status screen. Ownership gate: no active-client binding ⇒ report localNoRelease before any cluster I/O — resolveHomeModel renders the honest no-env screen (or a named offline via the remembered-name fallback). Provisioned machines are untouched (binding scopes discovery to the active client's namespace, scan already disabled). Side effect: the common unprovisioned re-entry now does zero cluster I/O. Mutation-proven both ways (gate removed → the unprovisioned probe dials out and lands unreachable; over-gated → the provisioned probe stops short); make ci green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed resources
Design sign-off for the status-aware `tracebloc` home screen (cli#244): fold
the LOCKED reference layout into the shipping renderer — byte-for-byte for the
signed-in/Online screen.
- ui: add Printer.MenuRow (dim · bullet, command padded to width, 4-space gap,
dimmed description) — the locked command-row style.
- home: rework renderHome to the locked layout — two-blank header, greeting by
first name (profile → clean email local-part → omit gracefully), a 30-col dim
rule, the two honest status axes (detection + no-false-Online invariant
unchanged), two command buckets ("Your data" + "Your secure environment",
`delete` folded in, "Manage" dropped), rows via MenuRow, and a dim
`love from tracebloc` sign-off. Not-signed-in + no-env restyled to match.
- doctor: promote `doctor` to a real top-level command; `cluster doctor` stays
as a hidden alias sharing one RunE (single diagnostic code path). The home
screen + env-status lines now read `<inv> doctor`.
- home: gate the `resources` row on the live command tree — absent until #237
wires `resources`, appears automatically once it does (never a hardcode).
- tests: byte-identical lock test against the reference render; name derivation;
resources gating (render + command-tree, both ways); top-level doctor shares
the cluster-doctor path; all existing state/honesty/timeout coverage kept.
make ci green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>…nts to `doctor` Review follow-ups on the locked home screen (cli#244): - home: greetingName now runs BOTH the profile first name and the email local-part through one clean-token check (letters only, single word, no interior whitespace/newline/control chars) and caps the result at greetingNameMax = 14 runes — otherwise it omits the name. Stops a long local-part from stretching, or a newline-bearing FirstName from SPLITTING, the locked single-line header. The clean short demo name is unaffected, so the byte-identical golden render is unchanged. - doctor: retarget the now-hidden `cluster doctor` remediation hints to the canonical `tracebloc doctor` (the alias still runs, so non-breaking) — client.go x4 (create-fail hint, discovery-fail error, two connect-timeout errors) + cluster/discover.go's ErrNoParentRelease tail. Kept doctor.Run's suffix-strip in lockstep (it trims that exact tail so doctor never tells you to run doctor) and updated the two tests that pinned the old text. - tests: greeting-name bound/sanitize cases (over-long / interior-newline / tab / control-char / multi-word -> omit; cap boundary used); an end-to-end one-line-header guard; a direct hidden-alias assertion (`doctor` visible top-level, `cluster doctor` present but Hidden). Fixed the golden test's stale cmd/hsdemo comment. make ci green; golden render still byte-identical. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 1584849. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Lukas spacing tweak (cli#244): the status->first-section gap was 2 blanks (a standalone pre-loop Newline + Section's own leading blank) while the inter- section gaps were only 1. renderBuckets now emits a Newline before each Section, and the standalone pre-loop Newline is dropped — so every section gets exactly 2 blanks above and the status->first-section gap stays 2 (not 3). Mirrors the locked demo's new Newline+Section+Newline-per-bucket render. Golden test updated (+1 blank before "Your secure environment"); make ci green, render still byte-identical to the demo (850 bytes). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
aptracebloc
left a comment
There was a problem hiding this comment.
Reviewed end-to-end and verified the two open Bugbot findings against the code (not just taking the bot's word) — detail left on each thread. Strong PR: the bounded-probe robustness is well-built (ownership gate skips all I/O when there's no binding; per-probe + overall timeouts; the cap-1 buffered channel to shed a context-ignoring credential plugin), and the honesty rule is well-placed. Three adjustments before it's ready:
1. Please fix the PR description. "Scope deliberately deferred → No command renames … cluster doctor (not a not-yet-existing top-level doctor)" is no longer accurate — this PR does promote doctor to a top-level command (newDoctorCmd(false) on root), keeps cluster doctor as a hidden alias, and switches the banner + discovery hints to tracebloc doctor. Good change (it matches the #243 nav-map, which already showed top-level doctor as decided) — just update the description so the history isn't misleading.
2. doctor doesn't use the active-client namespace binding → it can contradict the home screen that now links to it. Confirmed real (Medium); detail on the doctor.go thread.
3. Provisioned-vs-no-environment keys off a different field than the ownership gate → a provisioned-but-nameless profile misclassifies as "no environment". Real fragility (Medium); detail on the home.go thread.
#2 and #3 share one root: the provisioned/namespace signal isn't sourced uniformly across the home screen and doctor. All three are cheap fixes — not approving yet on account of #2 (confirmed, user-visible) and #3. Happy to re-review once addressed.
— drafted with Claude (Opus 4.8), sent by @aptracebloc
Resolve internal/cli/root.go: keep the status-aware renderHomeScreen for bare `tracebloc` (the point of this PR) over develop's static banner. #237 (resources) is now merged, so the resources row renders — it stays gated on the live command tree, never hardcoded. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…review #2/#3) Addresses @aptracebloc's review — both confirmed Medium, one shared root: the provisioned/namespace signal wasn't sourced uniformly across the home screen and doctor. - doctor ignored the active-client binding: runClusterDoctor now applies bindActiveClientNamespace before cluster.Load — the same seam `cluster info` / data / the home screen use — so with no --namespace/--context it targets the active client's cached namespace, not the kubeconfig default. Removes the home-screen <-> doctor contradiction. Binding-only: a local config read, no extra cluster dial, matching what the home screen does when provisioned (check the bound namespace, no cluster-wide scan). - provisioned-vs-no-environment keyed off env.name (ActiveClientName) while the probe's ownership gate keyed off the cached namespace, so a provisioned-but-unnamed profile misread as "no environment / run the installer". resolveHomeModel now takes a `provisioned` signal from the SAME field the gate uses (ActiveClientNamespace) and renders offline on `provisioned || env.name != ""`; the display name falls back to the client ID so it stays a *named* offline. Tests (mutation-proven): new TestResolveHomeModel_States case (provisioned, namespace only, no name -> offline) + TestClusterDoctor_BindsActiveClientNamespace. Updated TestHasTopLevelCommand (resources is wired now that #237 merged). make ci green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LukasWodka
commented
Jul 13, 2026
Thanks for the careful pass, @aptracebloc — all three addressed,
Both fixes are mutation-proven; the shared root (uniform provisioned/namespace signal) is now closed. Re-review whenever. |
aptracebloc
left a comment
There was a problem hiding this comment.
Re-reviewed after 4c8bd22 — all three items from my earlier review are addressed and verified against the code:
- Description: the "No command renames" claim is removed (the PR does promote
doctorto top-level, keptcluster doctoras a hidden alias). doctorbinding (#2):runClusterDoctornow appliesbindActiveClientNamespace(&opts)beforecluster.Load— same seam as the home screen — so the home-screen ↔ doctor contradiction is gone. Covered byTestClusterDoctor_BindsActiveClientNamespace.- Provisioned signal (#3):
realRememberedClient()sourcesprovisionedfromActiveClientNamespace(the same signal the ownership gate uses) with a client-ID fallback for the display name, andresolveHomeModelrenders offline onprovisioned || env.name != "". The uniform-sourcing fix, with a new state-table case.
Both Bugbot threads resolved, no new findings on the latest commit, branch is current with develop, CI green. LGTM — approving.
— drafted with Claude (Opus 4.8), sent by @aptracebloc
Uh oh!
There was an error while loading. Please reload this page.
#241 was stacked on #237 (resources SHOW), which squash-merged to develop; develop also gained the #244 status-aware home screen + #231 top-level doctor. Resolved: - internal/cli/resources.go, resources_test.go → #241's (the BUILT `set`). develop's copy is #237's SHOW + the deferred `resources set` stub, and #241 is exactly the PR that replaces that stub with the real command. Only #237 ever touched these files on develop, so no SHOW change is lost. - internal/cli/root.go → develop's (new home screen + top-level doctor); #241 adds nothing to root.go (`resources` is wired via resources.go).
…elease policy (#296) Every claim re-verified against the actual command tree (go run ./cmd/tracebloc --help + each subcommand on develop @ 5af9a80): README: - v0.3.0 status blurb -> v0.8.0 latest release + a lifecycle command table (login/logout/auth, client/status, resources show+set, data ingest/list/delete/validate, cluster info, doctor, delete, version/completion/home screen); notes what landed post-v0.8.0 - 15-of-16 tasks -> all 16 (semantic_segmentation shipped in #247; registry verified: 16x CLISupported, zero false) - roadmap tail: 9-of-10-modalities + v0.2/v0.3 story -> v0.2-v0.8 arc; semseg removed from Next (done); cloud-source kept (backed by RFC-0002 + the in-help 1 GiB cap copy) - links the navigation map docs/cli-navigation.md: - stale-basis disclaimer removed (was develop @ 27c5392) - resources set (#241) + status-aware home screen (#244) un-dashed — both merged; proposed classDef dropped - nonexistent home-screen-spec.md references removed (point at #244) - doctor node: top-level doctor shipped, cluster doctor = hidden alias (pinned by TestClusterCmd_DoctorIsHiddenAlias) - known-gaps list pruned to what is still true (offboard partial teardown still exits 0 — re-verified in delete.go) scripts/RELEASE_CHECKLIST.md: - 5-line release policy: trigger = customer-visible merge or weekly, owner = DevEx squad (role, not person) - EKS smoke: dataset push -> data ingest; v0.1-ticket pre-flight genericized release.yml: header comment claimed release notes come from CHANGELOG.md — no such file exists; it's generate_release_notes: true. Fixes#296 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…release policy (#307) * docs: truth pass — README/nav-map/checklist match the v0.8 surface; release policy (#296) Every claim re-verified against the actual command tree (go run ./cmd/tracebloc --help + each subcommand on develop @ 5af9a80): README: - v0.3.0 status blurb -> v0.8.0 latest release + a lifecycle command table (login/logout/auth, client/status, resources show+set, data ingest/list/delete/validate, cluster info, doctor, delete, version/completion/home screen); notes what landed post-v0.8.0 - 15-of-16 tasks -> all 16 (semantic_segmentation shipped in #247; registry verified: 16x CLISupported, zero false) - roadmap tail: 9-of-10-modalities + v0.2/v0.3 story -> v0.2-v0.8 arc; semseg removed from Next (done); cloud-source kept (backed by RFC-0002 + the in-help 1 GiB cap copy) - links the navigation map docs/cli-navigation.md: - stale-basis disclaimer removed (was develop @ 27c5392) - resources set (#241) + status-aware home screen (#244) un-dashed — both merged; proposed classDef dropped - nonexistent home-screen-spec.md references removed (point at #244) - doctor node: top-level doctor shipped, cluster doctor = hidden alias (pinned by TestClusterCmd_DoctorIsHiddenAlias) - known-gaps list pruned to what is still true (offboard partial teardown still exits 0 — re-verified in delete.go) scripts/RELEASE_CHECKLIST.md: - 5-line release policy: trigger = customer-visible merge or weekly, owner = DevEx squad (role, not person) - EKS smoke: dataset push -> data ingest; v0.1-ticket pre-flight genericized release.yml: header comment claimed release notes come from CHANGELOG.md — no such file exists; it's generate_release_notes: true. Fixes#296 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: refresh README latest-release refs v0.8.0 -> v0.9.1 (#296) Addresses Asad's review on #307: this truth pass predated the v0.9.0 and v0.9.1 cuts, so a few version claims went stale. - Status blurb: "v0.8.0 is the latest release" -> "v0.9.1 is the latest release". - The post-v0.8.0 "ships with the next release" framing for resources / status-aware home screen / top-level doctor / semantic_segmentation -> "Shipped in v0.9.0" (they released in v0.9.0, no longer "next"). - Roadmap tail: "Since the v0.8.0 cut, develop gained ..." -> a "v0.9 added ...; v0.9.1 is the current latest" entry, extending the v0.2-v0.8 arc. Historical v0.8.0 references (the v0.8.0 cut, cluster doctor in v0.8.0) kept as deliberate contrasts. Homebrew tap stays dropped (#300) - no reintroduction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

Summary
Reworks the bare
tracebloc(no subcommand) home screen from a stateless command list into a status-aware landing screen: it opens with where you actually stand — are you signed in, and is this machine's secure environment live and connected — then the commands. This is the everyday re-entry point for a returning user, and the screen the installer's upcoming "stop-and-check" gate will hand off to on a healthy re-run.Design — two axes, never fused
· Onlineis printed only when the environment is live locally (cluster reachable + jobs-manager Ready) AND positively confirmed heartbeating (backendEdgeDevicestatus). Anything we can't confirm — heartbeat unknown, backend unreachable, probe timed out — degrades to· running, never a false green. This honesty rule is mutation-tested.States:
not-signed-in·online·running(live, heartbeat unconfirmed) ·starting(workload not Ready) ·offline / can't reach it from here·no-environment.Heartbeat credential — note for the follow-up installer PR
The online status is read via the signed-in user token (
lookupClientStatus→GET /edge-device/). There is no login-free path that authenticates as the in-cluster client itself — the chart'sCLIENT_IDsecret is readable, but nothing presents it to the backend as a credential. Consequence:Fine here — but it's an input for the installer "stop-and-check" gate (which runs before login); we'll decide there how the healthy hand-off behaves when not currently signed in.
Performance / robustness
Bare
traceblocruns constantly; the old screen did zero I/O. Detection here is concurrent and bounded (1.5s overall budget, 1.2s per-probe +RestConfig.Timeout), best-effort, never fatal. Measured: logged-out ~25ms; an unreachable / black-hole cluster caps at ~1.2s and degrades to a named offline rather than hanging. A context-ignoring credential-plugin probe (aws eks get-token/gke-gcloud-auth-plugin/az) can't hang the render — the render bails at the budget while the abandoned goroutine finishes in the background via a cap-1 buffered channel.Command surface
doctoris promoted to a top-level command (tracebloc doctor), matching the docs: CLI navigation map (single source of truth) #243 nav-map that already showed top-leveldoctoras decided.cluster doctorstays as a hidden alias on the sameRunE, so existing docs, muscle memory, and scripts keep working; the banner + discovery hints now point attracebloc doctor.resourcesrow now renders, since feat(resources):tracebloc resources— show this machine's allowance (SHOW; set deferred) (#143) #237 (tracebloc resources) has merged. It's gated on whether the command is actually registered on the root (the live command tree, never a hardcode), so a row never names a command that isn't there.tbprefix + tip show only when a real installer-placedtbalias exists and you invoked the long name.Code review
Ran an adversarial review before opening. The three critical guarantees — never lies about Online, never hangs, never crashes — all hold and are mutation-proven. Fixed in the second commit:
datacommands'binding.explain).Consciously not addressed here (follow-ups): glyphs/emoji under
--plain(a CLI-wide--plainpolicy call), the 3×config.Load/ re-GETof jobs-manager (cheap + bounded), and GPU counting onlynvidia.com/gpu(documented NVIDIA-only).Test plan
make cigreen: build, vet,gofmt -s,go test -race -cover ./..., errcheck / ineffassign / misspell, schema-check.Checklist
developmake cigreendoctor;cluster doctorhidden alias;resourcesrow gated on the live tree)PR 1 of 2. PR 2 = the installer "stop-and-check" gate (client repo) that recognizes an existing install and hands off to this screen instead of re-provisioning.
Note
Medium Risk
Every bare CLI invocation may run bounded Kubernetes and backend probes, which affects UX timing and status accuracy but not auth or data paths. Doctor path consolidation is low risk; the home screen state machine is the main regression surface.
Overview
Bare
tracebloc(no subcommand) is now a status-aware home screen instead of a static command list: greeting, separate sign-in and secure-environment lines, then command buckets. Detection is concurrent and time-bounded (~1.5s); probes degrade softly and never fail the command.Online only when the client is live locally (jobs-manager Ready) and the backend positively reports online via the signed-in user token; unconfirmed heartbeat stays running, never a false green. An ownership gate skips cluster discovery without an active-client binding so unrelated installs are not shown as yours; remembered client names keep provisioned machines on a named offline path when probes time out or miss the release.
doctoris promoted to a top-level command (home screen and failure hints point here).cluster doctorremains as a hidden alias sharing the sameRunE. User-facing copy and discovery errors switch fromtracebloc cluster doctortotracebloc doctor. Theresourcesmenu row is gated on whether that command is registered on the root.New
uihelpers (MenuRow,CheckLine,CrossLine,WarnLine) support the locked layout; broad table tests cover states, honesty, timeouts, and the doctor alias.Reviewed by Cursor Bugbot for commit 03ad7d3. Bugbot is set up for automated code reviews on this repo. Configure here.