Skip to content

installer: mirror the brand terminal style system (surface 2) - #363

Merged
LukasWodka merged 2 commits into
developfrom
style/installer-output-system
Jul 21, 2026
Merged

installer: mirror the brand terminal style system (surface 2)#363
LukasWodka merged 2 commits into
developfrom
style/installer-output-system

Conversation

@LukasWodka

@LukasWodkaLukasWodka commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Surface 2 of 2 — ports the Go CLI's brand terminal style system (tracebloc/cli#367) to the bash installer + the Windows ps1, so every tracebloc surface reads identically. Organising idea from the homepage gradient: cyan orients (structure), lime moves (action). Design spec + real render: the style artifact shared in the thread.

common.sh — a colour engine mirroring internal/ui

  • Decides mode {none,16,true} + bg {dark,light}once at source time — before setup_log_file redirects stdout through tee, so -t 1 sees the real terminal:
    • NO_COLOR / non-TTY / TERM=dumb / TB_PLAIN=1 → none; COLORTERM=truecolor|24bit → 24-bit; else ANSI-16. COLORFGBG picks the background.
    • This also adds the colour-off gating the installer never had (it used to emit escapes unconditionally).
  • A tone table (_sgr) maps each role to its exact 24-bit hex on dark and light + the nearest ANSI-16 fallback — the same values the Go engine emits (#01a5cc, #91e947, #a7ed6c, …).
  • Re-points the helpers: success ✔ → lime, warn ⚠ → amber, error ✖ → bold red, step / step_header → cyan headings, labels → dim. Legacy CYAN/GREEN/… kept as tone aliases so untouched call sites stay on-brand and honour the off switch.

summary.sh — the connected / starting / error screens

Cyan headings · lime commands (tracebloc data ingest) · cyan-underline links · soft-lime descriptions · lime online (replacing the 🟢 emoji) · dim label : value rows.

install-k8s.ps1 — Windows parity (16-colour tier)

Write-Host can't do truecolor, so Windows gets the ANSI-16 tier of the same roles: summary commands Cyan → Green, the "next" heading → Cyan, metadata labels dim with plain values. Text unchanged → Pester assertions unaffected.

Test plan

  • scripts/manifest.sha256 regenerated (R8 supply-chain gate) for the 3 changed installer files.
  • Full bats suite green except the two pre-existing local failures (validate_config / _extract_yaml_value, unrelated).
  • bash -n + shellcheck clean on the changed libs. The tone escapes were verified to match cli#367 byte-for-byte (38;2;1;165;204 cyan, 38;2;145;233;71 lime). No bats test asserts on colour codes, so the new gating is safe.

🤖 Generated with Claude Code


Note

Low Risk
Terminal presentation and checksum manifest only—no install logic, credentials, or cluster behavior changes.

Overview
Ports the tracebloc CLI brand terminal palette (cyan = structure, lime = action) into the bash installer and aligns the Windows summary screen with the same roles.

common.sh replaces flat RED/GREEN/CYAN with a small color engine: picks none / 16-color / truecolor from NO_COLOR, TTY, COLORTERM, and light/dark background; exposes semantic TB_* tones matching internal/ui; wires success/warn/error/step/step_header to those tokens; keeps legacy color names as aliases so existing call sites stay on-brand and now respect color-off (previously escapes always printed).

summary.sh restyles the post-install screens: cyan headings, lime commands, underlined cyan links, dim labels, lime instead of 🟢, and branded error/warn lines.

install-k8s.ps1 applies the ANSI-16 tier of the same mapping (DarkGray labels, Cyan “what’s next”, Green runnable commands) without changing copy.

manifest.sha256 updated for the three touched installer scripts.

Reviewed by Cursor Bugbot for commit 530d359. Bugbot is set up for automated code reviews on this repo. Configure here.

Ports the Go CLI's style system (cli #367) to the bash installer + the
Windows ps1, so both surfaces read identically. Same organising idea from
the homepage gradient: cyan orients (structure), lime moves (action).
common.sh — a colour engine mirroring internal/ui:
- Decides mode {none,16,true} + background {dark,light} ONCE at source time
(before setup_log_file redirects stdout through tee, so `-t 1` sees the real
terminal): NO_COLOR / non-TTY / TERM=dumb / TB_PLAIN=1 → none;
COLORTERM=truecolor|24bit → 24-bit; else ANSI-16. COLORFGBG picks the bg.
This ALSO adds the colour-off gating the installer never had.
- A tone table (_sgr) maps each role to its exact 24-bit hex on dark AND light
(primary/secondary ramps) + the nearest ANSI-16 fallback — the same values
the Go engine emits (cyan #01a5cc, lime #91e947, soft lime #a7ed6c, …).
- Re-points the helpers: success ✔ → lime, warn ⚠ → amber, error ✖ → bold red,
step / step_header → cyan headings, labels → dim. Legacy CYAN/GREEN/… kept as
tone aliases so untouched call sites stay on-brand and honour the off switch.
summary.sh — the connected / starting / error screens: cyan headings, lime
commands (`tracebloc data ingest`), cyan-underline links, soft-lime descriptions,
lime ● online (replacing the 🟢 emoji), dim label:value rows.
install-k8s.ps1 — Windows parity at the 16-colour tier (Write-Host can't do
truecolor): summary commands Cyan → Green, the "next" heading → Cyan, metadata
labels dim with plain values. Text unchanged (Pester assertions unaffected).
scripts/manifest.sha256 regenerated (R8 supply-chain gate) for the three
changed installer files.
Tests: full bats suite green except the two pre-existing local failures
(validate_config / _extract_yaml_value); bash -n + shellcheck clean on the
changed libs. No bats test asserts on colour codes, so the new gating is safe.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

@BugBot run

Comment threadscripts/lib/summary.sh
The style retheme replaced the 🟢 emoji with a lime ● (design decision: no
emoji). summary.bats:68 still asserted *"live 🟢"*, which passed on the local
bash/bats but failed in CI (multibyte-glob matching differs across versions —
same reason the validate_config / _extract_yaml_value baseline failures invert
locally vs CI). Assert the ASCII heading "Your secure environment is live"
instead — robust across locale, colour on/off, and bash version.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka
LukasWodka merged commit c7c5ea6 into developJul 21, 2026
30 checks passed
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

@BugBot run

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 881afb0. Configure here.

LukasWodka added a commit that referenced this pull request Jul 21, 2026
* Enforce the terminal style system + terminology (installer)
Guards the style system (STYLE.md, shipped in #363) against regressions from
other contributors, layered strongest-first:
- scripts/check-style.sh — a CI gate (blocking, in the "Static analysis" job)
with three MECHANICAL checks: no hardcoded brand colour outside the tone
engine (common.sh), no status/traffic-light emoji, and no "workspace" in
user-facing text (→ "secure environment"). Internal identifiers (the DNS-1123
sanitisers), comments, and scripts/tests/ are exempt; a line can opt out with
a trailing `# style-guard: allow`. Semantic calls (role misuse, softer wording)
stay with review — a grep can't police those.
- STYLE.md — the reference: cyan=structure / lime=action, the role→tone table,
where the engine lives, and the terminology glossary (SoT = docs TERMINOLOGY.md).
- CODEOWNERS — the guard + STYLE.md are code-owned, so the rules themselves
can't be quietly weakened. (The engine files under scripts/lib/ + the ps1 are
already owned via the R8 trust root.)
- PR template — a checklist line pointing at STYLE.md + the guard.
Cleaned the two pre-existing violations the guard surfaced so it ships blocking
with develop clean:
- common.sh --help: "Namespace / workspace label" → "Secure-environment name".
- install-k8s.ps1 summary: "Workspace :" → "Environment :" (aligns with the bash
summary; re-padded the Version/Mode labels to match). No Pester assertion keys
on the label text.
Manifest regenerated (R8) for the common.sh + ps1 edits. Guard passes; bash -n +
shellcheck clean; the 3 local bats failures are pre-existing on develop
(confirmed with the change stashed), unrelated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Address Bugbot: make the brand-colour check case-insensitive
The hex scan matched only lowercase (01a5cc), so an uppercase #01A5CC would
slip past. Add -i to check 1. (Same fix applied to the cli guard.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Address Bugbot: guard fails closed, anchored comment filter, shellcheck the guard
Three findings on #364.
1. Fails open on errors (Medium). scan swallowed grep stderr and read empty as
clean, so a scan failure (bad regex/flags, missing tree) passed the blocking
gate silently. Now: a missing scripts/ tree exits 2 up front, and grep exit
≥2 sets guard_error → the guard exits 2 (fail closed), never a silent pass.
2. Comment filter matched mid-line (Low). ':[0-9]+: *#' could exempt a
user-facing string that merely embeds that shape (e.g. a URL port + fragment).
Anchored to the file:line: prefix: '^[^:]+:[0-9]+:[[:space:]]*#'.
3. Guard omitted from ShellCheck (Low). Added scripts/check-style.sh to the
Static-analysis ShellCheck step so regressions in the guard itself are caught.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Address Bugbot: make fail-closed actually reachable (parent-shell scan)
Follow-up to the previous fail-closed attempt, which didn't work: guard_error
was set inside scan, but scan was invoked via $(scan …) — a command-substitution
subshell — so the flag never reached the parent and the `exit 2` path was
unreachable (Bugbot: "fail-closed path never triggers"). A grep error could
still print ok / exit 0.
Fix: call scan in the PARENT shell (scan …, not $(scan …)); it now sets a global
`hits` plus guard_error directly, and the per-check display filtering moves to
the report() argument. Verified: a forced grep error (invalid regex) now
propagates guard_error and exits 2.
(The other two re-flags — guard omitted from ShellCheck, and the make-ci gap on
the cli side — were already fixed in earlier commits; confirmed present.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* check-style: surface grep errors instead of suppressing (still fail-closed)
Drop 2>/dev/null on the scan grep so a real internal error prints to stderr;
rc>=2 already turns it into a fail-closed exit 2 (verified). Also removes the
pattern that kept tripping Bugbot's (now-inaccurate) 'fails open' heuristic —
the rc check on the very next line is the actual fail-closed path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka
LukasWodka deleted the style/installer-output-system branch August 14, 2026 13:53
shujaatTracebloc added a commit that referenced this pull request Aug 24, 2026
…1) (#804)
Called by nobody. The topology arithmetic lands first, dark and tested, so the
flip that changes the default cluster shape stays a separate, revertable PR --
the shape that worked on backend#2220.
WHY. Uncapped k3d node containers (`NanoCpus=0 CpuQuota=0 Memory=0`) each report
the WHOLE Docker VM, so the DEFAULT topology -- common.sh:821-822 ships
SERVERS=1 AGENTS=1 -- tells Kubernetes the machine is twice its size. Measured
on k3d v5.9.0 / k3s v1.35.5 / Docker 29.5.2: a 7.75 GiB VM presented as
15.50 GiB, byte-exactly 2.000x. And it is reachable through THIS file: on that
cluster _machine_training_resources returned cpu=9,memory=4Gi, and two pods at
that envelope both went Running -- 18 cpu / 8 GiB admitted on 10 cpu / 7.75 GiB.
WHAT. `_honest_topology` (bash) and `Get-HonestTopology` (ps1): the VM's real
size in, an honest node count and per-node memory cap out. Both return the SAME
STRING so the twins are compared byte-for-byte by the same fixture rows, and
both treat an unreadable VM as NOTHING/$null -- "I cannot answer", which a
caller must not read as one node, because collapsing a cluster on a failed probe
is worse than leaving the topology alone.
Two asymmetries decide the shape, both measured, neither assumed:
MEMORY IS CAPPABLE, CREATE-TIME ONLY. k3d caps a node by bind-mounting a
SYNTHETIC /proc/meminfo into the container (a "fakeowner" mount), not via the
cgroup -- kubelet never reads the cgroup for capacity. `docker update
--memory=4g` on a running node set memory.max and left /proc/meminfo at the
VM's 8126672 kB, so capacity did not budge even across a restart. An existing
cluster cannot be capped in place.
CPU IS NOT CAPPABLE AT ALL. k3d 5.9.0 has no CPU flag, and neither `--cpus`
(CFS quota, cpu.max 200000 100000) nor `--cpuset-cpus` (nproc 5) moves
capacity.cpu off 10, because cadvisor counts /sys/devices/system/cpu/present
and /proc/cpuinfo and no cgroup namespaces either. Verified against a forced
node de-register/re-register.
So memory is made honest by capping and cpu only by FEWER NODE CONTAINERS, which
is why the return value carries cpu_honest rather than implying the cap closed
it. Same conclusion the GPU path already reached for --gpus=all.
CONTRACT v2. New embedded constants: VM_RESERVE_MEM_BYTES (1 GiB, measured --
serverlb 39.27 MiB + tools 14.69 MiB on a live cluster, plus dockerd and guest
page cache outside any container) and NODE_MIN_{CPU_MILLI,MEM_BYTES}.
The node minimum is overhead + floor = 5 GiB / 2 cores -- DERIVED, but recorded
in the contract because neither installer can do arithmetic on JSON. A recorded
derivation nobody checks is just a fourth constant waiting to rot, so
gen-envelope-embed.sh now re-checks the derivation and REFUSES a contract where
it has drifted, before writing anything. That matters here specifically: an
embed generated from a half-vendored contract would be wrong in two installers
at once, and a generator that rewrites one file then dies is Bugbot#766.
That guard also caught its own blast radius: "adopting a changed contract"
bumped overhead and left per_node_minimum behind, so the generator refused and
the test failed. Correct refusal -- upstream's generator recomputes the derived
value, so a vendored contract always arrives consistent. The test now simulates
that, and the half-vendored case became a test of its own.
Also fixes a comment I got wrong in backend#2220, in BOTH twins
(install-client-helm.sh, install-k8s.ps1): "installer-provisioned clusters are
single-node k3d, so this is a no-op in the field". They are not -- the default
is two nodes. The anchor tie-break is a field no-op only because both k3d nodes
report IDENTICAL figures, and they do that because each reports the whole VM,
which is this bug.
TESTS. 11 topology vectors generated from the contract, replayed through BOTH
twins (5 bats + 7 Pester cases), including the sum(nodes x cap) <= usable VM
invariant as a property over 12 VM sizes x 5 requested counts, and the unreadable
and requested<1 boundaries. Verified out of band that both languages produce
byte-identical output on all 11 rows.
installer_parity.json deliberately unchanged: this function is pure arithmetic
with no external commands and no branching on cluster state, so the contract
vectors are its parity mechanism. That fixture exists for CONTROL FLOW -- every
backend#2220 divergence lived in a state that was not a clean measurement -- and
the eventual CALLER belongs there, because it will shell out to docker and
branch. Reasoning recorded in both suites.
Local: 461 bats + 887 Pester pass (assess.bats:40 fails on clean develop too,
verified by stashing); shellcheck severity=error and -S warning -x clean;
PSScriptAnalyzer errors identical to develop; gen-envelope-embed/gen-manifest/
gen-installer-parity --check all clean. No chart files touched, so no version
bump.
NOT READY: scripts/.client-runtime-ref still pins the client-runtime FEATURE
BRANCH commit, which the squash-merge of #363 will orphan. The file carries a
DO-NOT-MERGE block with the re-pin recipe.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@LukasWodka@saadqbal@aptracebloc