feat(terraform): render an estate chant read — types as kinds, roots as boxes, and a decision about what is a card (#379, #380, #382) - #383
Merged
Conversation
…as boxes, and a decision about what is a card (#379, #380, #382) A Terraform estate reaches behold through chant's terraform lexicon, which reads the HCL an estate already has and emits one entity per block. So it is already a chant project as far as behold is concerned, and `behold serve` already drew it — 247 nodes on the estate this was built against, every card titled `Terraform::Resource`, all in one box, four fifths of them variables and outputs and settings. Three passes, guarded on the IR carrying terraform entities so every other estate gets the identical object back: normalizeTerraformNodes moves the resource type out of attrs.address into kind, keeping the block class in attrs.block. That is the shape a carve node already has, so one presentation pack now serves both producers — dispatching on score for a carve node and on block for a read one — and pinhole's keyword icon heuristic resolves aws_s3_bucket and friends for both. A data source also gains a `reads` row naming what it filters on, with the interpolation left unresolved, which is #381's recommendation after that spike measured the cross-root read and refused to infer an edge from it. groupTerraformByRoot makes the roots the boxes, from attrs.root or the id prefix. It detects an IR already grouped upstream and leaves it alone, so chant#2266 retires it rather than fighting it. filterTerraformCards is #382's decision, written down here and in AGENTS.md: resources, data sources and modules are the estate and are cards by default; outputs and variables are its interface and appear at detail 3, where chant also starts carrying parsed bodies; terraform, provider and locals blocks are settings and are never cards. Nothing is dropped silently — the note says what is not drawn and where to see it, the way edgelessNote says why a view has no edges. A logical lens beside the choudoufu one: one box per root, and only the edges the graph states. No edges are invented anywhere; a stock estate has none until chant#2265. Wired into both /api/graph branches and both /api/overlay branches, plus the two logical paths, each running the passes exactly once. Verified against two real IR documents from chant's published lexicon 0.59.0 — behold's own example-carve/legacy-tf as one root, and water park's baseline and satellite as two, the pair carrying the cross-root read. Served for real: 4 cards at detail 2, 24 at detail 3, root boxes on both, the note naming the 26 blocks it left out. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0124XP1LJTZxgJRy7Dh1UfXt
This was referenced Sep 8, 2026
Closed
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 #379, #380, #382. Child work of #378; #381 was answered and closed first, and its finding shaped this.
The starting point
behold servealready rendered a Terraform estate, because chant's terraform lexicon reads the estate's own HCL and a project using it is a chant project. It was just unreadable: 247 nodes, every card titledTerraform::Resourcewith a generic glyph, one box, and 108 variables plus 53 outputs plus 14terraformblocks competing with 43 resources for the canvas.Three passes
normalizeTerraformNodesattrs.addressintokind, block class intoattrs.block; a data source gainsreadsgroupTerraformByRootfilterTerraformCardsAll guarded on
hasTerraformEntities, so a chant, k8s or choudoufu estate gets the identical object back — asserted.#382's decision, written into AGENTS.md so the next lens can find it:
resource,data,moduleoutput,variableterraform,provider,localsOne pack, two producers.
render.tsregistered theterraformlexicon forcarveCardFields, and a read block has no score. It now dispatches: carve node by score, read block by block. Neither producer had to change lexicon, which matters — a carve node and a read block are both Terraform and should not disagree about what they are.No invented edges. A stock estate has none until chant#2265. #381 measured the one relationship that looked derivable and refused it; a data source states what it reads as a row (
reads name ${var.boundary_name}), interpolation unresolved.Verified against real output
Two recorded
chant graph --format irdocuments from the published lexicon 0.59.0, provenance in the test files: behold's ownexample-carve/legacy-tfas one root (29 nodes, all seven entity kinds), and water park'sbaselinepluswaterpark-runneras two roots at detail 3 (30 nodes, the cross-root read). Served for real:Tests
just checkgreen: 88 files, 1718 tests.terraform-lens.test.ts(18) covers each pass, idempotence, an unknown future entity kind left untouched, a non-Terraform estate byte-identical, the elision arithmetic and its wording, the pack's dispatch, and the lens.terraform-route.test.ts(4) drives/api/graphat both details and through the logical lens with the chant shell-out mocked, and asserts a k8s estate is unaffected.🤖 Generated with Claude Code
https://claude.ai/code/session_0124XP1LJTZxgJRy7Dh1UfXt