@intentius/behold 0.19.0 → 0.21.0
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.
- package/AGENTS.md +90 -5
- package/README.md +54 -1
- package/demos.json +3 -2
- package/dist/cli.js +1327 -303
- package/example-argo-estate/README.md +8 -8
- package/example-argo-estate/app-a/package.json +2 -2
- package/example-argo-estate/app-b/package.json +2 -2
- package/example-argo-estate/chant.workspace.json +27 -0
- package/example-argo-estate/control-plane/package.json +2 -2
- package/example-argo-estate/package-lock.json +133 -56
- package/example-carve/README.md +14 -14
- package/example-carve/app/package-lock.json +72 -9
- package/example-carve/app/package.json +2 -2
- package/example-carve/legacy-tf/versions.tf +1 -1
- package/example-choudoufu-estate/README.md +4 -4
- package/example-choudoufu-estate/chant.workspace.json +32 -0
- package/example-choudoufu-estate/scripts/choudoufu-up.sh +21 -0
- package/example-flux-estate/README.md +5 -5
- package/example-flux-estate/app-a/package.json +2 -2
- package/example-flux-estate/app-b/package.json +2 -2
- package/example-flux-estate/chant.workspace.json +27 -0
- package/example-flux-estate/control-plane/package.json +2 -2
- package/example-flux-estate/package-lock.json +133 -56
- package/example-k8s/README.md +20 -20
- package/example-k8s/chant.config.ts +1 -1
- package/example-k8s/ops/k3d-apply.op.ts +1 -1
- package/example-k8s/package-lock.json +129 -211
- package/example-k8s/package.json +2 -3
- package/example-terraform-estate/README.md +5 -5
- package/example-terraform-estate/chant.workspace.json +22 -0
- package/example-writes/README.md +5 -5
- package/example-writes/chant.config.ts +1 -1
- package/example-writes/ops/apply.op.ts +1 -1
- package/example-writes/ops/floci.op.ts +2 -2
- package/example-writes/ops/reconcile.op.ts +1 -1
- package/example-writes/package-lock.json +62 -175
- package/example-writes/package.json +2 -3
- package/package.json +6 -6
- package/web/app.js +431 -17
- package/web/behaviour-scale.js +32 -0
- package/web/behaviour-scale.test.js +39 -0
- package/web/embed.js +76 -0
- package/web/embed.test.js +45 -0
- package/web/index.html +20 -0
- package/web/operator.js +14 -1
- package/web/panel.js +11 -3
- package/web/run-playhead.js +13 -1
- package/web/view-url.js +138 -0
- package/web/view-url.test.js +84 -0
- package/web/workspace-gates.js +44 -0
- package/web/workspace-gates.test.js +34 -0
- package/example-choudoufu-estate/.behold.json +0 -8
package/AGENTS.md
CHANGED
|
@@ -14,6 +14,23 @@ apply creds.
|
|
|
14
14
|
- Actions (delegated writes): `op-run` (start an `ApplyOp`/`ReconcileOp`),
|
|
15
15
|
`op-signal` (approve a gate), `op-status` / `op-report` (watch it).
|
|
16
16
|
|
|
17
|
+
## A declared workspace
|
|
18
|
+
|
|
19
|
+
A directory with a `chant.workspace.json` is a chant workspace (chant ≥ 0.81.0,
|
|
20
|
+
INTENTIUS/chant#2524). `behold serve <root>` serves it from chant's own member
|
|
21
|
+
list (`chant workspace ls --json`, read contract 1; any other contract is
|
|
22
|
+
refused), and reads each `chant` and `terraform` member through `chant
|
|
23
|
+
workspace graph --member <name>`. A `choudoufu` member keeps behold's own
|
|
24
|
+
reader, whose vocabulary (rungs, ownership, adoptable rows) chant's graph does
|
|
25
|
+
not carry. Node ids are `<member>/<id>` with the declared member name, the same
|
|
26
|
+
form chant composes. A member chant cannot read is drawn as one
|
|
27
|
+
`UnreadableMember` node carrying `attrs._unreadable: {code, message}` with
|
|
28
|
+
chant's reason code. An `other` member is listed on startup and in doctor and
|
|
29
|
+
draws nothing. `.behold.json`'s `members` are ignored beside a declaration;
|
|
30
|
+
`behold doctor --fix` writes the declaration for an estate that has only them.
|
|
31
|
+
Several directories on the command line (`behold serve a b c`) stay the loose
|
|
32
|
+
view behold composes itself.
|
|
33
|
+
|
|
17
34
|
## Getting a server
|
|
18
35
|
|
|
19
36
|
```sh
|
|
@@ -239,7 +256,8 @@ Two sources, in this order:
|
|
|
239
256
|
1. `attrs._behaviour` on the nodes, as `chant graph --live --overlay` painted
|
|
240
257
|
them, with `meta._behaviour` beside them for the graph-level half.
|
|
241
258
|
2. `behaviour.<env>.json` at a member's root — `{meta, entities: {<address>:
|
|
242
|
-
block}}` — read **only** when no node of that member carries the attr
|
|
259
|
+
block}}` — read **only** when no node of that member carries the attr and
|
|
260
|
+
the member's read carried no `meta._behaviour` either (#402), and
|
|
243
261
|
**only on the overlay** (`/api/graph` is the source graph; a prediction
|
|
244
262
|
about a live account has no business on it). The file's keys are the
|
|
245
263
|
member's own addresses; behold prefixes them to reach the composed id
|
|
@@ -261,6 +279,41 @@ overlay is untouched either way: nothing in this pass reads or writes
|
|
|
261
279
|
`_status`. `?logical=1` projects a different picture and carries no behaviour
|
|
262
280
|
block in M1.
|
|
263
281
|
|
|
282
|
+
### Live versus declared (#402, M5)
|
|
283
|
+
|
|
284
|
+
With a traffic level, `/api/overlay` predicts each member twice and carries
|
|
285
|
+
both figures and their difference. The level is `?traffic=` on the request,
|
|
286
|
+
else `.behold.json`'s `{"behaviour": {"traffic": "100 rps, p50"}}`, else none;
|
|
287
|
+
with none nothing is asked for and the payload is exactly M1's. behold never
|
|
288
|
+
defaults a level, because every figure is *at* one.
|
|
289
|
+
|
|
290
|
+
`meta.behaviour` gains `live`, `declared` (each `{perHour, currency, basis}`,
|
|
291
|
+
where `basis` is `total` for a figure the engine stated and `sum` for behold
|
|
292
|
+
adding the engine's per-entity figures) and `delta` (`{perHour, currency}`,
|
|
293
|
+
live minus declared), plus the same three per member under `members[<name>]`.
|
|
294
|
+
A delta is only ever like with like: one currency, and two totals or two sums.
|
|
295
|
+
Anything else yields no delta and a line in `diagnostics` saying why. A node
|
|
296
|
+
also carries the file's block under `attrs._behaviourDeclared`, including a
|
|
297
|
+
node the account no longer holds, which has no `_behaviour` at all.
|
|
298
|
+
|
|
299
|
+
Who reads what is `src/behaviour-delta.ts`'s header. A chant member's live
|
|
300
|
+
half is its own overlay read with `--traffic` added (sent only to a chant at
|
|
301
|
+
0.75.0 or newer; an older one rejects the flag). A choudoufu member is not
|
|
302
|
+
read by chant, so behold predicts it through a generated reader project with
|
|
303
|
+
`binary: "choudoufu"` (the Terraform member's scratch project, under
|
|
304
|
+
`behold-chdf-*`), which needs the optional terraform lexicon. A terraform
|
|
305
|
+
member has no live half and so no delta.
|
|
306
|
+
|
|
307
|
+
The declared half goes through `memberIr` and is cached under the member's
|
|
308
|
+
source stamp plus the traffic level. The live half goes through `overlayIr`
|
|
309
|
+
and is dropped by whatever drops the overlay. With a level set, the
|
|
310
|
+
`behaviour.<env>.json` documents are not read at all: a fixture's figures
|
|
311
|
+
beside a real prediction would be two answers to one question.
|
|
312
|
+
|
|
313
|
+
`/api/graph?traffic=` (or the config default) paints the declared side alone
|
|
314
|
+
as `attrs._behaviour` with `meta.behaviour.declared`. The file is the only
|
|
315
|
+
estate that route has, so it carries no live half and no delta.
|
|
316
|
+
|
|
264
317
|
### Colour by drift, cost or headroom (#399, M2)
|
|
265
318
|
|
|
266
319
|
The SPA colours the graph by one of three modes. The mode is **client-side
|
|
@@ -534,7 +587,17 @@ inside a project, and it does not weaken the invariant above:
|
|
|
534
587
|
render with the deltas baked into the SVG, which is how `behold export` and
|
|
535
588
|
static snapshots honour a hand layout.
|
|
536
589
|
|
|
537
|
-
|
|
590
|
+
A declared workspace (#464) keeps that file in the workspace root, the
|
|
591
|
+
directory holding `chant.workspace.json`, rather than in its first member.
|
|
592
|
+
|
|
593
|
+
`behold doctor --fix` (#464, ws-015) is the third exception, and it runs only
|
|
594
|
+
when you type the flag: for an estate root whose members are listed in
|
|
595
|
+
`.behold.json`, it writes `chant.workspace.json` in that root (never over an
|
|
596
|
+
existing one) and moves the saved `.behold/layout.json` from the first member to
|
|
597
|
+
the root with its node ids rewritten. It then runs `chant workspace check`. The
|
|
598
|
+
old layout file and `.behold.json` are left as they were.
|
|
599
|
+
|
|
600
|
+
The fourth exception is the carve walkthrough's two steps above (#254), and it
|
|
538
601
|
is narrower still: they exist only when behold booted a `behold demo carve`
|
|
539
602
|
copy, they write only into `<copy>/app/carveout/`, and the directory they write
|
|
540
603
|
into is a scratch dir behold created inside a directory it copied for you a
|
|
@@ -736,7 +799,7 @@ its own graph and overlay counts as it runs.
|
|
|
736
799
|
| `terralith-1` | `terralith-gen` at scale 1 plus the `estate.chdf.hcl` sidecar it omits, applied by choudoufu from nothing: 79 cards, all 79 bound | docker, choudoufu, go |
|
|
737
800
|
| `terralith-4` | the same at scale 4: 301 cards, all 301 bound. The estate behold is sized against | the same |
|
|
738
801
|
| `terralith-4-adopt` | scale 4 again, but STOCK terraform applies it first, so nothing wears a marker: 301 cards — 85 UNOWNED, 84 bound by derived identity alone, 132 neutral. One `choudoufu live-import` line, which the up script prints and you run, and all 301 read bound | docker, choudoufu, go, terraform |
|
|
739
|
-
| `waterpark` | `../waterpark/access` in place as a bare Terraform directory (#384): five roots as boxes, 58 cards, 49 edges, nothing written under the estate | `@intentius/chant-lexicon-terraform` + `@
|
|
802
|
+
| `waterpark` | `../waterpark/access` in place as a bare Terraform directory (#384): five roots as boxes, 58 cards, 49 edges, nothing written under the estate | `@intentius/chant-lexicon-terraform` + `@cdktn/hcl2json` beside behold — optional peers behold declares and does not install |
|
|
740
803
|
|
|
741
804
|
**The scratch, by name and port.** Each emulator is the entry's own, booted by
|
|
742
805
|
its up script and removed by the `scripts/down.sh` that script wrote into the
|
|
@@ -866,9 +929,9 @@ water park's `access/` before it was taken:
|
|
|
866
929
|
|
|
867
930
|
1. **The lexicon is opt-in.** chant resolves the lexicon from the config file's
|
|
868
931
|
own location, so the generated project has to see it, and making it a
|
|
869
|
-
dependency would put `@
|
|
932
|
+
dependency would put `@cdktn/hcl2json`, a ~1.8 MB wasm blob, in the install
|
|
870
933
|
of every user who serves a chant project. `@intentius/chant-lexicon-terraform`
|
|
871
|
-
and `@
|
|
934
|
+
and `@cdktn/hcl2json` are therefore **optional peers**: declared in
|
|
872
935
|
package.json (the only place their versions are named, the refusal reads
|
|
873
936
|
them from there), never installed by behold, probed at serve and doctor time,
|
|
874
937
|
and refused with the one install line and where behold looked. The same gate
|
|
@@ -1001,6 +1064,28 @@ and marking every one of them would say nothing.
|
|
|
1001
1064
|
This is presentation, not vocabulary. `statusVocabulary` gets no terraform row:
|
|
1002
1065
|
there is no status to name. See #429 for the measurement that established it.
|
|
1003
1066
|
|
|
1067
|
+
### An adoptable row is painted like an unowned one, plus its evidence (#413)
|
|
1068
|
+
|
|
1069
|
+
A `NEEDS_DISCOVERY` omission paired with an `adoptable[]` row at the same
|
|
1070
|
+
address paints `warn` with `ownership: "foreign"`, the same verdict the UNOWNED
|
|
1071
|
+
branch gives and for the same reason: the live object carries no marker for
|
|
1072
|
+
this estate. On its own, `NEEDS_DISCOVERY` stays neutral — the declaration
|
|
1073
|
+
names no identity, and with nothing found that is a "could not answer" rather
|
|
1074
|
+
than an action.
|
|
1075
|
+
|
|
1076
|
+
The card leads with `attrs.adopt`, which is choudoufu's own `adopt_command`
|
|
1077
|
+
here rather than the marker pair an unowned row carries. `adoptLine`'s
|
|
1078
|
+
composed-command branch had no real caller until this. behold composes no
|
|
1079
|
+
command of its own: the API that writes a tag differs per resource type.
|
|
1080
|
+
|
|
1081
|
+
`attrs.matchedOn` is the difference in kind. An unowned row needs no evidence
|
|
1082
|
+
because the identity IS the match; here choudoufu asserts that a live object is
|
|
1083
|
+
a declaration because their identity-bearing arguments agree, and a person
|
|
1084
|
+
about to stamp two tags onto somebody's resource is owed the list.
|
|
1085
|
+
|
|
1086
|
+
`web/app.js` renders both through the one copyable row, and its tooltip says
|
|
1087
|
+
whether the line is a command or a pair of tags.
|
|
1088
|
+
|
|
1004
1089
|
### The adoption sweep is off unless a serve asks (#412)
|
|
1005
1090
|
|
|
1006
1091
|
`live-plan -json` carries `adoptable[]` (live resources the estate-wide sweep
|
package/README.md
CHANGED
|
@@ -310,6 +310,55 @@ behold shells the **project's own** chant (resolved from the project's
|
|
|
310
310
|
`@intentius/chant ^0.18.1` or later for the live overlay; `graph --live` observed
|
|
311
311
|
nothing before that fix.
|
|
312
312
|
|
|
313
|
+
## Linking to a view, and framing behold in a host
|
|
314
|
+
|
|
315
|
+
The view is in the URL. behold reads it at boot and writes it back as the view
|
|
316
|
+
changes, so a reload keeps it and a link opens where the sender was:
|
|
317
|
+
|
|
318
|
+
```
|
|
319
|
+
http://localhost:4600/?member=delivery&zoom=components&env=prod&node=delivery/appService
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
`member`, `zoom`, `env`, `tier`, `lens` (the colour: `drift`, `cost` or
|
|
323
|
+
`headroom`), `node` and `radial` (`1` or `0`). An empty `env=` is the source
|
|
324
|
+
graph. A value the estate can't honour (a member it doesn't have, a zoom it
|
|
325
|
+
doesn't offer) is named on the now line. A static export takes the same
|
|
326
|
+
parameters against what it captured.
|
|
327
|
+
|
|
328
|
+
A host pane, such as arugula's workspace block, frames behold with
|
|
329
|
+
`?embed=1&host=<the host's origin>`, and optionally `theme=light`, `dark` or
|
|
330
|
+
any theme name the picker lists:
|
|
331
|
+
|
|
332
|
+
- the panel and the inspect pane start folded, and the Deploy tab and the theme
|
|
333
|
+
picker are gone
|
|
334
|
+
- picking a card posts `{type: "behold:select", member, node}` to the parent at
|
|
335
|
+
`host`; picking a member's box posts the same with `node: null`
|
|
336
|
+
- the parent moves the view with `{type: "behold:view", ...}`, carrying any of
|
|
337
|
+
the URL's fields (`member: null` is the whole estate). Messages from any
|
|
338
|
+
other origin are ignored
|
|
339
|
+
- gates are the host's to approve: behold draws them and says so
|
|
340
|
+
|
|
341
|
+
`gates=<env>` sets the env the gate strip reads, apart from the graph's, so a
|
|
342
|
+
host can open behold on the source graph and still show the gates of the env
|
|
343
|
+
it watches.
|
|
344
|
+
|
|
345
|
+
behold binds 127.0.0.1 and answers only loopback names, since it runs writes
|
|
346
|
+
with the credentials of whoever started it. A page from another site can't
|
|
347
|
+
write to it. A host that frames behold through a proxy of its own passes the
|
|
348
|
+
proxy's name with `--allow-host <name>` (or `BEHOLD_ALLOWED_HOSTS`); `--host`
|
|
349
|
+
(or `BEHOLD_HOST`) binds another address, which then needs `--allow-host` for
|
|
350
|
+
the names it is reached by.
|
|
351
|
+
|
|
352
|
+
In a declared workspace, the gates waiting on a person come from
|
|
353
|
+
`chant workspace status <env> --json`, keyed `member/op/gate` the way arugula's
|
|
354
|
+
workspace block keys them, so the two show the same set. Standalone, a gate's
|
|
355
|
+
button says who chant will record (the user running behold) before the click.
|
|
356
|
+
Every approve route refuses in preview mode.
|
|
357
|
+
|
|
358
|
+
A host that already watches the workspace tells behold it changed with
|
|
359
|
+
`POST /api/refresh?notify=1`, instead of running behold with `--poll` beside
|
|
360
|
+
it. behold drops its cached reads and every open page re-pulls.
|
|
361
|
+
|
|
313
362
|
## Terraform carve-out: `behold carve <report.json>`
|
|
314
363
|
|
|
315
364
|
`chant carve advise` ranks a Terraform estate by **peelability**: how cleanly
|
|
@@ -375,7 +424,11 @@ config, kept separate from `chant.config.ts` so behold's concerns (like the
|
|
|
375
424
|
tier picker) don't leak into chant's. Three keys: `tiers` below, `executor`
|
|
376
425
|
(which forge deploys an environment), and `members`, spelled `[{ "dir": "x", "kind":
|
|
377
426
|
"chant" | "choudoufu" | "terraform" }]`, how an estate root names what it
|
|
378
|
-
composes, fail-closed on a kind behold has no reader for.
|
|
427
|
+
composes, fail-closed on a kind behold has no reader for. `members` is
|
|
428
|
+
deprecated: a chant workspace declaration (`chant.workspace.json`) lists an
|
|
429
|
+
estate's members for every reader, and beside one, `.behold.json`'s list is
|
|
430
|
+
ignored. `behold doctor --fix` writes the declaration from it (see
|
|
431
|
+
[Serve a chant workspace](https://intentius.io/behold/using/workspace/)). First, the
|
|
379
432
|
project's deploy-**tier** axis, a dimension orthogonal to `environment` (chant
|
|
380
433
|
has no native tier concept; it is entirely a project convention, e.g. Loom's
|
|
381
434
|
components branching on an env-conditioned `namingParams.tier`):
|
package/demos.json
CHANGED
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
"name": "terraform-estate",
|
|
68
|
-
"description": "A multi-root Terraform estate, declared only: two roots applied separately (envs/prod and platform) plus the shared module both are built on — 37 cards boxed by root, module calls expanded into their own contents, and the two look-alike versions.tf files told apart. Shows what behold does NOT infer as clearly as what it draws: platform declares the KMS key and prod reads it back by alias, and no edge is drawn between them, because both ends carry the same unresolved interpolation (#381). Needs chant's terraform lexicon beside behold, which behold does not install: npm install --no-save @intentius/chant-lexicon-terraform @
|
|
68
|
+
"description": "A multi-root Terraform estate, declared only: two roots applied separately (envs/prod and platform) plus the shared module both are built on — 37 cards boxed by root, module calls expanded into their own contents, and the two look-alike versions.tf files told apart. Shows what behold does NOT infer as clearly as what it draws: platform declares the KMS key and prod reads it back by alias, and no edge is drawn between them, because both ends carry the same unresolved interpolation (#381). Needs chant's terraform lexicon beside behold, which behold does not install: npm install --no-save @intentius/chant-lexicon-terraform @cdktn/hcl2json (the refusal names the exact line). No cloud, no Docker, no terraform binary.",
|
|
69
69
|
"source": "bundled",
|
|
70
70
|
"dir": "example-terraform-estate",
|
|
71
71
|
"requires": [],
|
|
@@ -109,7 +109,8 @@
|
|
|
109
109
|
"AWS_ENDPOINT_URL": "http://127.0.0.1:4650",
|
|
110
110
|
"AWS_ACCESS_KEY_ID": "test",
|
|
111
111
|
"AWS_SECRET_ACCESS_KEY": "test",
|
|
112
|
-
"AWS_REGION": "us-east-1"
|
|
112
|
+
"AWS_REGION": "us-east-1",
|
|
113
|
+
"TOFU_LIVE_COLLECT_UNCLAIMED": "1"
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
},
|