@intentius/behold 0.14.0 → 0.16.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 CHANGED
@@ -54,6 +54,13 @@ preview-locked, and one load runs at a time (409 otherwise).
54
54
  per-node observed state, drift and field ownership; `/api/reconcile?env=`
55
55
  summarizes the pending change set; `/api/substrates` reports substrate
56
56
  readiness; `/api/events` (SSE) pushes `changed`/`op`/`apply`/`run`/`pr`.
57
+ With `?components=1&env=`, each component node carries `_liveStatus` (the
58
+ `chant components status` verdict) and, when the release ledger recorded
59
+ one, `_release` — `{runId, forge, originSource, url?, gitSha, digest,
60
+ timestamp, actor, approver?}` (#165). `url` is present only when the record
61
+ itself carries an address (chant#2045); `originSource: "inferred"` means
62
+ behold read the id's spelling and nothing more, and a `forge: "unknown"` id
63
+ is never resolved to a link on behold's guess.
57
64
  `?ops=1` is the ops lens — the project's declared Ops as a phase track, read
58
65
  from each emitted `dist/ops/<name>/op.json`, with the current run painted
59
66
  over it (`meta.run`, `meta.gate`); `/api/ops/<name>/status` reads that Op's
@@ -164,6 +171,33 @@ and nothing else about the run is behold's to decide: a stream that dies leaves
164
171
  the playhead at the last settled step and says so, and a gate paints as pending
165
172
  only when chant's `gateState` query named it.
166
173
 
174
+ ### The executor contract (#165, #61)
175
+
176
+ `.behold.json` may designate which forge deploys an environment:
177
+
178
+ ```json
179
+ { "executor": { "prod": { "forge": "github", "workflow": "deploy-prod.yml" } } }
180
+ ```
181
+
182
+ It designates the committed WORKFLOW, not just the forge: two environments'
183
+ generated pipelines carry identical job ids, so a picker cannot tell them
184
+ apart, and a contract that names prod must not sit on a guess. For a
185
+ designated env, `POST /api/apply` and a committed ApplyOp's `/api/ops/:name/run`
186
+ answer `409 {code: "executor-forge"}`, auto-sync declines out loud, and the
187
+ only deploy is `POST /api/ci/dispatch?env=<env>`, which runs the designated
188
+ workflow through the operator's own `gh` and follows it on the dial. Any
189
+ approval the workflow's environment requires is granted on the forge by a
190
+ GitHub identity behold does not have; the dial links the run's page and offers
191
+ nothing else. Fail-closed: a designation behold cannot honour (a typo'd forge,
192
+ a missing or non-dispatchable workflow, a forge with no trigger here) disables
193
+ Deploy for that env with the reason on `/api/project`'s `executor` block, and
194
+ never falls back to running it here. `/api/project` reports `{forge, workflow,
195
+ ok, reason?}` per designated env.
196
+
197
+ Without a designation, dispatch picks the committed workflow named for the env
198
+ (`chant-components-<env>`, chant ≥ 0.54.0) and refuses a tie on the older
199
+ job-overlap match rather than letting directory order choose.
200
+
167
201
  ## The operating loop (a strip, a timeline, and a gate that is not the run gate)
168
202
 
169
203
  A project that declares a `ConvergeOp` gets an operator strip on the ops lens:
@@ -257,3 +291,39 @@ own Terraform is not edited either. The only request-derived value is `select`,
257
291
  and it must be an address the served report already ranks — the value that
258
292
  reaches the spawn's argv comes from a closed set read off disk. No cloud write,
259
293
  no Terraform mutation, no edit to anyone's chant source.
294
+
295
+ ## Changing behold (for agents working on this repo)
296
+
297
+ The sections above are about driving a running behold. This one is about
298
+ changing it — the working rules that used to live in a handoff note and now
299
+ live here, with the enforceable ones enforced.
300
+
301
+ - **Branches and merges.** Work on a branch off `main` (an isolated worktree
302
+ when more than one agent is active). `main` is protected by a ruleset: every
303
+ change arrives as a PR, and the `check` job in `ci.yml` must be green before
304
+ it merges. A check that has merely settled is not a pass — read the lines.
305
+ No `gh pr merge --auto`; merge one PR at a time, in order, and rebase the
306
+ next onto what landed.
307
+ - **The local gate** is `just check` (tsc, tests, build). `just test` keeps
308
+ the reporter's full output in `vitest.log`, because #334's one-shot
309
+ first-run failures kept losing the failing file's name — if the suite fails
310
+ once and passes on rerun, the log from the first run is the evidence; attach
311
+ it to #334.
312
+ - **Releasing** is `just release`, after the version-bump PR merged: it tags
313
+ `behold-v<version>`, pushes the tag (which is what runs `release.yml`), and
314
+ waits for npm to show the version. It refuses in every state where a tag
315
+ push would be wrong. Never re-push a `behold-v*` tag by hand — a tag push
316
+ re-fires publish. Tags absent from origin (behold-v0.10.0, 0.10.1) stay
317
+ absent for that reason.
318
+ - **Scratch infrastructure** is governed by `src/scratch.ts`, asserted by
319
+ `scratch.test.ts` across every boot site: anything behold boots is named
320
+ `behold-*`, refuses if the name is taken, tears down only what it created,
321
+ and never binds the shared emulator ports. The protected names — `floci*`,
322
+ `chant-floci*`, the kubemicrovm and fountain k3d clusters, :4566 — are
323
+ listed there, not in prose. A new boot site calls `assertScratch` before
324
+ its spawn and gets a row in the test.
325
+ - **The write boundary** is the Invariant section above. Apply is never a
326
+ button; `/api/carve/apply` does not exist and the route test asserts it
327
+ stays that way.
328
+ - **chant's working rules** (its single-lane checkout, its release path) are
329
+ chant's and live in chant's repo, not here.