@intentius/behold 0.9.1 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@
7
7
  "build": "chant build src -o manifests/app.yaml --format yaml"
8
8
  },
9
9
  "dependencies": {
10
- "@intentius/chant": "^0.44.7",
11
- "@intentius/chant-lexicon-k8s": "^0.44.7"
10
+ "@intentius/chant": "^0.44.8",
11
+ "@intentius/chant-lexicon-k8s": "^0.44.8"
12
12
  }
13
13
  }
@@ -7,7 +7,7 @@
7
7
  "build": "chant build src -o manifests/app.yaml --format yaml"
8
8
  },
9
9
  "dependencies": {
10
- "@intentius/chant": "^0.44.7",
11
- "@intentius/chant-lexicon-k8s": "^0.44.7"
10
+ "@intentius/chant": "^0.44.8",
11
+ "@intentius/chant-lexicon-k8s": "^0.44.8"
12
12
  }
13
13
  }
@@ -7,7 +7,7 @@
7
7
  "build": "chant build src -o dist/control-plane.yaml --format yaml"
8
8
  },
9
9
  "dependencies": {
10
- "@intentius/chant": "^0.44.7",
11
- "@intentius/chant-lexicon-k8s": "^0.44.7"
10
+ "@intentius/chant": "^0.44.8",
11
+ "@intentius/chant-lexicon-k8s": "^0.44.8"
12
12
  }
13
13
  }
@@ -14,22 +14,22 @@
14
14
  "app-a": {
15
15
  "name": "behold-example-argo-app-a",
16
16
  "dependencies": {
17
- "@intentius/chant": "^0.44.7",
18
- "@intentius/chant-lexicon-k8s": "^0.44.7"
17
+ "@intentius/chant": "^0.44.8",
18
+ "@intentius/chant-lexicon-k8s": "^0.44.8"
19
19
  }
20
20
  },
21
21
  "app-b": {
22
22
  "name": "behold-example-argo-app-b",
23
23
  "dependencies": {
24
- "@intentius/chant": "^0.44.7",
25
- "@intentius/chant-lexicon-k8s": "^0.44.7"
24
+ "@intentius/chant": "^0.44.8",
25
+ "@intentius/chant-lexicon-k8s": "^0.44.8"
26
26
  }
27
27
  },
28
28
  "control-plane": {
29
29
  "name": "behold-example-argo-control-plane",
30
30
  "dependencies": {
31
- "@intentius/chant": "^0.44.7",
32
- "@intentius/chant-lexicon-k8s": "^0.44.7"
31
+ "@intentius/chant": "^0.44.8",
32
+ "@intentius/chant-lexicon-k8s": "^0.44.8"
33
33
  }
34
34
  },
35
35
  "node_modules/@dagrejs/dagre": {
@@ -464,9 +464,9 @@
464
464
  }
465
465
  },
466
466
  "node_modules/@intentius/chant": {
467
- "version": "0.44.7",
468
- "resolved": "https://registry.npmjs.org/@intentius/chant/-/chant-0.44.7.tgz",
469
- "integrity": "sha512-etCchkfbWFHJ+XnTYVVn9GT4/gZi/O8SVphDXHeuPOfK5VvRxo0unveIUEsuRoHGHgzN9Y+aj0Jk15xhIE7Gzg==",
467
+ "version": "0.44.8",
468
+ "resolved": "https://registry.npmjs.org/@intentius/chant/-/chant-0.44.8.tgz",
469
+ "integrity": "sha512-c44GY1RExBBEfPB495qwFR09L4CxYXm/CVDYQQbcUtq5QZ/fSQYLymKxSiHKFbhTd3UllHv70Ic3maC+ivm1vg==",
470
470
  "license": "Apache-2.0",
471
471
  "dependencies": {
472
472
  "@dagrejs/dagre": "^3.0.0",
@@ -482,9 +482,9 @@
482
482
  }
483
483
  },
