Skip to content

serve a bare Terraform directory: generate the reader config in a scratch dir, so an estate needs no file of its own (#378) #384

Description

@lex00

Child of #378. The last behold-side piece of the Terraform lane, and the one #378 chose but did not build.

What does not work

#383 made a Terraform estate legible — cards titled by resource type, roots as boxes, the zoom decision — but only for a project that already declares the lexicon. Point behold at a real Terraform directory and it refuses:

$ behold doctor access/envs/prod
  fail  project   no chant.config.ts here, and no estate members declared
        fix       Point behold at a chant project, or run `behold demo`

That is the correct answer today and the wrong answer for the lane. Every estate this exists to draw is a directory of .tf files and nothing else.

What #378 decided

When INTENTIUS/waterpark#88 was withdrawn — the estate is more useful untouched — the epic picked the other path and recorded it:

behold generates the reader config in a scratch directory at serve time, so an estate is browsable without a file in the estate's own repo.

So: behold serve <terraform-dir> writes a chant.config.ts naming the roots into a scratch directory, points chant at it, and renders what comes back. Nothing is written in the estate.

The three things that decide the shape

1. Where the lexicon comes from. chant resolves @intentius/chant-lexicon-terraform from the config file's own location, so the scratch project needs it resolvable. behold does not depend on it today (@intentius/chant, @intentius/chant-k8s-client, @intentius/pinhole, hono). Options, and this is the real decision:

  • A dependency. Simplest to use, and it puts an HCL parser (@cdktf/hcl2json, a ~1.8 MB wasm blob, itself an optional peer of the lexicon) in behold's install for every user, most of whom serve chant projects. Weigh against carve-lens.ts's posture — behold has kept Terraform tooling out of its tree on purpose.
  • An optional dependency, probed like a demo's requires and refused politely with an install line when absent. Matches how behold demo already gates on binaries it does not ship, and how the choudoufu member refuses when its binary is missing.
  • Resolve from the estate, if the repo happens to have it. Zero install for the repos that already use chant, nothing for the ones that do not — which is every repo this feature is for.

My read is the optional dependency, but it is a real trade and the issue exists to settle it.

2. Which directories are roots. chant carve advise --from <parent> over the parent of six root modules returns zero resources — the parse is one root at a time, and the lexicon's config names roots explicitly. So behold has to discover them. A root is a directory holding .tf files; a modules/ directory that is only ever called by source = "../../modules/x" is not a root of its own, and including it would draw the module's internals twice. Measured on a real estate: six directories hold .tf, one of them (modules/persona) is a shared module called by three others.

Cheap discovery that is probably right: directories with at least one .tf declaring a terraform {} or provider block, which is what a root has and a called module does not. Behold parses no HCL, so this is the same regex-depth probe the choudoufu member kind uses for its live { block.

3. Member kind, or not. #378 says there is no terraform member kind, and means it about reading: behold parses no HCL and the render goes through chant. A kind whose read shells chant graph against a generated config does not contradict that — it is a scaffold, not a second reader — and it would inherit the probe, the cache stamp, the doctor line and the estate composition for free (#368). The alternative is a serve-time special case outside the member system.

Worth deciding deliberately rather than by whichever is quicker, because the answer decides whether a Terraform root can sit in a composed estate beside a chant project and a choudoufu estate.

Definition of done

  • behold serve <dir> on a directory of .tf files renders it, with no file written anywhere under <dir>.
  • The scratch directory follows the write boundary: behold's only in-project write stays .behold/layout.json (AGENTS.md, "Invariant"). A generated config lives outside the estate, is behold's own, and is cleaned up or reused deterministically.
  • Root discovery reports what it found and what it skipped, so a missing root is visible rather than silently absent.
  • A behold doctor line for the lexicon: present, absent with an install line, or resolved from the estate.
  • The refusal when the lexicon is unavailable names the one command that fixes it, the way chant's own carve refusal does ("Terraform carve-out needs the HCL parser, which is not installed. Install it once: npm install -D @cdktf/hcl2json").
  • AGENTS.md's "Rendering a Terraform estate" section gains the serve path beside the passes.

Acceptance

behold serve on a copy of a real multi-root estate draws one box per root with the cards #383 already produces, and the estate directory is byte-identical afterwards.

Note

Edges arrive separately: chant#2265 is in progress, and when it lands this same path renders them with no change here.

🤖 Claude Code — https://claude.ai/code/session_0124XP1LJTZxgJRy7Dh1UfXt

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestterraformthe plain-Terraform estate lane (#230's remainder)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions