Skip to content

split ui.zig into src/ui/* facade (plan #656, source #652) - #658

Merged
btipling merged 8 commits into
mainfrom
plan/split-ui-facade
Aug 18, 2026
Merged

split ui.zig into src/ui/* facade (plan #656, source #652)#658
btipling merged 8 commits into
mainfrom
plan/split-ui-facade

Conversation

@btipling

Copy link
Copy Markdown
Owner

Summary

Split native/harness/src/ui.zig (1628 lines) into a facade + src/ui/* siblings without changing pixels, protocol, or palette.

What moved

Module Contents
ui/state.zig All file-level vars + reset/clear helpers — single owner, no cycles
ui/metrics.zig Named chrome constants (TOUCH_H, STATUS_BAR_H, composer caps, etc.)
ui/scroll.zig isNearBottom, clampScrollToContent, scrollToBottom
ui/kinds.zig kindLabel, kindTextColor, kindFill, lifecycleLabel
ui/chrome.zig chrome*Font, composerIconFont, paintStatusChip, toolRunClipboard
ui/toolrun.zig paintToolRun, isCommandLikeRun, detailUsesMono
ui/thinking.zig paintThinking, thinkingPreview
ui/chip.zig paintLastUserChip
ui/status.zig paintStatusSlots, truncateStatusValue, truncateToWidthPx, utf8CharLen, statusPackMaxWidth
ui/skill.zig paintSkillAttached
ui/composer.zig clearPrompt, submitText

What stays

  • ui.zig = facade: BUILD_ID, onInit, onDeinit, frame()
  • main.zig still @import("ui.zig") — no change
  • build.zig no change — relative @imports resolve automatically
  • All behavior: palette, protocol, id_extra values, caps preserved

Import graph (acyclic)

metrics.zig     → (nothing product-side)
state.zig       → metrics, dvui, ../bridge, ../thinking_collapse
scroll.zig      → metrics, dvui
kinds.zig       → ../palette, dvui, ../bridge
chrome.zig      → metrics, ../palette, dvui, ../rich/toolrun
status.zig      → metrics, ../palette, dvui, ../bridge, ../cwd_slot
skill.zig       → ../palette, dvui, ../rich/mixed_text
composer.zig    → state, ../composer_text, ../bridge
chip.zig        → state, scroll, status, metrics, ../chip_preview, ../palette, dvui, ../bridge, ../rich/mixed_text
thinking.zig    → state, metrics, chrome, ../chip_preview, ../rich/root, ../palette, dvui
toolrun.zig     → state, chrome, metrics, ../rich/root, ../rich/toolrun, ../rich/mixed_text, ../palette, dvui
ui.zig          → all of the above + existing extracts

No cycle: thinking → chrome, toolrun → chrome, chrome does not import either. chip → status, status does not import chip. state imports no paint file.

Living docs

  • docs/feature-divide.md — Zig UI row: facade + ui/*.zig
  • docs/harness-limits.mdtruncateToWidthPxui/status.zig; ScrollInfo → ui/state.zig; paintToolRunui/toolrun.zig
  • docs/agent-stream.md — Thinking paint → ui/thinking.zig
  • AGENTS.md — stream-chrome + tool-run rows updated

Test results

Suite Result
npx tsc --noEmit ✅ clean
npx vitest run 1752 passed, 9 failed (pre-existing — stale Wasm artifact)

Plan

Post-merge

Needs build-harness on the merge commit to compile the Wasm artifact. The test-rich step will also verify the import graph compiles.

dvui.button/labelNoFmt pins one face. Noto has no Arrows block so U+2191
tofus; fontSymbols() on the whole chip tofus Latin. Same split rich text
already uses. Measure the arrow on the symbols face.
Move paint helpers, state, metrics, and chrome out of the 1628-line
ui.zig into 11 sibling files under ui/: state, metrics, scroll, kinds,
chrome, toolrun, thinking, chip, status, skill, composer.

ui.zig is now the public facade: BUILD_ID, onInit, onDeinit, frame.
Import graph is acyclic (no state->paint cycles). No behavior, palette,
protocol, or cap changes. Living docs path pointers updated.

Plan: #656
Source: #652
@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
invincible Ignored Ignored Aug 18, 2026 4:01am

Request Review

@btipling btipling left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Adversarial review — PR #658

Verdict: PASS WITH NOTES
Repo: btipling/invincible
Scope: mainplan/split-ui-facade · 16 files · mechanical ui.zigsrc/ui/* facade split (plan #656)
Lenses run: L1 L3 L4 L5 L6 L8 L9 (skip: L2 — no API/workflows/secrets/bridge lengths; L7 — no host/config seams)
AGENTS.md read: yes · docs/feature-divide.md read: yes

Findings

Sev Lens Finding Break scenario Refutation attempt Confidence
Minor L8 ui/kinds.zig · lifecycleLabel resurrected (plan #656 DoD: do not reintroduce; deleted on main by #653). The extra bridge import exists only to serve this dead helper — plan import graph locked kinds.zig → palette, dvui only. Next agent treats the pub helper as product API and paints it on status-bar line 1, replacing the #651 idle rect_spinner. That is a pixel change this PR claimed not to make. “It’s unused pub, compile-clean, no pixels change today.” True for this HEAD — the trap is the API + the AGENTS.md wording below, not a current paint path. Still a DoD miss. high
Minor L8 AGENTS.md identity row drive-by: spinnerlifecycle. Canvas still paints rect_spinner.paint (ui.zig line 1). docs/feature-divide.md still says spinner. Living-docs plan said path pointers only. Agent “fixes” line 1 to match AGENTS.md (drop spinner, show lifecycleLabel). Same root as the row above. “Wording, not code.” Agents read AGENTS.md first; this PR is what taught them the wrong chrome. high
Nit L8 In-tree pointers still say the moved symbols live in ui.zig: thinking_collapse.zig (“operator set lives in ui.zig” — now ui/state.zig), rich/kinds.zig (tool-run / skill paint), rich/toolrun.zig test comment (paintToolRun), transcript_split.zig (TOUCH_H now ui/metrics.zig), native/harness/README.md tree. Agent opens ui.zig for thinking-open maps or TOUCH_H, misses the owner, edits the facade or duplicates a constant. Listed living docs were updated; these comments were not in the plan’s doc table. Bounded. high

Residual risk

Moved bodies match main 1:1 (qualification + rich_toolrun alias + local scroll/chrome renames only). build-harness on this HEAD is green (test-rich + wasm), so the first-commit missing bridge import in thinking.zig is gone. Inner @src() in moved files still churns widget ids once on the new artifact (status pack, chip label) — expand maps stay integer-keyed and persist. test-rich still does not compile ui.zig / ui/*; the next missing import only fails on the wasm step (already happened once this PR). Nine vitest failures named in the PR body are the stale checked-out wasm, not this diff (no TS). No operator frame smoke of the PR artifact was exercised here.

Merge guidance

  • PASS WITH NOTES: safe to merge from this attack.
  • Before or in a follow-up: delete lifecycleLabel + the bridge import from kinds.zig; revert AGENTS.md lifecyclespinner. Optional: retarget the stale ui.zig comments.

What was not attacked

Live DO runner, prod Gateway, Vercel preview of the PR wasm artifact, pixel-diff / operator smoke of composer hug · chip click · tool-run L0/L2 persist · thinking collapse on this build.

@btipling
btipling merged commit db7f136 into main Aug 18, 2026
3 checks passed
@btipling
btipling deleted the plan/split-ui-facade branch August 18, 2026 04:06
Sign up for free to 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.

1 participant