484
484
  "node_modules/@intentius/chant-k8s-client": {
485
- "version": "0.44.7",
486
- "resolved": "https://registry.npmjs.org/@intentius/chant-k8s-client/-/chant-k8s-client-0.44.7.tgz",
487
- "integrity": "sha512-G9VVki/qHZ+hbKrxDDnVVhfeZWoHgozLA7tjcY/lsa/fDIwI0xMJnAN/DJNcQLBpRscXm3y3Ki/PNQ9SxshmLA==",
485
+ "version": "0.44.8",
486
+ "resolved": "https://registry.npmjs.org/@intentius/chant-k8s-client/-/chant-k8s-client-0.44.8.tgz",
487
+ "integrity": "sha512-k+w06ewOdUvaak9lnRRauy+xobDNfljht6/VVO0UtgCEv97xd1bqWWjzo16Zi0A0Ce0T1hls6h95JSGrxoRmpQ==",
488
488
  "license": "Apache-2.0",
489
489
  "optional": true,
490
490
  "dependencies": {
@@ -492,19 +492,19 @@
492
492
  }
493
493
  },
494
494
  "node_modules/@intentius/chant-lexicon-k8s": {
495
- "version": "0.44.7",
496
- "resolved": "https://registry.npmjs.org/@intentius/chant-lexicon-k8s/-/chant-lexicon-k8s-0.44.7.tgz",
497
- "integrity": "sha512-h8QXOYoxjsAeA1w8koLQlZqOuSQcvWpEHKmwlf0AKPgwuVRiapwJI+UtuoBwL/AOrBKDh6Z5pW3zbo6vDltGvA==",
495
+ "version": "0.44.8",
496
+ "resolved": "https://registry.npmjs.org/@intentius/chant-lexicon-k8s/-/chant-lexicon-k8s-0.44.8.tgz",
497
+ "integrity": "sha512-4jU+NzhyobLQZVBAnG40RgrD6AFnnJzLIoHPzZhXzitLsXgGuLblhpPDI2ZXWKdUOxZX9nkeqNkhoQF9tuFV0A==",
498
498
  "license": "Apache-2.0",
499
499
  "dependencies": {
500
500
  "@types/js-yaml": "^4.0.9",
501
501
  "js-yaml": "^4.1.1"
502
502
  },
503
503
  "optionalDependencies": {
504
- "@intentius/chant-k8s-client": "^0.44.7"
504
+ "@intentius/chant-k8s-client": "^0.44.8"
505
505
  },
506
506
  "peerDependencies": {
507
- "@intentius/chant": "^0.44.7",
507
+ "@intentius/chant": "^0.44.8",
508
508
  "typescript": "^5.9.3",
509
509
  "zod": "^4.3.6"
510
510
  }
@@ -170,14 +170,21 @@ no AWS account and no network. The only dependency beyond this directory is
170
170
  `@cdktf/hcl2json`, which chant lazy-loads and names in its error if absent. This
171
171
  tier guarantees the first thirty seconds of the video.
172
172
 
173
- **`--live` is the full video.** Boot a scratch Floci, arm
174
- `legacy-tf/floci-override.tf.disabled` (see its header), and `terraform apply`
175
- the estate into it, so the state is one terraform really wrote. Then the observe
176
- beats become footage rather than caption: after Emit, `chant lifecycle diff
177
- --live` reads the bucket out of Floci, clean, while Terraform still owns it; the
178
- handoff runs a real `terraform plan` showing no destroy; and behold's overlay
179
- flips the bucket green afterwards. The line it exists for is "Terraform forgot
180
- it, chant adopted it, and it never blinked."
173
+ **`--live` is the full video.** `behold demo carve --live` boots a scratch
174
+ Floci (`behold-carve-floci`, its own port, deleted on exit), arms
175
+ `legacy-tf/floci-override.tf.disabled` (see its header) into the demo copy, and
176
+ `terraform apply -target`s the starred resources into it so the tfstate the
177
+ advisor reads is one terraform really wrote. At Handoff the stepper gains a
178
+ read-only **terraform plan** button: before `state rm` it shows a no-op, after
179
+ it the carved bucket is simply gone from Terraform's world `0 to destroy`.
180
+ The line the tier exists for is "Terraform forgot it, chant adopted it, and it
181
+ never blinked."
182
+
183
+ One beat stays caption rather than footage for now: `chant lifecycle diff
184
+ --live` reading the bucket clean while Terraform still owns it. chant's AWS
185
+ observe is CFN-stack-scoped by logical id, so a Terraform-owned resource reads
186
+ confirmed-missing regardless of its existence — chant#1647 tracks the
187
+ physical-identity read path that unlocks it.
181
188
 
182
189
  The live tier needs `docker` and `terraform` on PATH, boots its own throwaway
183
190
  Floci and deletes it after, and never touches an existing `floci*` container.
@@ -7,7 +7,7 @@
7
7
  "build": "chant build src -o manifests/app.yaml --format yaml"
8
8
  },
