Skip to content

feat: behold demo carve — the recordable carve walkthrough, offline tier (#254) - #272

Merged
lex00 merged 5 commits into
mainfrom
feat/254-carve-walkthrough
Aug 10, 2026
Merged

feat: behold demo carve — the recordable carve walkthrough, offline tier (#254)#272
lex00 merged 5 commits into
mainfrom
feat/254-carve-walkthrough

Conversation

@lex00

@lex00 lex00 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Part 2 of #254 (M1.5 of #230): one command from npm to the peel walkthrough. Builds on #258's carve lens, #262's estate, #264's JSON view, #257's sidecar write boundary and #268's demo catalog.

npx @intentius/behold demo carve     # no Docker, no cloud, no terraform binary

What ships

behold demo carve — a demos.json entry (requires: []) that copies example-carve through #268's shared loader, npm-installs the chant every step will shell (app/), installs @cdktf/hcl2json into the copy's root node_modules (chant lazy-loads the HCL parser with a bare import resolved from its own install upward, so that is where it resolves — not beside the .tf files), and runs chant carve advise --report over the copy. A boot that fails any of that serves the committed carve-report.json and puts the reason on the statusbar, the terminal and the Carve tab. A blank graph is the one outcome that never happens.

The stepper — a Carve tab mounted at runtime (panel.js's new addPanelTab), so no other project grows a dead tab. Six steps on the deploy dial's own track, gated by real data dependencies rather than by wizard etiquette:

step what it does
Advise band legend with the report's own counts, and where the runs will write
Pick a graph click is the pick; inspect shows the arithmetic, the step names the cut
Emit POST /api/carve/emitcarve emit --state --select <addr> + chant lint; renders the emitted source, chant's boundary report (via json-view) and the lint verdict
Bridge POST /api/carve/bridgecarve bridge, never --apply-rewrites; renders the data source, the rewired survivors and the patch
Handoff the runbook's commands with copy buttons, and a paragraph on why it is not a button
Done a ✓ carved → chant marker on the card + the end card

A finished run deliberately does not advance: the result is the step, and skipping past it would hide the thing the run was for. A new pick drops the previous runs — showing one resource's emitted source under another's name is the one way this panel could actively lie.

Honesty in the Pick step, both ways. With a chant#1636 report (the one committed on main now has the lists) it names the survivor the cut would strand and what bridging it costs. With a counts-only report — which is still what a real behold demo carve regenerates, since example-carve/app pins chant 0.44.4 — it reports the counts, names the cut from the breakdown, and says plainly that the survivors are not in this report. "None" and "not reported" stay different claims, and both paths are covered by tests.

chant lint, not chant build (chant#1637) — carve emit folds the bucket's versioning/public-access-block sub-resources into the carve set without carrying them as native props, so build fails two AWS policy rules on source the advisor scored 88. The Emit panel carries a "why lint and not build?" note; README, example-carve/README.md and the docs page say it too.

The write boundary (src/carve-actions.ts)

behold's second write, narrower than the first:

  • The two routes exist only in carve mode and act only when this server booted a demo copy. A plain behold carve report.json shows the same six steps with the runs greyed out and a reason; an ordinary project serve doesn't register them at all (404).
  • One directory is written: <copy>/app/carveout. It sits inside the chant project on purpose — the emitted source imports @intentius/chant-lexicon-aws and Node resolves that from the file's own directory upward, so a sibling carveout/ would fail chant lint on an install problem rather than on the source.
  • The only value off the wire is select, and it must be an address the served report already ranks. The element that reaches the spawn's argv comes from a closed set read off disk, which is a stronger claim than any regex.
  • carve bridge never gets --apply-rewrites. Verified by diffing the copy's legacy-tf/ after a full run: identical.
  • Same conservative shape as feat: the layout sidecar — a hand layout that leaves the browser (#228) #257: JSON body only, caps, structured {error, code, remedy} refusals. AGENTS.md now states both exceptions and their exact size.

Meeting #268

The walkthrough is runnable anywhere but is not a project to switch into — carve mode claims /api/graph and /api/project at app creation, so a running server cannot become one. The catalog row stays visible, marked switchable: false, with the command that works on its face; POST /api/demos/open refuses it before the copy and install rather than after.

Tests

  • src/carve-actions.test.ts — the actions against a fake project-local chant installed into a temp demo copy: the argv built, the --output directory created before chant's --report needs it (real chant writes the report first, verified against 0.44.4), the artifact read-back, path shortening, and a bin that exits non-zero if it ever sees --apply-rewrites. Plus the route guards: 404 off carve mode, 403 outside a demo, 400 on an unranked select, 415/413 on the body.
  • web/carve-steps.test.js — the stepper's pure half: the gates, completed vs "walked past", cutSummary's two claims (including the fallback to the IR node's own counts), runbookCommands (drops annotations, keeps trailing comments, dedupes the runbook's two terraform plans), and lintVerdict counting warnings off chant's summary line rather than a column number.
  • smoke/ui-smoke.mjs — a second stub in carve mode serving the real committed report through the real lens, with the two POSTs canned, so the six steps are deterministic in CI with no chant and no npm install. 30 new checks drive the whole walk headlessly.
  • src/demos-route.test.ts / web/demos.test.js — the unswitchable catalog row and its early refusal.
  • just check green (979 tests), npm run smoke:ui green.

Also driven end to end in a browser against a real behold demo carve — real chant 0.44.4, real emit, real lint, real bridge — which is how the auto-advance, the marker's position (measured, not read off attributes chant doesn't stamp) and a /api/resources 500 on every carve-mode boot were all found.

Deferred

  • The Floci --live tier (carve walkthrough: behold demo carve — the recordable peel-advise → carve experience (#230 M1.5) #254's second comment): scratch Floci in Docker, terraform apply the estate into it, lifecycle diff --live after Emit, a real terraform plan showing no destroy on camera. A follow-up.
  • The morph. The carved card is marked in place; it does not yet slide out of the Terraform boundary into the chant project beside last month's carves. The Done step names this rather than implying it is done.
  • Edge highlights in the graph — the lens draws boundary edges when the report carries them, and the committed fixture now does; a regenerated one won't until example-carve/app's pinned chant is bumped past chant#1636. The stepper reads both.
  • Estate framing. feat: behold carve <report.json> — the Terraform peelability report as a graph (#252) #258's carve mode is single-view: carveRoutes claims /api/graph and /api/project wholesale, and there is no seam for composing the report view with a chant member's graph. So this ships the honest minimum — the carve view with the walkthrough's demo context on it — and leaves serving the report inside an estate frame to the follow-up that adds the seam.

#254 stays open for the live tier and the morph.

🤖 Generated with Claude Code

lex00 and others added 5 commits August 9, 2026 23:10
)

One command from npm to a banded Terraform estate and a six-step stepper that
carves a bucket across the line on camera: advise → pick → emit → bridge →
handoff → done. No Docker, no cloud, no terraform binary.

`behold demo carve` copies example-carve (#262), npm-installs the chant every
step shells, drops @cdktf/hcl2json where chant's lazy import actually resolves
it (the copy's ROOT node_modules — chant resolves from its own install upward,
not from the .tf files), and runs `carve advise --report` over the copy. A boot
that fails any of that serves the committed report and says so on the statusbar
rather than drawing a blank graph.

The stepper lives on a Carve tab mounted at runtime, so no other project grows
a dead tab. Its two ACTION steps really run: POST /api/carve/emit shells
`carve emit --state --select <addr>` and then `chant lint`, and renders the
emitted source, chant's own boundary report and the lint verdict; POST
/api/carve/bridge shells `carve bridge` WITHOUT --apply-rewrites and renders the
proposed data source, the rewired survivors and the patch. Handoff is copy
buttons and a paragraph about why it is not a button. Done marks the card
chant-owned at the observe position.

The write boundary (src/carve-actions.ts): the routes exist only in carve mode
and act only when this server booted a demo copy; one directory is written
(`<copy>/app/carveout`, inside the chant project so the emitted source's lexicon
import resolves); the only request-derived value is `select`, and it must be an
address the served report already ranks, so the argv element comes from a closed
set read off disk. No cloud write, no Terraform mutation — verified by diffing
the copy's legacy-tf after a full run.

Emit reports `chant lint`, never `chant build` (chant#1637): the emitted bucket
folds its sub-resources into the carve set without carrying them as native
props, so build fails two policy rules on source scored 88. The step links the
reason; the README and docs say it too.

Tests: the actions and their refusals against a fake project-local chant in a
temp copy (argv, the mkdir chant needs before --report, the artifact read-back,
never --apply-rewrites), the route guards, the stepper's pure state machine, and
a headless six-step walk in the UI smoke against a carve-mode stub serving the
real committed report through the real lens.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…aw report (#254)

CI caught a race the local run never lost: `/api/carve` is a second fetch (it
exists for the edge lists chant#1636 will publish), and a click that lands
before it does left `cutSummary` with no resource — which made it claim "no
boundary edges at all" for a bucket that has one. A different, wrong statement
about the same resource.

The counts are already on every card: the lens puts `inbound`/`outbound` in
`attrs`. Read them there when the raw report isn't in hand, with `??` so a
genuine 0 stays 0. The panel no longer waits for the fetch to say something
true, and the smoke waits on a readiness marker so it tests the panel rather
than the network.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tab's innerText (#254)

The arithmetic line carries "1 inbound" too, so a substring test over the whole
tab passed whether or not the cut summary rendered — and CI failing only the
note next to it had no way to say which. Assert on .carve-cut and
.carve-honesty directly, and print both when the note is missing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…itchable catalog row (#254)

main moved the demo loader to an async `spawn` and grew a one-click catalog in
the switcher. Two consequences for the walkthrough:

- the carve boot's three steps (npm install, the HCL parser, `carve advise`)
  use the same non-blocking spawn, so cli.ts keeps no `spawnSync`;
- `behold demo carve` is runnable anywhere but is not a project to switch INTO
  — carve mode claims /api/graph and /api/project at app creation, so a running
  server cannot become one. The row stays in the catalog marked
  `switchable: false` with the command that works, and POST /api/demos/open
  refuses it before the copy rather than after the install.

Also: main's committed carve-report.json is now a chant#1636 one, so the smoke
asserts the KNOWN path (the survivor named) and the counts-only fallback moves
to the unit tests, which no longer depend on a fixture this repo ships.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lex00
lex00 merged commit 2b120dc into main Aug 10, 2026
3 checks 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.

1 participant