@intentius/behold 0.18.0 → 0.20.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 +377 -76
- package/README.md +238 -190
- package/demos.json +17 -8
- package/dist/cli.js +13118 -11622
- 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 +139 -62
- package/example-carve/README.md +14 -14
- package/example-carve/app/package-lock.json +75 -12
- 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 +139 -62
- 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 +135 -217
- package/example-k8s/package.json +2 -3
- package/example-terraform-estate/README.md +109 -0
- package/example-terraform-estate/backends/backend.s3.tf +10 -0
- package/example-terraform-estate/chant.workspace.json +22 -0
- package/example-terraform-estate/envs/dev/README.md +5 -0
- package/example-terraform-estate/envs/prod/data.tf +18 -0
- package/example-terraform-estate/envs/prod/network.tf +80 -0
- package/example-terraform-estate/envs/prod/outputs.tf +13 -0
- package/example-terraform-estate/envs/prod/provider.tf +10 -0
- package/example-terraform-estate/envs/prod/services.tf +21 -0
- package/example-terraform-estate/envs/prod/storage.tf +31 -0
- package/example-terraform-estate/envs/prod/variables.tf +17 -0
- package/example-terraform-estate/envs/prod/versions.tf +10 -0
- package/example-terraform-estate/modules/service/main.tf +58 -0
- package/example-terraform-estate/modules/service/outputs.tf +7 -0
- package/example-terraform-estate/modules/service/variables.tf +16 -0
- package/example-terraform-estate/modules/service/versions.tf +10 -0
- package/example-terraform-estate/platform/iam.tf +40 -0
- package/example-terraform-estate/platform/kms.tf +16 -0
- package/example-terraform-estate/platform/outputs.tf +7 -0
- package/example-terraform-estate/platform/variables.tf +10 -0
- package/example-terraform-estate/platform/versions.tf +10 -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 +65 -178
- package/example-writes/package.json +2 -3
- package/package.json +7 -6
- package/web/app.js +439 -34
- package/web/behaviour-scale.js +32 -0
- package/web/behaviour-scale.test.js +39 -0
- package/web/index.html +23 -1
- package/web/pending.js +67 -0
- package/web/pending.test.js +62 -0
- package/web/read-cost.js +49 -0
- package/web/read-cost.test.js +76 -0
- package/web/refresh-queue.js +26 -0
- package/web/refresh-queue.test.js +54 -0
- package/example-choudoufu-estate/.behold.json +0 -8
package/README.md
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
# behold
|
|
2
2
|
|
|
3
|
-
**A live control plane on [chant](https://github.com/INTENTIUS/chant).**
|
|
4
|
-
|
|
5
|
-
drift — then act through delegated, gated Ops.
|
|
3
|
+
**A live control plane on [chant](https://github.com/INTENTIUS/chant).** Every
|
|
4
|
+
substrate in one graph, coloured by drift. Then act through delegated, gated Ops.
|
|
6
5
|
|
|
7
|
-
📖 **[Documentation](https://intentius.io/behold/)**
|
|
8
|
-
gets you to a running graph without reading anything else first.
|
|
6
|
+
📖 **[Documentation](https://intentius.io/behold/)** starts with an agent prompt
|
|
7
|
+
that gets you to a running graph without reading anything else first.
|
|
9
8
|
|
|
10
9
|
Where Argo CD shows one cluster's tree, behold shows the mixed-substrate estate:
|
|
11
|
-
cloud drift on AWS, supply-chain drift on GitHub Actions, artifact presence
|
|
12
|
-
|
|
10
|
+
cloud drift on AWS, supply-chain drift on GitHub Actions, artifact presence on
|
|
11
|
+
Helm. Each substrate's own kind of truth, in one picture.
|
|
13
12
|
|
|
14
13
|
```
|
|
15
14
|
chant source ──build/lint──▶ graph IR ──behold──▶ live graph + drift + (delegated) actions
|
|
@@ -18,30 +17,34 @@ chant source ──build/lint──▶ graph IR ──behold──▶ live graph
|
|
|
18
17
|
|
|
19
18
|
## Quick start (npm)
|
|
20
19
|
|
|
21
|
-
No chant project yet? The bundled demo is the five-minute path
|
|
22
|
-
policy
|
|
23
|
-
(needs Docker):
|
|
20
|
+
No chant project yet? The bundled demo is the five-minute path: an S3 bucket and
|
|
21
|
+
policy against a local emulator. Needs Docker, and nothing else.
|
|
24
22
|
|
|
25
23
|
```sh
|
|
26
24
|
npx @intentius/behold demo # copies the example to ./behold-demo, installs, serves
|
|
27
|
-
# → http://localhost:4600
|
|
25
|
+
# → http://localhost:4600. Blue = declared; click Deploy, watch it turn green
|
|
28
26
|
```
|
|
29
27
|
|
|
30
28
|
The copied project is yours: edit its source and watch the graph change live.
|
|
31
|
-
There's a whole catalog
|
|
29
|
+
There's a whole catalog, and `behold demo --list` names the rest (`behold demo
|
|
32
30
|
k8s` stands the same loop up on a throwaway k3d cluster: runtime Pods, field
|
|
33
|
-
ownership; `behold demo argo-estate` needs nothing at all
|
|
31
|
+
ownership; `behold demo argo-estate` needs nothing at all, being a three-project
|
|
34
32
|
Argo CD estate, declared only, so it runs where Docker doesn't; `behold demo
|
|
35
33
|
choudoufu-estate` is four tag-owned OpenTofu estates served composed, where a
|
|
36
|
-
card's colour is **ownership**
|
|
34
|
+
card's colour is **ownership** (bound, unowned, pending) rather than "nothing
|
|
37
35
|
changed": a resource whose attributes drifted out of band still carries its
|
|
38
36
|
markers and is still green, so attribute drift is a separate read you ask for
|
|
39
37
|
with ⌘K → "Re-check live with plan"; `behold demo carve` is the Terraform peel
|
|
40
|
-
walkthrough, below).
|
|
41
|
-
|
|
38
|
+
walkthrough, below). Two more need no Docker either: `behold demo
|
|
39
|
+
terraform-estate` draws a multi-root Terraform estate boxed by root, and shows
|
|
40
|
+
what behold refuses to infer, since one root reads a key the other declares and
|
|
41
|
+
no edge is drawn between them; `behold demo flux-estate` is a GitOps estate on
|
|
42
|
+
k3d, a Flux control plane plus two app projects composed. `behold demo fountain`
|
|
43
|
+
clones a real self-hosted app onto a throwaway cluster. Every loaded demo lands
|
|
44
|
+
in the panel's recents, so switching between them is the Scope tab.
|
|
42
45
|
|
|
43
46
|
Working in a checkout rather than an npm install? There is a second catalog,
|
|
44
|
-
`workbench.json`, which is this checkout's and is not shipped
|
|
47
|
+
`workbench.json`, which is this checkout's and is not shipped. It is
|
|
45
48
|
deliberately absent from package.json's `files`, so an npm install has
|
|
46
49
|
`demos.json` and nothing else. It holds the eleven internal estates behold is
|
|
47
50
|
developed against, named by relative path from this repo's root, so an entry
|
|
@@ -49,16 +52,16 @@ whose sibling checkout you don't have says so in `--list` instead of failing
|
|
|
49
52
|
halfway: chant's own two examples and water park's `access/` Terraform roots
|
|
50
53
|
served in place, `../fountain-ops` on its own k3d cluster, the live-mv
|
|
51
54
|
workbench's four estates, three `estate-gen` cohorts, and the generated
|
|
52
|
-
terralith at scale 1 and 4
|
|
55
|
+
terralith at scale 1 and 4, greenfield and adopted from a stock `terraform
|
|
53
56
|
apply` by a `live-import` you run by hand. `just example terralith-4` serves
|
|
54
57
|
one (`just example name="<entry>"`); each that boots a substrate brings up its
|
|
55
58
|
own scratch emulator and leaves a `scripts/down.sh` in the target that removes
|
|
56
59
|
it. `just e2e-workbench` loads the whole catalog and asserts each graph.
|
|
57
60
|
|
|
58
61
|
The catalog is in the panel too (#268): the Scope tab's switcher lists every
|
|
59
|
-
bundled demo under your recents, one click
|
|
60
|
-
|
|
61
|
-
install
|
|
62
|
+
bundled demo under your recents, one click from any served project. Demos
|
|
63
|
+
whose prerequisites are missing stay visible and disabled, saying what to
|
|
64
|
+
install; one that would clone from the network says so on the button.
|
|
62
65
|
|
|
63
66
|
Already have a chant project?
|
|
64
67
|
|
|
@@ -71,86 +74,67 @@ npx @intentius/behold serve . --env prod --poll 30 # live drift overlay
|
|
|
71
74
|
|
|
72
75
|
`behold doctor` is the first thing to run on a project behold hasn't seen: one
|
|
73
76
|
line each for the project's kind, its own chant install and version, declared
|
|
74
|
-
lexicons,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
lexicons, which of them can be aimed at an emulator, the envs the picker will
|
|
78
|
+
infer, the kube context chant binds versus your ambient one, substrate
|
|
79
|
+
readiness, committed Ops, and a line per non-chant member kind it found. Each is
|
|
80
|
+
pass, warn or fail with a one-line fix. Nothing is started and nothing changes.
|
|
81
|
+
The exit code is non-zero only when something would actually stop behold serving
|
|
82
|
+
the project well, so CI can gate on it. `--json` for scripts and agents.
|
|
83
|
+
|
|
84
|
+
Driving it from an agent or script? `GET /api` is the front door. It lists the
|
|
85
|
+
graph and action routes with a one-line description each, and
|
|
86
|
+
`GET /api/doctor?reads=1` answers what the last reads cost.
|
|
81
87
|
[AGENTS.md](./AGENTS.md) (shipped in the package) is the read/act contract.
|
|
82
88
|
|
|
83
|
-
## Preview:
|
|
89
|
+
## Preview: one project's graph, nothing set up
|
|
84
90
|
|
|
85
91
|
`behold preview` is the quick way to look at a chant project's graph in a
|
|
86
|
-
browser at one port.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
```sh
|
|
90
|
-
npm install
|
|
91
|
-
npm run dev -- preview # → http://localhost:4600, cwd as the project
|
|
92
|
-
npm run dev -- preview ../my-project # → someone else's project
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
With no path, it opens the **current directory** — run it from inside your chant
|
|
96
|
-
project. Pass a path to look at another one.
|
|
97
|
-
|
|
98
|
-
behold also ships a turnkey **demo**: the whole live experience running Loom on a
|
|
99
|
-
local [Floci](https://github.com/lex00/floci) emulator, no cloud account and
|
|
100
|
-
nothing to configure. Opt in with `--emulator`:
|
|
92
|
+
browser at one port. No env, no emulator, no config: it opens the project you
|
|
93
|
+
point it at.
|
|
101
94
|
|
|
102
95
|
```sh
|
|
103
|
-
|
|
96
|
+
npx @intentius/behold preview # the current directory
|
|
97
|
+
npx @intentius/behold preview ../my-project # somewhere else
|
|
104
98
|
```
|
|
105
99
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
Docker+Floci, no arbitrary-project switching). If Floci isn't up, **Bring up** on
|
|
110
|
-
its substrate pill boots the emulator and deploys Loom. Needs Docker.
|
|
100
|
+
With no path it opens the **current directory**, so running it from inside your
|
|
101
|
+
project is the shortest thing that works. From a checkout the same commands run
|
|
102
|
+
through `npm run dev -- preview`.
|
|
111
103
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
radial layout — where _logical_ is a traditional AWS architecture diagram: nested
|
|
115
|
-
VPC/subnet ⊃ component boxes, CIDRs as labels, one headline resource per
|
|
116
|
-
composite), watch live per-component status, read the reconcile plan, inspect any
|
|
117
|
-
node, and **deploy to the emulator** — the full observe → reconcile → apply,
|
|
118
|
-
Apply all, and a one-click Reset.
|
|
104
|
+
To colour the graph by what is actually deployed, name an environment with
|
|
105
|
+
`serve . --env prod`, below.
|
|
119
106
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
with your own `--env` and creds.
|
|
107
|
+
<sub>`preview`/`export` also take an `--emulator` flag, a v0.1.0-era turnkey path
|
|
108
|
+
for one specific demo project. `behold demo` and `serve --local` supersede it for
|
|
109
|
+
every purpose; it is kept for compatibility and is not documented further.</sub>
|
|
124
110
|
|
|
125
|
-
## Export
|
|
111
|
+
## Export and host: a shareable, interactive snapshot
|
|
126
112
|
|
|
127
113
|
`behold export` freezes whatever estate you're looking at into a **self-contained
|
|
128
|
-
static folder** that any static host can serve
|
|
129
|
-
interactive
|
|
130
|
-
resources
|
|
131
|
-
|
|
114
|
+
static folder** that any static host can serve. Read-only, and fully
|
|
115
|
+
interactive. Pan/zoom, the zoom dial (components, logical, composites,
|
|
116
|
+
resources, attributes, plus the ops track and the apply order where the project
|
|
117
|
+
has them), radial layout, the inspect pane, and the env/tier pickers all work
|
|
118
|
+
client-side; there's no live observe or deploy.
|
|
132
119
|
|
|
133
120
|
```sh
|
|
134
121
|
npm run dev -- export --out ./behold-export # defaults to cwd, like preview
|
|
135
122
|
# or someone else's project, with its live overlay:
|
|
136
123
|
# npm run dev -- export <project> --env <name> --out ./behold-export
|
|
137
|
-
# or the turnkey Loom-on-Floci demo:
|
|
138
|
-
# npm run dev -- export ../loomster --emulator --out ./behold-export
|
|
139
124
|
npx serve ./behold-export # → open it, no backend running
|
|
140
125
|
```
|
|
141
126
|
|
|
142
127
|
Like `preview`, `export` defaults to the current directory and stays plain (no
|
|
143
128
|
env, no emulator) unless you ask. `--env <name>` turns on that project's live
|
|
144
|
-
overlay for the snapshot
|
|
145
|
-
as `preview --emulator`, for exporting that demo.
|
|
129
|
+
overlay for the snapshot.
|
|
146
130
|
|
|
147
131
|
It captures every read endpoint for the whole lens matrix (each env/tier × zoom ×
|
|
148
|
-
radial) in-process
|
|
149
|
-
is byte-identical to live.
|
|
150
|
-
|
|
132
|
+
radial) in-process, through the exact same handlers the live server runs, so a
|
|
133
|
+
snapshot is byte-identical to live. The live app can't run on a Worker, since it
|
|
134
|
+
needs Docker and a chant subprocess; the pre-baked export can.
|
|
151
135
|
|
|
152
|
-
The bundle is **deploy-ready for Cloudflare
|
|
153
|
-
assets-only `wrangler.jsonc` (no server code, pure static)
|
|
136
|
+
The bundle is **deploy-ready for Cloudflare**, since `behold export` writes an
|
|
137
|
+
assets-only `wrangler.jsonc` (no server code, pure static):
|
|
154
138
|
|
|
155
139
|
```sh
|
|
156
140
|
cd ./behold-export && npx wrangler deploy # → https://<name>.<account>.workers.dev
|
|
@@ -158,18 +142,17 @@ cd ./behold-export && npx wrangler deploy # → https://<name>.<account>.wor
|
|
|
158
142
|
|
|
159
143
|
Set the Worker name with `--name`, or edit `wrangler.jsonc`. Auth via
|
|
160
144
|
`wrangler login` or `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` (same as any
|
|
161
|
-
Workers deploy).
|
|
162
|
-
|
|
145
|
+
Workers deploy). GitHub Pages, S3, nginx and Cloudflare Pages
|
|
146
|
+
(`wrangler pages deploy .`) all work too.
|
|
163
147
|
|
|
164
|
-
## Try it
|
|
148
|
+
## Try it: your first apply, no cloud account
|
|
165
149
|
|
|
166
150
|
The bundled `example-writes` is one S3 bucket. `serve --local` boots *that
|
|
167
151
|
project's own* local emulator (Floci, via Docker, generically through
|
|
168
|
-
`chant emulator up
|
|
169
|
-
you a **▶ Deploy (floci-apply)** button
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
`behold --help` for how the two relate:
|
|
152
|
+
`chant emulator up`, chant #920), points behold's live overlay at it, and gives
|
|
153
|
+
you a **▶ Deploy (floci-apply)** button on the panel's **Deploy** tab that
|
|
154
|
+
deploys to it, with no AWS account and no creds. It works for any
|
|
155
|
+
emulator-backed lexicon rather than one demo project.
|
|
173
156
|
|
|
174
157
|
```sh
|
|
175
158
|
npm install
|
|
@@ -179,30 +162,31 @@ npm run demo # installs example-writes' deps, then serves it with --local
|
|
|
179
162
|
|
|
180
163
|
<sub>(or by hand: `npm install --prefix example-writes && npm run dev -- serve example-writes --local --env prod`)</sub>
|
|
181
164
|
|
|
182
|
-
1. The graph shows the bucket
|
|
183
|
-
2. Click **▶ Deploy (floci-apply)**
|
|
165
|
+
1. The graph shows the bucket and its TLS policy in **blue** (declared, not yet deployed).
|
|
166
|
+
2. Click **▶ Deploy (floci-apply)** on the panel's **Deploy** tab (or ⌘K → "Deploy: Sync"). The
|
|
184
167
|
now-line streams Build → Apply → Verify; the bucket is created in the emulator
|
|
185
168
|
via the CloudFormation API.
|
|
186
|
-
3. The nodes flip **green (managed)
|
|
169
|
+
3. The nodes flip **green (managed)**: behold's overlay observed the live emulator.
|
|
187
170
|
|
|
188
|
-
No Docker running? behold still serves the source graph and tells you to start
|
|
189
|
-
it
|
|
171
|
+
No Docker running? behold still serves the source graph and tells you to start
|
|
172
|
+
it, rather than dying on you.
|
|
190
173
|
|
|
191
174
|
**Real AWS.** The same project's **▶ Deploy** button starts its `ApplyOp` against a
|
|
192
175
|
real account: `npm run dev -- serve example-writes --env prod` (needs AWS
|
|
193
|
-
credentials). What the
|
|
176
|
+
credentials). What the Deploy tab offers depends on what the project committed: a
|
|
194
177
|
committed `ApplyOp` gets the **▶ Deploy (<op>)** button (plus **Approve** when
|
|
195
178
|
gated); a project with only components gets **▶ Deploy…**, which opens the dial's
|
|
196
179
|
component picker; **Adopt** appears per foreign node (`ReconcileOp`); every other
|
|
197
180
|
Op runs from ⌘K (**Run: \<name\>**). Full walkthrough:
|
|
198
181
|
**[example-writes/README.md](example-writes/README.md)**.
|
|
199
182
|
|
|
200
|
-
## The k3d demo
|
|
183
|
+
## The k3d demo: `behold demo k8s`
|
|
201
184
|
|
|
202
|
-
`npm run demo:k8s` is the k8s analogue
|
|
185
|
+
`behold demo k8s` (or `npm run demo:k8s` from a checkout) is the k8s analogue
|
|
186
|
+
of the Floci demo above: it brings up a
|
|
203
187
|
local, single-node [k3d](https://k3d.io) cluster (Docker only, no cloud
|
|
204
|
-
account), then serves the bundled `example-k8s`
|
|
205
|
-
Service
|
|
188
|
+
account), then serves the bundled `example-k8s` (an nginx Deployment and
|
|
189
|
+
Service) with `--local`. Same mechanism, same shape: declared-not-deployed
|
|
206
190
|
(blue) → click **Run** on `k3d-apply` → managed (green), server-side applied
|
|
207
191
|
with chant's own field manager. Ctrl-C tears the cluster back down.
|
|
208
192
|
|
|
@@ -223,48 +207,32 @@ including the exact commands and what each state looks like over the API:
|
|
|
223
207
|
- The vizes only read (`chant graph`, snapshots, Temporal history).
|
|
224
208
|
- Actions don't mutate directly. Sync starts your `ApplyOp`; Adopt starts your
|
|
225
209
|
`ReconcileOp` (opens a PR a human merges). behold *triggers* Ops you committed,
|
|
226
|
-
running on your executor
|
|
210
|
+
running on your executor. It holds no apply creds.
|
|
227
211
|
- Two write gestures, both human-confirmed: **Apply** (gate signal) and **Open PR**
|
|
228
212
|
(merge). Authority stays in your source and your worker, never in behold.
|
|
229
|
-
- The first product is **read-only, full stop**: the mixed graph + drift + source
|
|
230
|
-
deep-links. Writes are a later, opt-in layer.
|
|
231
213
|
|
|
232
214
|
## Why a Node service (not an edge function)
|
|
233
215
|
|
|
234
216
|
The live path (`chant graph --live --overlay`, `chant lifecycle plan`) shells
|
|
235
217
|
`kubectl`/`aws`/`az`/the Temporal client and holds cloud creds. That needs a real
|
|
236
|
-
process, so behold is a Node service you run where your creds live
|
|
237
|
-
`argocd-server`, not like a hosted SaaS. Read-only means it only needs **read**
|
|
218
|
+
process, so behold is a Node service you run where your creds live. Read-only means it only needs **read**
|
|
238
219
|
roles (describe/list), so it's least-privilege to run.
|
|
239
220
|
|
|
240
221
|
## Agent-drivable, on chant's MCP
|
|
241
222
|
|
|
242
223
|
behold is drivable by an agent, and leans on chant's MCP rather than reinventing it:
|
|
243
224
|
|
|
244
|
-
- **Reads
|
|
225
|
+
- **Reads.** `lifecycle-diff`, `lifecycle-snapshot`, plus behold's own read API
|
|
245
226
|
(the overlay graph as JSON, blast radius, frame diffs).
|
|
246
|
-
- **Delegated actions
|
|
227
|
+
- **Delegated actions.** The writes *are* chant MCP Op tools: `op-run` starts an
|
|
247
228
|
`ApplyOp`/`ReconcileOp`, `op-signal` approves a gate, `op-status`/`op-report`
|
|
248
229
|
watch it. So an agent "syncing prod" is `op-run prod-apply` then
|
|
249
|
-
`op-signal prod-apply approve-apply
|
|
230
|
+
`op-signal prod-apply approve-apply`: gated, durable, no creds in behold.
|
|
250
231
|
|
|
251
232
|
behold's value over raw MCP is the live spatial + temporal view and the coupling
|
|
252
233
|
between them; the underlying capabilities are chant's, exposed the same way to a
|
|
253
234
|
human and an agent. See [AGENTS.md](./AGENTS.md).
|
|
254
235
|
|
|
255
|
-
## Status
|
|
256
|
-
|
|
257
|
-
Bootstrap. `behold serve <project>` renders the **source** mixed-substrate graph
|
|
258
|
-
(cross-lexicon edges are real today — verified in chant core) in a browser, with
|
|
259
|
-
click-to-inspect and source deep-links.
|
|
260
|
-
|
|
261
|
-
Both read paths work: `/api/graph` (the source mixed-substrate graph) and
|
|
262
|
-
`/api/overlay` (the source-anchored **live drift** graph — declared topology kept,
|
|
263
|
-
nodes coloured managed/foreign/pending; needs chant ≥ 0.18.1 and cloud creds).
|
|
264
|
-
|
|
265
|
-
Not yet: the deployment-lanes timeline and the delegated actions. See the concept
|
|
266
|
-
notes and the issue set below.
|
|
267
|
-
|
|
268
236
|
## Usage
|
|
269
237
|
|
|
270
238
|
```sh
|
|
@@ -279,22 +247,72 @@ Then open http://localhost:4600. With `--env`, the SPA shows the live overlay;
|
|
|
279
247
|
without it, the source graph.
|
|
280
248
|
|
|
281
249
|
**Live updates.** The server watches the served project's source and pushes a
|
|
282
|
-
refresh over SSE (`/api/events`) when a `.ts` file changes
|
|
283
|
-
graph updates
|
|
250
|
+
refresh over SSE (`/api/events`) when a `.ts` file changes, so you edit your
|
|
251
|
+
infra and the graph updates with no reload. Add `--poll <secs>` (with `--env`) to also re-query live
|
|
284
252
|
drift on an interval and push updates when a node's status changes:
|
|
285
253
|
|
|
286
254
|
```sh
|
|
287
|
-
behold serve ./infra --env prod --poll 30 #
|
|
255
|
+
behold serve ./infra --env prod --poll 30 # wait 30s between completed drift sweeps
|
|
288
256
|
```
|
|
289
257
|
|
|
258
|
+
**Read budget.** HTTP observations, background polling, and frame captures share
|
|
259
|
+
one Chant subprocess budget: two reads at once by default (one on a one-CPU
|
|
260
|
+
host). `BEHOLD_ESTATE_CONCURRENCY` overrides that process-wide limit. Up to 64
|
|
261
|
+
distinct reads can wait; excess reads fail explicitly rather than growing an
|
|
262
|
+
unbounded queue. Identical in-flight reads share work only when the project,
|
|
263
|
+
resolved Chant, source stamp, argv, and effective environment match. Source
|
|
264
|
+
watcher invalidation also separates new requests from work begun before an edit.
|
|
265
|
+
|
|
266
|
+
**What is cached, and what drops it.** A member's source IR is cached under its
|
|
267
|
+
source stamp, resolved chant and graph options (`src/member-ir.ts`), because
|
|
268
|
+
~800ms of every `chant` invocation is module load that has nothing to do with the
|
|
269
|
+
member's content. A member's completed live overlay document is cached too
|
|
270
|
+
(`src/overlay-ir.ts`), so that a re-render such as collapsing a box is a
|
|
271
|
+
re-render rather than a second pass over the account. That second cache is the
|
|
272
|
+
stated exception to "a live read is never cached", and its header names
|
|
273
|
+
everything that drops an entry: `POST /api/refresh` ("Re-check live"),
|
|
274
|
+
`GET /api/overlay?plan=1` ("Re-check live with plan"), the capture that ends an
|
|
275
|
+
Op run, and the member's source moving. What it cannot notice is a change made to
|
|
276
|
+
the account by something that is not behold, which is what the re-check rows are
|
|
277
|
+
for.
|
|
278
|
+
|
|
279
|
+
A running read has a 180-second deadline, configurable with
|
|
280
|
+
`BEHOLD_READ_TIMEOUT_MS`. A disconnected GET releases its subscription; when no
|
|
281
|
+
callers remain, the read is canceled. On Unix, cancellation stops the whole
|
|
282
|
+
npm/tsx/Node process group, escalating from TERM to KILL after one second. On
|
|
283
|
+
Windows only the direct child is terminated. Delegated writes retain their
|
|
284
|
+
existing lifecycle and are never deduplicated as reads.
|
|
285
|
+
|
|
286
|
+
The browser runs one refresh at a time and collapses intervening notifications
|
|
287
|
+
into one follow-up. It no longer starts additional reads at 3/8/15-second offsets.
|
|
288
|
+
Polling uses the same estate namespace bindings as the HTTP overlay and reuses
|
|
289
|
+
the primary member's observation for lanes capture. Slow sweeps extend the poll
|
|
290
|
+
period; they do not overlap the next sweep. These bounds control duplicate work;
|
|
291
|
+
they do not eliminate Chant's underlying live discovery cost.
|
|
292
|
+
|
|
293
|
+
**What a read cost.** Every scheduled chant read is filed with its running time,
|
|
294
|
+
its outcome and which side of the source/live split it sits on.
|
|
295
|
+
`GET /api/doctor` serves the same report `behold doctor` prints with that ledger
|
|
296
|
+
on it; `GET /api/doctor?reads=1` is the cheap half: counters, the per-member
|
|
297
|
+
samples, `inFlight` (running and queued), and `unmeasured`, the count of served
|
|
298
|
+
members the ledger is blind to because they spawn their own binary rather than
|
|
299
|
+
chant. The loading scrim shows the slowest recent member, since an estate read
|
|
300
|
+
finishes when its last member does.
|
|
301
|
+
|
|
302
|
+
**The picture can arrive in pieces.** `GET /api/overlay?progressive=1` answers at
|
|
303
|
+
once with the estate composed from each member's source, every card marked
|
|
304
|
+
pending, then streams one SSE frame per member as its own live read lands.
|
|
305
|
+
Opt-in and never the default: the blocking `/api/overlay` is what agents read and
|
|
306
|
+
what `behold export` captures, and it is unchanged.
|
|
307
|
+
|
|
290
308
|
behold shells the **project's own** chant (resolved from the project's
|
|
291
|
-
`node_modules` first), so the project decides the chant version
|
|
292
|
-
`@intentius/chant ^0.18.1` or later for the live overlay
|
|
293
|
-
nothing before that fix
|
|
309
|
+
`node_modules` first), so the project decides the chant version. Pin it to
|
|
310
|
+
`@intentius/chant ^0.18.1` or later for the live overlay; `graph --live` observed
|
|
311
|
+
nothing before that fix.
|
|
294
312
|
|
|
295
313
|
## Terraform carve-out: `behold carve <report.json>`
|
|
296
314
|
|
|
297
|
-
`chant carve advise` ranks a Terraform estate by **peelability
|
|
315
|
+
`chant carve advise` ranks a Terraform estate by **peelability**: how cleanly
|
|
298
316
|
each resource could be carved into native chant source. `behold carve` draws
|
|
299
317
|
that ranking: one card per resource, three panels (carve now / boundary work /
|
|
300
318
|
leave in Terraform) on the same `attrs._status` drift palette every other view
|
|
@@ -308,9 +326,13 @@ curl localhost:4600/api/carve # the raw report, for agents
|
|
|
308
326
|
|
|
309
327
|
behold parses no HCL and needs no Terraform tooling: the report **is** the
|
|
310
328
|
contract. A file that isn't a peelability report is refused with a structured
|
|
311
|
-
`{error, code: "carve-report", remedy}
|
|
312
|
-
never a blank graph. See
|
|
313
|
-
|
|
329
|
+
`{error, code: "carve-report", remedy}`, in the terminal and from the routes,
|
|
330
|
+
never a blank graph. See [the carve lens](https://intentius.io/behold/using/carve/) and
|
|
331
|
+
[the Terraform estate page](https://intentius.io/behold/using/terraform/) for the
|
|
332
|
+
other half of that lane: Terraform is a member
|
|
333
|
+
kind now (`MEMBER_KINDS = ["chant", "choudoufu", "terraform"]`), so an estate of
|
|
334
|
+
`.tf` files draws beside a chant project, and carve state is read from chant's
|
|
335
|
+
own `<address>.carve.json` manifests.
|
|
314
336
|
|
|
315
337
|
### The walkthrough: `behold demo carve`
|
|
316
338
|
|
|
@@ -318,41 +340,48 @@ never a blank graph. See `docs/using/carve` and issue #230 for the roadmap
|
|
|
318
340
|
npx @intentius/behold demo carve # no Docker, no cloud, no terraform binary
|
|
319
341
|
```
|
|
320
342
|
|
|
321
|
-
Copies a half-migrated estate
|
|
322
|
-
both describing the same AWS account
|
|
343
|
+
Copies a half-migrated estate (a small chant project beside a Terraform one,
|
|
344
|
+
both describing the same AWS account), installs the chant it will shell, runs
|
|
323
345
|
`chant carve advise` over the copy, and opens the banded graph with a six-step
|
|
324
346
|
stepper on the panel's **Carve** tab:
|
|
325
347
|
|
|
326
|
-
1. **Advise
|
|
327
|
-
2. **Pick
|
|
348
|
+
1. **Advise.** The bands, with what each one means.
|
|
349
|
+
2. **Pick.** Click a green card. The inspect pane shows the score arithmetic;
|
|
328
350
|
the step names the boundary the cut crosses.
|
|
329
|
-
3. **Emit
|
|
351
|
+
3. **Emit.** Runs `chant carve emit --state --select <addr>` into
|
|
330
352
|
`app/carveout/` in the copy, then shows the emitted chant source and the
|
|
331
353
|
`chant lint` result.
|
|
332
|
-
4. **Bridge
|
|
354
|
+
4. **Bridge.** Runs `chant carve bridge` (never `--apply-rewrites`) and renders
|
|
333
355
|
the proposed data source, the rewired survivors and the patch.
|
|
334
|
-
5. **Handoff
|
|
356
|
+
5. **Handoff.** The runbook's commands with copy buttons, and **not** a button:
|
|
335
357
|
`terraform state rm` and `terraform apply` change who owns a live resource,
|
|
336
358
|
so they stay yours to run. The panel says so.
|
|
337
|
-
6. **Done
|
|
359
|
+
6. **Done.** The card is marked chant-owned at the observe position;
|
|
338
360
|
`terraform import` reverses all of it.
|
|
339
361
|
|
|
340
362
|
The Emit step reports `chant lint`, not `chant build`: `build` fails on the
|
|
341
|
-
emitted bucket (scored 84 by the advisor) on one rule
|
|
342
|
-
bucket policy the source Terraform never declared
|
|
343
|
-
reason.
|
|
363
|
+
emitted bucket (scored 84 by the advisor) on one rule: WAW042, a TLS-deny
|
|
364
|
+
bucket policy the source Terraform never declared. The panel links the
|
|
365
|
+
reason. [example-carve/README.md](example-carve/README.md) has the estate's full story, the band table,
|
|
344
366
|
and the offline/`--live` split.
|
|
345
367
|
|
|
346
|
-
behold writes only into the demo copy it made
|
|
347
|
-
else. Your Terraform is never edited; see AGENTS.md, "Invariant".
|
|
368
|
+
behold writes only into `app/carveout/` inside the demo copy it made, and
|
|
369
|
+
nothing else. Your Terraform is never edited; see AGENTS.md, "Invariant".
|
|
348
370
|
|
|
349
|
-
## Configuration
|
|
371
|
+
## Configuration: `.behold.json`
|
|
350
372
|
|
|
351
373
|
An optional `.behold.json` in the served project's root is **behold's own**
|
|
352
|
-
config
|
|
353
|
-
tier picker) don't leak into chant's.
|
|
374
|
+
config, kept separate from `chant.config.ts` so behold's concerns (like the
|
|
375
|
+
tier picker) don't leak into chant's. Three keys: `tiers` below, `executor`
|
|
376
|
+
(which forge deploys an environment), and `members`, spelled `[{ "dir": "x", "kind":
|
|
377
|
+
"chant" | "choudoufu" | "terraform" }]`, how an estate root names what it
|
|
378
|
+
composes, fail-closed on a kind behold has no reader for. `members` is
|
|
379
|
+
deprecated: a chant workspace declaration (`chant.workspace.json`) lists an
|
|
380
|
+
estate's members for every reader, and beside one, `.behold.json`'s list is
|
|
381
|
+
ignored. `behold doctor --fix` writes the declaration from it (see
|
|
382
|
+
[Serve a chant workspace](https://intentius.io/behold/using/workspace/)). First, the
|
|
354
383
|
project's deploy-**tier** axis, a dimension orthogonal to `environment` (chant
|
|
355
|
-
has no native tier concept
|
|
384
|
+
has no native tier concept; it is entirely a project convention, e.g. Loom's
|
|
356
385
|
components branching on an env-conditioned `namingParams.tier`):
|
|
357
386
|
|
|
358
387
|
```json
|
|
@@ -364,17 +393,17 @@ components branching on an env-conditioned `namingParams.tier`):
|
|
|
364
393
|
}
|
|
365
394
|
```
|
|
366
395
|
|
|
367
|
-
- `envVar`
|
|
396
|
+
- `envVar` is the env var name the project's source branches on; behold sets it
|
|
368
397
|
for the chant shell-out whenever a tier is picked (`?tier=` → this var, never
|
|
369
398
|
a chant CLI flag).
|
|
370
|
-
- `values`
|
|
399
|
+
- `values` is the tier picker's options.
|
|
371
400
|
|
|
372
401
|
**No `.behold.json` (or no `tiers` key) → no tier axis:** the picker doesn't
|
|
373
|
-
render and the graph loads with no tier selected
|
|
374
|
-
that doesn't opt in. There's no other tier config surface (not
|
|
402
|
+
render and the graph loads with no tier selected, which is the default for any
|
|
403
|
+
project that doesn't opt in. There's no other tier config surface (not
|
|
375
404
|
`chant.config.ts`, not an env var behold guesses the name of).
|
|
376
405
|
|
|
377
|
-
### Deploy executors
|
|
406
|
+
### Deploy executors: `executor`
|
|
378
407
|
|
|
379
408
|
The same file can designate which forge deploys an environment (#165):
|
|
380
409
|
|
|
@@ -392,7 +421,7 @@ tell them apart. For a designated environment the Deploy button dispatches that
|
|
|
392
421
|
workflow through your own `gh` login and follows the run on the dial; a local
|
|
393
422
|
apply, a committed ApplyOp for that environment, and auto-sync are all refused
|
|
394
423
|
(`409 executor-forge`), and rollback is withheld. Any approval the workflow's
|
|
395
|
-
GitHub environment requires is granted on GitHub
|
|
424
|
+
GitHub environment requires is granted on GitHub by your identity there. The
|
|
396
425
|
dial links the run's page and offers nothing else, because behold holds no
|
|
397
426
|
identity that could clear a forge gate. A designation behold cannot honour (a
|
|
398
427
|
typo'd forge, a missing workflow, one without `workflow_dispatch`) disables
|
|
@@ -401,15 +430,15 @@ it on your machine. The workflow is read from the `.github/workflows` of the
|
|
|
401
430
|
repository the project belongs to, so a project that is one directory of a
|
|
402
431
|
monorepo works as-is.
|
|
403
432
|
|
|
404
|
-
A dispatched run's id is kept under `~/.behold/ci-runs
|
|
405
|
-
state
|
|
433
|
+
A dispatched run's id is kept under `~/.behold/ci-runs/`, the operator's own
|
|
434
|
+
state outside the project, so a behold restarted mid-deploy re-adopts the run
|
|
406
435
|
and keeps following it. A follow whose stream dies is reported as `lost`, never
|
|
407
436
|
as a verdict; `just e2e-ci-github` proves the whole contract against GitHub.
|
|
408
437
|
|
|
409
|
-
### The hand-layout sidecar
|
|
438
|
+
### The hand-layout sidecar: `.behold/layout.json`
|
|
410
439
|
|
|
411
440
|
dagre places your nodes; you can move them. Drag a card, resize a containment
|
|
412
|
-
box, and the offsets are remembered per project
|
|
441
|
+
box, and the offsets are remembered per project and lens: in `localStorage`
|
|
413
442
|
first, and (when the served project is writable) in a `.behold/layout.json`
|
|
414
443
|
sidecar beside it, so a layout is shareable, reviewable in a diff, and honoured
|
|
415
444
|
by `behold export`:
|
|
@@ -419,14 +448,14 @@ by `behold export`:
|
|
|
419
448
|
```
|
|
420
449
|
|
|
421
450
|
This is the **only** file behold writes inside a served project. It stores
|
|
422
|
-
deltas
|
|
423
|
-
on top of it
|
|
451
|
+
deltas rather than absolute positions, so the graph stays chant's and your
|
|
452
|
+
layout sits on top of it. A delta whose node has left the estate is dropped.
|
|
424
453
|
`POST /api/layout` refuses politely in preview mode, during a static-export
|
|
425
454
|
capture, on a read-only directory, and above its size caps. `↺ layout` in the
|
|
426
455
|
graph clears the current lens on both tiers.
|
|
427
456
|
|
|
428
457
|
**Gitignore it.** `.behold.json` (above) is config and belongs in the repo;
|
|
429
|
-
`.behold/` is per-user state
|
|
458
|
+
`.behold/` is per-user state, one person's arrangement of the picture, so add
|
|
430
459
|
it to the served project's `.gitignore` unless you actually want to share and
|
|
431
460
|
review a layout:
|
|
432
461
|
|
|
@@ -436,30 +465,46 @@ review a layout:
|
|
|
436
465
|
|
|
437
466
|
## Layout
|
|
438
467
|
|
|
468
|
+
A shape rather than a manifest. `src/` is ~85 modules with one concern each, and
|
|
469
|
+
[AGENTS.md](./AGENTS.md) is the map that stays current.
|
|
470
|
+
|
|
439
471
|
```
|
|
440
472
|
src/
|
|
441
|
-
cli.ts
|
|
442
|
-
server.ts
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
473
|
+
cli.ts the verbs: serve, preview, demo, doctor, export, carve
|
|
474
|
+
server.ts the Hono API + static SPA, every route read and delegated
|
|
475
|
+
|
|
476
|
+
the read path
|
|
477
|
+
chant.ts shells the PROJECT's own chant; every read is scheduled
|
|
478
|
+
read-scheduler.ts one process-wide budget, in-flight sharing, cancellation
|
|
479
|
+
read-stats.ts what each read cost (/api/doctor)
|
|
480
|
+
member-ir.ts per-member source cache; overlay-ir.ts, the live one
|
|
481
|
+
estate.ts N members composed into one graph (pinhole's composeStacks)
|
|
482
|
+
member-kind.ts what a member can be: chant, choudoufu, terraform
|
|
483
|
+
|
|
484
|
+
the lenses (pure IR -> IR, no subprocess)
|
|
485
|
+
logical*.ts one topology projection per substrate
|
|
486
|
+
ops-lens.ts declared Ops as a phase track; run-playhead.ts paints a run
|
|
487
|
+
carve-lens.ts a peelability report; stack-order.ts, chant's apply order
|
|
488
|
+
terraform-lens.ts collapse-lens.ts, edgeless.ts
|
|
489
|
+
|
|
490
|
+
render.ts pinhole painter, IR -> SVG, one pack per lexicon
|
|
491
|
+
web/ the SPA, unbundled ES modules (app.js + ~20 siblings)
|
|
492
|
+
docs/ the published site (Astro/Starlight)
|
|
493
|
+
demos.json the shipped demo catalog; workbench.json, this checkout's
|
|
494
|
+
example-*/ nine bundled projects the demos and e2e serve
|
|
495
|
+
e2e/ twelve acceptance runs, one per substrate lane
|
|
451
496
|
```
|
|
452
497
|
|
|
453
498
|
## The painter
|
|
454
499
|
|
|
455
500
|
behold reuses [pinhole](https://github.com/INTENTIUS/pinhole)'s SVG painter as a
|
|
456
|
-
library
|
|
457
|
-
speaks the overlay vocabulary managed/foreign/pending
|
|
501
|
+
library: a mature renderer with themes, icons, and `_status` drift colouring
|
|
502
|
+
that already speaks the overlay vocabulary managed/foreign/pending. The server lays the IR out
|
|
458
503
|
and paints it with `layoutIr` + `renderSvg` (`src/render.ts`); the SPA inlines the
|
|
459
504
|
SVG and wires click-inspect by `data-node-id` against the IR. pinhole's layout is
|
|
460
|
-
dagre
|
|
505
|
+
dagre, which is pure JS with no native dependency.
|
|
461
506
|
|
|
462
|
-
Where an official mark exists, a node paints it instead of a generic glyph
|
|
507
|
+
Where an official mark exists, a node paints it instead of a generic glyph. A
|
|
463
508
|
Deployment gets the Kubernetes wheel-and-helm heptagon, a Kustomization the
|
|
464
509
|
Flux mark, a `Helm::Release` the Helm wheel. The corpus is vendored under
|
|
465
510
|
`web/icons/` (30 kubernetes/community SVGs, 3 cncf/artwork marks for
|
|
@@ -469,8 +514,8 @@ keyword heuristic rather than a wrong picture.
|
|
|
469
514
|
|
|
470
515
|
dagre's layout is a good first draft, not a final one: drag a card to move
|
|
471
516
|
it, grab a containment box's corner to resize it, and both survive a reload.
|
|
472
|
-
What persists is a delta
|
|
473
|
-
an absolute position
|
|
517
|
+
What persists is a delta (`{dx,dy}` for a card, `{dw,dh}` for a box) rather
|
|
518
|
+
than an absolute position, keyed by `behold.layout.<project>.<lens>`
|
|
474
519
|
(`web/layout-store.js`), so the graph stays chant's and the arrangement on
|
|
475
520
|
top of it is yours. `↺ layout` sits beside `⤢ fit` and shows up only once
|
|
476
521
|
something on the current lens is hand-placed.
|
|
@@ -485,11 +530,11 @@ the inspect pane's declared attributes, observed live state, drift pairs and
|
|
|
485
530
|
field ownership, the op log's JSON lines, and the payload behind an `/api`
|
|
486
531
|
error card.
|
|
487
532
|
|
|
488
|
-
Type splits by purpose:
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
re-derives the whole chrome
|
|
533
|
+
Type splits by purpose: a system mono stack carries node ids, ARNs and
|
|
534
|
+
statuses, and sans carries labels only. Colour comes from 552 Ghostty terminal palettes run
|
|
535
|
+
through an OKLCH-derived token pipeline (`web/theme.js`, the palettes in
|
|
536
|
+
`web/themes.js`), so a theme switch
|
|
537
|
+
re-derives the whole chrome rather than only the graph. A node whose drift
|
|
493
538
|
status just changed pulses once in the colour it became, off under
|
|
494
539
|
`prefers-reduced-motion`.
|
|
495
540
|
|
|
@@ -509,8 +554,8 @@ One server, one SPA: passing an env turns on the live overlay (`/api/overlay`),
|
|
|
509
554
|
omitting it shows the source graph (`/api/graph`). `serve` runs via `tsx` (no build
|
|
510
555
|
step); for the built binary, `just build` then `./bin/behold.js serve <project>`.
|
|
511
556
|
|
|
512
|
-
**Which chant runs.** behold does not bundle chant
|
|
513
|
-
resolved from the served project's `node_modules
|
|
557
|
+
**Which chant runs.** behold does not bundle chant. It *shells* the chant binary
|
|
558
|
+
resolved from the served project's `node_modules`, falling back to its own dep.
|
|
514
559
|
So local testing means installing chant into a project, not into behold. The bundled
|
|
515
560
|
`example/` does exactly that; point `serve` at any real chant project the same way.
|
|
516
561
|
|
|
@@ -525,9 +570,9 @@ behold, serves the example, and asserts the read-only API against a live server.
|
|
|
525
570
|
auto-detects AWS credentials:
|
|
526
571
|
|
|
527
572
|
- **no creds** → asserts `/api/graph` (the source mixed-substrate graph, offline).
|
|
528
|
-
- **AWS creds** → asserts `/api/overlay
|
|
529
|
-
CloudFormation and checks every node carries a drift status
|
|
530
|
-
nothing is deployed is a valid pass, since the point is the live path
|
|
573
|
+
- **AWS creds** → asserts `/api/overlay`, the source-anchored live overlay: it queries
|
|
574
|
+
CloudFormation and checks every node carries a drift status. All `pending` when
|
|
575
|
+
nothing is deployed is a valid pass, since the point is the live path.
|
|
531
576
|
|
|
532
577
|
It's hermetic apart from the chant install and (optionally) the cloud read; the
|
|
533
578
|
server is torn down on exit. `BEHOLD_E2E_PORT` overrides the port.
|
|
@@ -535,16 +580,19 @@ server is torn down on exit. `BEHOLD_E2E_PORT` overrides the port.
|
|
|
535
580
|
## Unit tests
|
|
536
581
|
|
|
537
582
|
```sh
|
|
538
|
-
npm test # vitest
|
|
583
|
+
npm test # vitest, 106 files, including in-process route tests against
|
|
584
|
+
# the real Hono app. No cloud, no cluster.
|
|
539
585
|
```
|
|
540
586
|
|
|
541
|
-
##
|
|
587
|
+
## Upstream
|
|
588
|
+
|
|
589
|
+
behold is a viewer over a compiler, so some of what it cannot do is not its to
|
|
590
|
+
fix. The open asks:
|
|
591
|
+
|
|
592
|
+
- chant **#822**, diff two historical snapshots, which is what a timeline of
|
|
593
|
+
more than the frames behold captures itself would need.
|
|
594
|
+
- chant **#2481**, `chant` costs ~800ms per invocation before it does any work,
|
|
595
|
+
and behold shells it once per member per read.
|
|
596
|
+
- pinhole **#79/#80/#81**, morph-over-time and first-class drift rendering.
|
|
542
597
|
|
|
543
|
-
|
|
544
|
-
status) — done, shipped chant 0.18.31; behold adopted it in M4 (see `src/overlay.ts`).
|
|
545
|
-
- chant **#822** — diff two historical snapshots (feeds the timeline).
|
|
546
|
-
- chant **#513** — compose separate stacks into one IR — done; behold's `composeEstate`
|
|
547
|
-
(`src/estate.ts`, #31) consumes pinhole's `composeStacks` built on it.
|
|
548
|
-
- pinhole **#82** — ship the painter as a library (done; behold consumes it).
|
|
549
|
-
- pinhole **#79/#80/#81** — drive `--live`/`--overlay`, first-class drift rendering, morph-over-time.
|
|
550
|
-
- Concept notes: `~/Documents/research/chant-live-control-plane.md`.
|
|
598
|
+
behold's own work is on the [issue tracker](https://github.com/INTENTIUS/behold/issues).
|