9
9
  "dependencies": {
10
- "@intentius/chant": "^0.44.7",
11
- "@intentius/chant-lexicon-k8s": "^0.44.7"
10
+ "@intentius/chant": "^0.44.8",
11
+ "@intentius/chant-lexicon-k8s": "^0.44.8"
12
12
  }
13
13
  }
@@ -7,7 +7,7 @@
7
7
  "build": "chant build src -o manifests/app.yaml --format yaml"
8
8
  },
9
9
  "dependencies": {
10
- "@intentius/chant": "^0.44.7",
11
- "@intentius/chant-lexicon-k8s": "^0.44.7"
10
+ "@intentius/chant": "^0.44.8",
11
+ "@intentius/chant-lexicon-k8s": "^0.44.8"
12
12
  }
13
13
  }
@@ -7,7 +7,7 @@
7
7
  "build": "chant build src -o dist/control-plane.yaml --format yaml"
8
8
  },
9
9
  "dependencies": {
10
- "@intentius/chant": "^0.44.7",
11
- "@intentius/chant-lexicon-k8s": "^0.44.7"
10
+ "@intentius/chant": "^0.44.8",
11
+ "@intentius/chant-lexicon-k8s": "^0.44.8"
12
12
  }
13
13
  }
@@ -14,22 +14,22 @@
14
14
  "app-a": {
15
15
  "name": "behold-example-flux-app-a",
16
16
  "dependencies": {
17
- "@intentius/chant": "^0.44.7",
18
- "@intentius/chant-lexicon-k8s": "^0.44.7"
17
+ "@intentius/chant": "^0.44.8",
18
+ "@intentius/chant-lexicon-k8s": "^0.44.8"
19
19
  }
20
20
  },
21
21
  "app-b": {
22
22
  "name": "behold-example-flux-app-b",
23
23
  "dependencies": {
24
- "@intentius/chant": "^0.44.7",
25
- "@intentius/chant-lexicon-k8s": "^0.44.7"
24
+ "@intentius/chant": "^0.44.8",
25
+ "@intentius/chant-lexicon-k8s": "^0.44.8"
26
26
  }
27
27
  },
28
28
  "control-plane": {
29
29
  "name": "behold-example-flux-control-plane",
30
30
  "dependencies": {
31
- "@intentius/chant": "^0.44.7",
32
- "@intentius/chant-lexicon-k8s": "^0.44.7"
31
+ "@intentius/chant": "^0.44.8",
32
+ "@intentius/chant-lexicon-k8s": "^0.44.8"
33
33
  }
34
34
  },
35
35
  "node_modules/@dagrejs/dagre": {
@@ -464,9 +464,9 @@
464
464
  }
465
465
  },
