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
Conversation
…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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #396. The re-audit's seven findings, measured on the same five estates.
access, full idsaddressandrootbelow detail 2, so the kind's read never goes below 2; a zoom that changes nothing for a member kind sayscomposites: same as resources on a choudoufu estatein the stripmember/addressownership: foreignon an unowned cardvocabulary.of, so chant cards are untouchedThe 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