Skip to content

fix(ui): the re-audit — the logical zoom survives scale, fit clears the panel, composites keeps the Terraform boxes, and four smaller things (#396) - #409

Merged
lex00 merged 9 commits into
mainfrom
feat/reaudit-396
Sep 10, 2026
Merged

fix(ui): the re-audit — the logical zoom survives scale, fit clears the panel, composites keeps the Terraform boxes, and four smaller things (#396)#409
lex00 merged 9 commits into
mainfrom
feat/reaudit-396

Conversation

@lex00

@lex00 lex00 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Closes #396. The re-audit's seven findings, measured on the same five estates.

finding before after
1 · logical zoom still the strip terralith-4 116,588 × 707 (165:1); workbench 29:1; waterpark 24:1 8,099 × 5,138 (1.58:1); 0.85:1; 0.66:1 — the entity graph's passes (row wrap, component packing, module bands, badges) now serve both renderers, and a choudoufu estate keeps its boxes in the logical projection
2 · fit hides the first column leftmost card under the panel at x < 285 fit targets the pane minus the panel's footprint, re-fits when the pane moves, never pulls back a pan or zoom
3 · composites undoes the Terraform picture waterpark at detail 1: one box named access, full ids five root boxes, faces intact — the lexicon dropped address and root below detail 2, so the kind's read never goes below 2; a zoom that changes nothing for a member kind says composites: same as resources on a choudoufu estate in the strip
4 · collision rule across the whole graph the workbench's 42 titles all member/address per box: 42 titles, 21 distinct, all four boxes read the address alone; ids stay unique
5 · ownership: foreign on an unowned card rendered through the estate's vocabulary, gated on vocabulary.of, so chant cards are untouched
6 · collapse re-runs the live read terralith-4 4,574 ms, workbench 5,793 ms per toggle 8 ms / 22 ms — one cached overlay document per member, keyed like #404's plan cache; the retired "no edges at this detail" note is gone from the collapsed strip
7 · small "a composed estate" on a lone estate; 7px box titles at fit; the adopt line on three rows "this estate"; a screen-pixel floor on box titles and badges; one clipped row with the full text on both tooltips

The workbench e2e now asserts the aspect ratio under 4:1 at every zoom the estate offers, on terralith-4 and waterpark. Both gates green at every merge; no console errors on any estate.

Built by two Opus agents in worktrees (server side and SPA side), merged clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq

lex00 and others added 9 commits September 10, 2026 16:53
…he address alone in all four (#396)

"A label two cards would share is used for neither" was counted across the
whole graph, and a split estate is one address declared in several boxes: all
42 of the live-mv workbench's cards kept `monolith/aws_cloudwatch_log_group.
team_a_0`, repeating the member name the box beside them already spells out.

The box IS the disambiguation, so the count is per box. Two cards in different
boxes may now share a label; only two in the same box are ambiguous and keep
their full ids.

The whole-graph rule was really protecting the restore — pinhole titles a card
from `node.id`, so the display id is the painted title, and two cards with one
label would be one node to dagre and one `data-node-id` to the SPA. Those two
demands are separable: the title repeats, the id does not. A repeated label is
minted with n U+200B ZERO WIDTH SPACE, which pinhole paints as nothing and
`restore` strips off the finished SVG beside the real ids — so the SVG that
leaves carries neither the minted id nor the character, and `data-node-id` is
unique and real. Measured on the workbench: 42 titles, 21 distinct, 42 distinct
node ids, no zero-width anywhere in the answer; the picture went 2038 x 2160 to
1844 x 2160 on the shorter titles alone.

`cardFaces` also takes `prefixes` now, for a caller whose drawn boxes are not
the groups whose names a title may drop — the logical lens, in the commit after
this one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
…h — terralith-4 165:1 → 1.6:1 (#396)

`renderGraph` grew the passes one at a time — the card faces, the edgeless
wrap, the component pack, the module sub-boxes, the badges — and
`renderArchitecture`, which every `?logical=1` branch renders through, got the
wrap alone. So the zoom came back as the strip those passes exist to prevent.

The two renderers differ in exactly one thing: which layout call sits in the
middle (`layoutIr` over flat groups, `layoutArchitecture` over nested
containers). So the passes are extracted rather than copied — `estateFace` is
everything before the layout call and `packEstateBoxes` everything after — and
both renderers run them.

The after-passes reposition a box's cards and resize the box around them, and a
box drawn INSIDE another box would then be moving inside a rect nothing
re-measured. So they run on a FLAT projection only: one level of containers,
which is what the choudoufu lens (a box per estate) and the Terraform lens (a
box per root) produce. The k8s topology's region ⊃ VPC ⊃ subnet nesting renders
exactly as it did.

The boxes are kept on a choudoufu estate: the logical picture now carries the
estate box AND the module sub-boxes, where before it was the resources graph
with the boxes taken off. The projection hands the member groups over as
`cardPrefixes`, so a card reads its address here exactly as it does at
`resources` — the lens draws containers of its own naming while the ids stay
`<member>/<address>`, so the two are different questions.

Measured, `/api/graph` and `/api/overlay` both:

  terralith-4       116588 x 707   164.91:1  →  8099 x 5138   1.58:1
  waterpark          20157 x 840    24.00:1  →  2917 x 4402   0.66:1
  choudoufu-workbench 14034 x 480   29.24:1  →  1844 x 2160   0.85:1
  choudoufu-workbench 14926 x 796   18.75:1  →  1844 x 2192   0.84:1  (overlay)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
The View panel is `position: fixed` and floats over the graph pane, so
"fit" — which fitted the pane — parked the leftmost column of cards
underneath it. On terralith-4 that was a whole column at x < 285; on
waterpark, half the `github` box. The cards were reachable only by
collapsing the panel or panning, at boot and after every re-render.

The fit's target is now the pane minus whatever the panel covers of it,
read at fit time rather than remembered: the panel drags, docks to either
edge and collapses, and a fit computed against where it used to be is the
same bug with a different offset. Collapsed or hidden, it has no
footprint and the fit is the whole pane, byte for byte what it was.

`setupGraphViewBox` applies it too, not just the ⤢ fit button — a zoom
change, an env change and the SSE re-pull all go through there and none
of them through `fitGraph`, which is why the column used to slide back
under the panel at each one. And because the fit now depends on the pane,
a graph sitting at fit is re-fitted when the pane moves under it (the
statusbar's note wrapping to a second line is enough); a pan or a zoom
takes ownership of the viewBox and is never pulled back.

Pinch and ⌘-scroll are untouched.

Measured in the smoke on the `nonChant` stub's 8000x3000 canvas: the
leftmost card's rectangle sits right of the panel's right edge at boot
and after a zoom change, moves left when the panel collapses, and moves
back when it re-opens.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
…ary (#396)

An unowned choudoufu card read `status: unowned` and, two rows below it,
`ownership: foreign` — chant's word for choudoufu's state, on the one row
#393 item 8 did not reach when it taught the legend, the counts, the
Model tab and the status row to speak the estate's own words.

`ownership` is a field two readers write. chant's answers it from the
resource's own marker (`owned` / `foreign`, its contract for that field);
src/choudoufu-live.ts has no marker to read and writes the same two words
for `bound[]` and `unowned[]`. Rendering it through `meta.vocabulary` —
value, to the colour that value means, to that colour's word — is the fix
the other three rows already had.

Rendered rather than renamed at the source, and gated on `vocabulary.of`.
Naming it in choudoufu-live would have to write choudoufu's words into a
field chant's own type says holds chant's, and a mapping applied to every
estate would turn a chant card's `owned` into `managed` to fix a
choudoufu card's `foreign`. Gated, a chant estate is untouched, and so is
a mixed one, which keeps chant's words on the legend by design and says
so in its tooltip: "bound reads as managed and unowned as foreign on the
choudoufu members" is exactly the sentence that holds those rows
together, and it is already there.

The other places `foreign` and `managed` could reach a choudoufu card
were already right: the statusbar counts, the Model tab's legend and its
NEEDS ATTENTION rows all read `statusLabels()`, the collapsed box badge
has choudoufu's words per lexicon (src/collapse-lens.ts), and
`/api/diff`'s `healthDetail` is choudoufu's own sentence. The smoke now
holds the whole card and the whole Model tab to it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
#396)

Item 7's three small things.

(a) "the components lens doesn't apply to a composed estate yet" is a
sentence about several members, and most of what behold serves this way
is one — `behold demo terralith-4-adopt` is a single directory, and so is
every estate the audit ran on but the workbench. An estate of one now
reads "doesn't apply to this estate yet — showing its entity graph".

(b) pinhole draws a group box's title at a flat `font-size="12"` and its
badge at `11`, in viewBox units. On terralith-4, whose box is 8000 units
wide, that is about 7px at fit: the label naming the member 301 cards
belong to and the one counting them are the two things on the canvas a
person cannot read at the zoom they arrive at. Both now have a floor in
SCREEN pixels — the size in units is raised by the inverse of the current
scale until the label renders at its natural size, never below it, so
anything already legible is untouched and a zoomed-in graph behaves
exactly as before. pinhole's anchor is kept, so a title stays welded to
its box's top-left corner and a badge to its top-right through every pan
and zoom; only the type grows. The floor yields to the box: a label may
not outgrow a fraction of the box's height nor its width, so a module
sub-box too small to hold readable type at this zoom stays small rather
than writing across its neighbours.

(c) The adopt line wrapped onto three lines in the 260px pane and pushed
the rest of the card off the bottom, for a string nobody reads off the
screen — the copy button beside it is how it gets used. One line now,
ellipsised, with the whole of it on the row's tooltip, on the button's,
and in the clipboard.

Verified on `behold demo terralith-4-adopt` at 1600x1000: box title 15px
and badge 13px at fit (both 7px before), the statusbar's components note
naming "this estate", and the adopt row one line high with the full tags
on both tooltips.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
…nd a zoom that changes nothing says so (#396)

waterpark at detail 1 collapsed its five root boxes into one named `access`
and re-titled all 73 cards with their whole composed id — the resources
picture undone by the zoom next to it.

The cause is not the lens: it is the tier the member is read at. chant's
terraform lexicon thins its attrs by detail and `address` and `root` go first,
and those two are not attributes of a Terraform block. They are its NAME and
the root it is declared in — what `normalizeTerraformNodes` titles a card from
and what `groupTerraformByRoot` boxes by. With both gone the root fallback took
the id's first segment (the member) and the title fallback took the whole id.
So the terraform member's read is pinned at detail 2 or better, in the kind's
own `read` where AGENTS.md says a kind's differences live. What a zoom DRAWS is
untouched: `filterTerraformCards` still runs at the caller's own detail, so 3
still adds the outputs and variables.

  waterpark, /api/graph?detail=1:  4284 x 2782, 1 box, 73 full ids
                              →    2917 x 4382, 5 boxes, the addresses

Which makes composites and resources one picture on a Terraform estate, and
they always were on a choudoufu one — a choudoufu member's read is
`live-check`, which takes no detail at all, so composites AND attributes are
byte-identical to resources there. Three picker rows, one image, nothing saying
why: #131's defect one member kind further out, and it gets #131's answer, a
caption. `unchangedZoomNote` in src/zoom-notes.ts, where every other caption
comes from, one clause on the note the strip already shows:

  composites: same as resources on a choudoufu estate
  attributes: same as resources on a choudoufu estate
  composites: same as resources on a Terraform estate

Silent on an estate of several kinds — composites genuinely changes the chant
member's half of that canvas, and the note is read next to all of it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
…le 4574ms → 8ms (#396)

`?collapse=1` is a render decision over a document behold already holds: which
member boxes are drawn shut. The SPA sends it as a query param, so "Collapse
large boxes" and "Expand all" each re-fetched `/api/overlay`, and each fetch
re-ran `live-ls` and `live-plan` per member — four seconds on terralith-4, with
the graph frozen behind "loading resources · live…", to draw the same 301 cards
as one card.

So a member's live read is cached, exactly the way #404 caches its plan and
under the same key: the member's source stamp plus the read's own options,
which is where the env lives. src/member-ir.ts's rule 1 refuses to cache a live
read; src/overlay-ir.ts is the stated exception to it and its header names,
once, everything that drops an entry — a re-observe that was asked for (`POST
/api/refresh`, and `?plan=1`, because "Re-check live with plan" re-checks both
halves of the answer), a write behold itself ran (the capture that ends an Op
run), and the member's source moving.

The collapsed view's caption came back with #393's retired "no edges at this
detail — sourceRef/dependsOn …": the notes were written about the COLLAPSED
graph, which is one node and no edges, so an estate with 266 real references
was reported edgeless at a tier that had nothing to do with it. A shut box
hides what the estate references rather than changing it, so both routes write
their notes about the expanded IR and add the collapse clause to them.

Measured, `/api/overlay` at detail 2, read then collapse then expand:

  terralith-4          4574ms  →     8ms  (expand 103ms)
  choudoufu-workbench  5793ms  →    22ms  (expand  21ms)

and `?plan=1` still costs its 7.9s, which is what asking to re-check means.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
…only at resources (#396)

`e2e/workbench-e2e.sh` measured terralith-4 at `?detail=2` and nothing else,
which is why the strip could come back at `logical` — 165:1 on terralith-4,
24:1 on waterpark — with the suite green. `shapes` now reads all five zooms the
SPA offers (logical, composites, resources, attributes, collapse=1) on both
`/api/graph` and, for an estate that declares an env, `/api/overlay`, with the
params web/app.js sends for each, and holds every one under 4:1. It prints the
dimensions and the ratio per zoom, so a picture that is merely getting worse is
readable in the log before it fails.

waterpark gets the same, plus the two facts #396 finding 3 turned on: its
composites zoom keeps more than one root box, and the strip says the zoom
changed nothing for this kind of member.

Measured against the three estates, this build:

  terralith-4          graph/overlay, five zooms   1.45:1 … 1.74:1
  choudoufu-workbench  graph/overlay, five zooms   0.84:1 … 0.85:1
  waterpark            graph, five zooms           0.66:1 … 0.80:1

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoXg2GDWwXJRPQo76t9Nrq
@lex00
lex00 merged commit e345577 into main Sep 10, 2026
1 check passed
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.

workbench re-audit: the logical zoom is still the strip, fit hides the first column, and five smaller things (#393)

1 participant