466
466
  "node_modules/@intentius/chant": {
467
- "version": "0.44.7",
468
- "resolved": "https://registry.npmjs.org/@intentius/chant/-/chant-0.44.7.tgz",
469
- "integrity": "sha512-etCchkfbWFHJ+XnTYVVn9GT4/gZi/O8SVphDXHeuPOfK5VvRxo0unveIUEsuRoHGHgzN9Y+aj0Jk15xhIE7Gzg==",
467
+ "version": "0.44.8",
468
+ "resolved": "https://registry.npmjs.org/@intentius/chant/-/chant-0.44.8.tgz",
469
+ "integrity": "sha512-c44GY1RExBBEfPB495qwFR09L4CxYXm/CVDYQQbcUtq5QZ/fSQYLymKxSiHKFbhTd3UllHv70Ic3maC+ivm1vg==",
470
470
  "license": "Apache-2.0",
471
471
  "dependencies": {
472
472
  "@dagrejs/dagre": "^3.0.0",
@@ -482,9 +482,9 @@
482
482
  }
483
483
  },
484
484
  "node_modules/@intentius/chant-k8s-client": {
485
- "version": "0.44.7",
486
- "resolved": "https://registry.npmjs.org/@intentius/chant-k8s-client/-/chant-k8s-client-0.44.7.tgz",
487
- "integrity": "sha512-G9VVki/qHZ+hbKrxDDnVVhfeZWoHgozLA7tjcY/lsa/fDIwI0xMJnAN/DJNcQLBpRscXm3y3Ki/PNQ9SxshmLA==",
485
+ "version": "0.44.8",
486
+ "resolved": "https://registry.npmjs.org/@intentius/chant-k8s-client/-/chant-k8s-client-0.44.8.tgz",
487
+ "integrity": "sha512-k+w06ewOdUvaak9lnRRauy+xobDNfljht6/VVO0UtgCEv97xd1bqWWjzo16Zi0A0Ce0T1hls6h95JSGrxoRmpQ==",
488
488
  "license": "Apache-2.0",
489
489
  "optional": true,
490
490
  "dependencies": {
@@ -492,19 +492,19 @@
492
492
  }
493
493
  },
