carlos / platform Public
--- target: CARLOS console (internal/console/templates) totalscore: 19 maxscore: 40 naheuristics: p0count: 2 p1_count: 3 timestamp: 2026-08-09T15-36-14Z slug: internal-console-templates ---
CARLOS console — ease-of-understanding critique
Method: dual-agent (A: design review · B: deterministic detector)
Design Health Score
| # | Heuristic | Score | Key Issue | |---|-----------|-------|-----------| | 1 | Visibility of System Status | 2 | State everywhere, verdict nowhere; Status reports cert provisioning, not app health | | 2 | Match System / Real World | 1 | Nine internal terms in one 24-word sentence (ladder.html:3-4); pointer/manifest/bake/adopt/sqid rendered verbatim | | 3 | User Control and Freedom | 3 | Confirm cards, rollback, 30-day trash all strong; restore silently doesn't restore serving | | 4 | Consistency and Standards | 2 | "Instances" = three different numbers on three screens; bake has two names; delete/retire/destroy all used | | 5 | Error Prevention | 3 | Best-in-class confirm/step-up/type-the-name; fast-track styled like a routine button | | 6 | Recognition Rather Than Recall | 1 | CLI commands to transcribe; exec/unit/static undefined; canary defined only in a vanishing empty state | | 7 | Flexibility and Efficiency | 2 | Deep links good; no search/filter/restart; consequences hidden in title= tooltips | | 8 | Aesthetic and Minimalist Design | 1 | Releases page ≈85 controls; Overview 6 tiles/3 destinations; 60-75-word always-on paragraphs | | 9 | Error Recovery | 2 | Two excellent diagnostics beside raw err.Error() passthrough | | 10 | Help and Documentation | 2 | Huge volume of inline help, zero structure; no glossary; footer is a disclaimer, not a docs link | | Total | | 19/40 | Poor — major clarity overhaul needed (craft is high; language misaimed) |
Design Specificity Verdict
LLM assessment: Authored, emphatically — but for the wrong reader. The em-dash discipline enforced in the funcmap, the documented chip grammar (bordered = fact, filled = outage, exactly one filled variant), the JS-off account switcher: a real designer's fingerprint. But every good decision serves epistemic honesty about platform internals — the operator's value, not the app owner's. The console reads as a beautifully documented internal tool.
Deterministic scan: 0 findings across all 23 templates (exit 0, clean). The detector confirms markup hygiene is not the problem; nothing it can measure is wrong. The problem lives entirely in vocabulary and information architecture.
Visual overlays: skipped — no browser automation in this session.
Priority Issues
[P0] No health verdict anywhere, and the column that looks like one is lying. Status on apps.html:9 / app_instances.html:10 renders the provisioning enum (pending|creating|issuing|ready|failed); ready means the cert issued, not the app is up. Route.LastError never surfaces. "healthy" appears only on the unlinked operator fleet page. Fix: computed verdict chip (All good / Needs attention / Not deployed yet / Can't tell right now) on apps list + Overview from data already in overview.go; rename Status→Health with values Running/Asleep/Starting up/Not responding/Setting up/Failed; move provisioning state to Settings›Domains.
[P0] "Supply-chain bake" is the highest-cost term in the product. Appears on 6 templates + confirm.go, explained differently each time, plus a second alias ("flagged production"). Fix: rename globally to safety delay, one canonical line: "Safety delay: on — new versions wait 24h on beta and 72h on live before machines pick them up." Fast-track → "Skip the safety delay", styled as danger.
[P1] "Instances" means three different things. Apps-list column counts kind=="instance" routes; Overview tile and Instances tab count all routes; "Inside the app" is a fourth unrelated concept. Fix: tab → Running; tile → Machines ("3 of 4 responding · 1 asleep"); drop the list column for a "Running at" URL; app-reported table → "Reported by the app itself" behind a details.
[P1] Releases page has no progressive reveal; the metaphor is taught before the answer. Ladder + 4 tables + 2 shell blocks + ~85 controls. Fix: first glance = "Live: d388035 · 2 days" / "Next: e91ac2 on beta — ready in 4h" + one button; All versions (20) behind details with one Promote… button per row (channel chosen in the confirm card); full ladder + channels + canaries + tags behind a "Release pipeline" disclosure.
[P1] Load-bearing content in title= tooltips and muted .small text. The consequence of disabling the safety delay exists only as a tooltip (ladder.html:26); bake state, "Promotions disabled", and the column legend are muted-small. Fix: promote consequences into visible text or the confirm cards; reserve .small for provenance.
[P2] The console tells users to run box-only commands, violating the product's own no-infra rule. appsettings.html:50-57 (operator carlos add + "on the platform's roadmap"), appinstances.html:32 (carlos add) vs app_releases.html:89-92 (carlos instances create) — conflicting instructions for the same job. Fix: one documented path (carlos instances create); never show a command the reader cannot run.
[P2] Raw internal errors rendered into prose. "Promote refused: "+err.Error(), "flag unreadable ({{.Err}})", "following a pointer that does not exist". Fix: plain first sentence + technical detail behind details.
[P3] summarize doesn't normalize empty status → "2 , 1 ready" on the primary screen (server.go:2413-2423 vs overview.go:178-180).
[P3] Footer LLM disclaimer is the last thing read on every destructive-confirm screen. Move to /about.
Persona Red Flags
Jordan (first-timer, the owner's worry): h1 says "Promote it" before Jordan knows what CARLOS does; apps table = 4 jargon columns of 6; empty Overview = six tiles of dashes with the actual next step on another tab; ladder lede = nine terms in one sentence; reads Status: ready as "app is fine" (it means cert issued); "restoring with no routes" undecipherable; the one rescuing sentence ("Stable is what every user of X gets") appears only after committing to a promote.
Alex (power user): no app search/filter; no restart control anywhere in the console though carlos restart shipped (#113); promote = 3 clicks + re-deciding a made decision; absolute timestamps title=-only; no copy button on pre blocks containing shell substitutions; two different "Settings" in the header; fleet pages unlinked from any nav.
Sam (screen reader/keyboard): no thead/scope on ~14 tables (the 11-column fleet table is unnavigable); unlabeled inputs on canary form, 20× per-row promote selects, account delete confirm, team invite, cli_approve code; N× bare "Revoke"/"Remove"/"Restore"/"Roll back" buttons with no row subject; title=-only consequences inaccessible; sparkline SVGs untitled and not aria-hidden; no skip link, no :focus-visible anywhere; metrics/fleet render with no nav at all.
Minor Observations
app_new pattern allows uppercase while help text says lowercase; "canary" defined only in an empty state that vanishes on first use; literal default env chip on every unbound route; Activity promises completeness but excludes CLI actions; From column renders bare IP; N stacked delete forms in trash; two duration formats (~24h vs 3d4h); R/W unexpanded in metrics; pagehead one-sentence pattern applied to only half the screens — applying it uniformly is near-free orientation.
Questions to Consider
- If the ladder diagram were deleted from the console (kept in CLI + specs), what would break? Gates are enforced server-side regardless; a "Live / Next up / one button" strip answers the same question in two lines.
- Is this one console or two? App pages speak operator language while the operator page is the only one saying "healthy" — the vocabularies are exactly backwards.
- The console explains its own uncertainty better than its own state ("authoritative clock is each box's own first-seen record"). Is the honest move to explain the approximation — or to stop showing a number it cannot know and say "Goes live in about 4 hours"?