| new file mode 100644 |
| index 000000000..854e62fc1 |
| --- /dev/null |
| +++ b/.impeccable/critique/2026-08-09T15-36-14Z__internal-console-templates.md |
| @@ -0,0 +1,75 @@ |
| +--- |
| +target: CARLOS console (internal/console/templates) |
| +total_score: 19 |
| +max_score: 40 |
| +na_heuristics: |
| +p0_count: 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.** app_settings.html:50-57 (operator carlos add + "on the platform's roadmap"), app_instances.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 |
| + |
| +1. 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. |
| +2. 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. |
| +3. 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"? |