494
494
  "node_modules/@intentius/chant-lexicon-k8s": {
495
- "version": "0.44.7",
496
- "resolved": "https://registry.npmjs.org/@intentius/chant-lexicon-k8s/-/chant-lexicon-k8s-0.44.7.tgz",
497
- "integrity": "sha512-h8QXOYoxjsAeA1w8koLQlZqOuSQcvWpEHKmwlf0AKPgwuVRiapwJI+UtuoBwL/AOrBKDh6Z5pW3zbo6vDltGvA==",
495
+ "version": "0.44.8",
496
+ "resolved": "https://registry.npmjs.org/@intentius/chant-lexicon-k8s/-/chant-lexicon-k8s-0.44.8.tgz",
497
+ "integrity": "sha512-4jU+NzhyobLQZVBAnG40RgrD6AFnnJzLIoHPzZhXzitLsXgGuLblhpPDI2ZXWKdUOxZX9nkeqNkhoQF9tuFV0A==",
498
498
  "license": "Apache-2.0",
499
499
  "dependencies": {
500
500
  "@types/js-yaml": "^4.0.9",
501
501
  "js-yaml": "^4.1.1"
502
502
  },
503
503
  "optionalDependencies": {
504
- "@intentius/chant-k8s-client": "^0.44.7"
504
+ "@intentius/chant-k8s-client": "^0.44.8"
505
505
  },
506
506
  "peerDependencies": {
507
- "@intentius/chant": "^0.44.7",
507
+ "@intentius/chant": "^0.44.8",
508
508
  "typescript": "^5.9.3",
509
509
  "zod": "^4.3.6"
510
510
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/behold",
3
- "version": "0.9.1",
3
+ "version": "0.10.1",
4
4
  "type": "module",
5
5
  "description": "behold — a live control plane on chant. See your whole estate (every substrate in one graph), coloured by drift; act through delegated, gated Ops.",
6
6
  "bin": {
@@ -37,7 +37,7 @@
37
37
  "@hono/node-server": "^2.0.5",
38
38
  "@intentius/chant": "^0.38.0",
39
39
  "@intentius/chant-k8s-client": "^0.44.5",
40
- "@intentius/pinhole": "^0.3.3",
40
+ "@intentius/pinhole": "^0.3.4",
41
41
  "hono": "^4.12.34"
42
42
  },
43
43
  "devDependencies": {
package/web/app.js CHANGED
@@ -1233,6 +1233,32 @@ const carveActions = {
1233
1233
  },
1234
1234
  runEmit: () => runCarveStep("emit"),
1235
1235
  runBridge: () => runCarveStep("bridge"),
1236
+ async runPlan() {
1237
+ if (carveState.busy) return;
1238
+ carveState.busy = "plan";
1239
+ carveState.error = null;
1240
+ renderPanelCarve();
1241
+ try {
1242
+ const res = await fetch("/api/carve/plan", {
1243
+ method: "POST",
1244
+ headers: { "content-type": "application/json" },
1245
+ body: "{}",
1246
+ });
1247
+ const body = await res.json().catch(() => ({ error: "plan returned an unreadable body", remedy: "" }));
1248
+ if (!res.ok || body.error) {
1249
+ carveState.error = { step: "handoff", ...body };
1250
+ showToast(`✗ terraform plan: ${body.error || res.status}`, false);
1251
+ } else {
1252
+ carveState.plan = body;
1253
+ showToast(body.noDestroy ? "✓ plan: nothing to destroy" : "✗ plan wants to destroy something", body.noDestroy);
1254
+ }
1255
+ } catch (err) {
1256
+ carveState.error = { step: "handoff", error: String((err && err.message) || err), remedy: "Is the behold server still running?" };
1257
+ } finally {
1258
+ carveState.busy = null;
1259
+ renderPanelCarve();
1260
+ }
1261
+ },
1236
1262
  copy(text, el) {
1237
1263
  const done = () => {
1238
1264
  el.dataset.copied = "1";
@@ -63,7 +63,10 @@ export function initialCarveState() {
63
63
  bridge: null,
64
64
  /** Set when the user says they ran the handoff commands. */
65
65
  handoff: false,
66
- /** Which step is mid-run ("emit" | "bridge" | null) the dial's progress. */
66
+ /** POST /api/carve/plan's answer (live tier only). Estate-wide, so a new
67
+ * pick keeps it — the plan is about the armed Terraform, not the pick. */
68
+ plan: null,
69
+ /** Which step is mid-run ("emit" | "bridge" | "plan" | null). */
67
70
  busy: null,
68
71
  /** The last structured refusal, keyed by step id. */
69
72
  error: null,
@@ -378,7 +381,7 @@ export function renderCarvePanel(host, state, ctx, actions) {
378
381
  pick: () => renderPick(body, state, ctx, actions),
379
382
  emit: () => renderEmit(body, state, ctx, actions),
380
383
  bridge: () => renderBridge(body, state, ctx, actions),
381
- handoff: () => renderHandoff(body, state, actions),
384
+ handoff: () => renderHandoff(body, state, ctx, actions),
382
385
  done: () => renderDone(body, state, ctx, actions),
383
386
  };
384
387
  renderers[step.id]();
@@ -426,6 +429,17 @@ function renderAdvise(body, ctx) {
426
429
  `Terraform read from ${ctx.demo.fromLabel}/ in your demo copy. Emit and bridge write only into ${ctx.demo.outLabel}/.`,
427
430
  ),
428
431
  );
432
+ if (ctx.demo.live) {
433
+ body.appendChild(
434
+ el(
435
+ "p",
436
+ "panel-muted carve-live-note",
437
+ `Live tier: the starred resources are REALLY applied into a scratch Floci (${ctx.demo.live.endpoint}, ` +
438
+ "deleted when this server exits) and the tfstate the advisor read was written by terraform, not shipped. " +
439
+ "The observe beat — chant reading the bucket live while Terraform still owns it — waits on chant#1647.",
440
+ ),
441
+ );
442
+ }
429
443
  }
430
444
  }
431
445
 
@@ -544,12 +558,38 @@ function renderBridge(body, state, ctx, actions) {
544
558
  for (const a of b.proposals || []) body.appendChild(artifactBlock(a, actions));
545
559
  }
546
560
 
547
- function renderHandoff(body, state, actions) {
561
+ function renderHandoff(body, state, ctx, actions) {
548
562
  const runbook = state.bridge && state.bridge.runbook;
549
563
  if (!runbook) {
550
564
  body.appendChild(el("p", "panel-muted", blockedReason(state, "handoff") || "no runbook yet"));
551
565
  return;
552
566
  }
567
+
568
+ // The live tier's read-only beat (#254): a REAL `terraform plan`. Before the
569
+ // operator pastes `state rm` it shows a no-op; after, the carved resource is
570
+ // simply absent from Terraform's world — not destroyed. That verdict line is
571
+ // the reason the tier exists.
572
+ if (ctx.demo && ctx.demo.live) {
573
+ const planBtn = el("button", "act carve-plan", state.busy === "plan" ? "planning…" : "▶ terraform plan (read-only)");
574
+ planBtn.disabled = !!state.busy;
575
+ planBtn.title = "Runs terraform plan in the demo copy against the scratch Floci. Reads everything, changes nothing.";
576
+ planBtn.addEventListener("click", actions.runPlan);
577
+ body.appendChild(planBtn);
578
+ if (state.plan) {
579
+ const verdict = el("div", "carve-plan-verdict");
580
+ verdict.appendChild(el("code", null, state.plan.planLine));
581
+ verdict.appendChild(
582
+ el(
583
+ "p",
584
+ "panel-muted",
585
+ state.plan.noDestroy
586
+ ? "0 to destroy — the resource never blinked."
587
+ : "The plan wants to destroy something. Stop and read it before touching the estate.",
588
+ ),
589
+ );
590
+ body.appendChild(verdict);
591
+ }
592
+ }
553
593
  // The one place the walkthrough refuses to be a button, stated out loud.
554
594
  const why = el("div", "carve-refusal carve-human");
555
595
  why.appendChild(el("div", "carve-refusal-title", "These two are not buttons, on purpose."));
@@ -605,13 +645,20 @@ function renderDone(body, state, ctx, actions) {
605
645
  if (!state.handoff) {
606
646
  body.appendChild(el("p", "panel-muted", "The handoff commands haven't been marked as run — this is the shape of the ending, not a claim about your estate."));
607
647
  }
648
+ if (state.pick) {
649
+ const watch = el("a", "act carve-morph-link", "▶ watch it move");
650
+ watch.href = "/carve/morph?select=" + encodeURIComponent(state.pick.node.id);
651
+ watch.target = "_blank";
652
+ watch.rel = "noopener";
653
+ watch.title = "The estate frame, morphing: the card slides out of the Terraform box and into the chant project box beside last month's carves.";
654
+ body.appendChild(watch);
655
+ }
608
656
  body.appendChild(
609
657
  el(
610
658
  "p",
611
659
  "panel-muted carve-honesty",
612
- "Deferred to the follow-up: the card doesn't yet SLIDE out of the Terraform box and into the chant project " +
613
- "beside last month's carves — it is marked in place. The Floci `--live` tier (a real terraform apply, a real " +
614
- "plan showing no destroy) is the other half of that follow-up.",
660
+ "Deferred to the follow-up: the Floci `--live` tier a real terraform apply into a scratch emulator, " +
661
+ "a real plan showing no destroy.",
615
662
  ),
616
663
  );
617
664
  const again = el("button", "act", "↺ start over");