@intentius/behold 0.16.0 → 0.17.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
@@ -195,8 +195,24 @@ never falls back to running it here. `/api/project` reports `{forge, workflow,
195
195
  ok, reason?}` per designated env.
196
196
 
197
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.
198
+ (`chant-components-<env>`, chant ≥ 0.54.0); on the older job-overlap match it
199
+ refuses a tie rather than letting directory order choose, and a workflow named
200
+ for another env never stands in. `just e2e-ci-github` proves the contract
201
+ against the real forge (example-ci + `.github/workflows/behold-e2e-dispatch.yml`),
202
+ including the `lost` verdict, which `BEHOLD_CI_FOLLOW_TIMEOUT_MS` and
203
+ `BEHOLD_CI_POLL_FAIL_BUDGET` let a run force on purpose; a lost run is the one
204
+ `POST /api/ci/readopt` re-follows.
205
+
206
+ A dispatched run is followed honestly (#165 §6, PR #350): only GitHub's own
207
+ `completed` + conclusion paints `ok`/`failed`; a poll-failure budget or the
208
+ follow deadline promotes the run to `lost` (chips frozen at last-observed,
209
+ the run possibly still live at its own page), never to a verdict. The adopted
210
+ run id is persisted as one JSON per project under `~/.behold/ci-runs/` — the
211
+ operator's machine-state, outside the project tree, so the Invariant below
212
+ still names the whole in-project write surface. `GET /api/ci/run` reads the
213
+ record; `POST /api/ci/readopt` (also attempted once at boot) re-follows an
214
+ unconcluded run by its saved id, so a restart mid-deploy leaves the run a
215
+ reader instead of nothing.
200
216
 
201
217
  ## The operating loop (a strip, a timeline, and a gate that is not the run gate)
202
218
 
@@ -304,11 +320,13 @@ live here, with the enforceable ones enforced.
304
320
  it merges. A check that has merely settled is not a pass — read the lines.
305
321
  No `gh pr merge --auto`; merge one PR at a time, in order, and rebase the
306
322
  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.
323
+ - **The local gate** is `just check` (tsc, tests, build). Every vitest run
324
+ also writes `.vitest-last.json` (the json reporter, PR #351), and `just
325
+ test` keeps the reporter's full output in `vitest.log` — both gitignored —
326
+ because #334's one-shot first-run failures kept losing the failing file's
327
+ name. If the suite fails once and passes on rerun, those two files from the
328
+ first run are the evidence; attach them to #334 (`jq '.testResults[] |
329
+ select(.status != "passed") | .name' .vitest-last.json` names the file).
312
330
  - **Releasing** is `just release`, after the version-bump PR merged: it tags
313
331
  `behold-v<version>`, pushes the tag (which is what runs `release.yml`), and
314
332
  waits for npm to show the version. It refuses in every state where a tag
package/README.md CHANGED
@@ -8,8 +8,8 @@ drift — then act through delegated, gated Ops.
8
8
  gets you to a running graph without reading anything else first.
9
9
 
10
10
  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 on Helm —
12
- each substrate's own kind of truth, in one picture.
11
+ cloud drift on AWS, supply-chain drift on GitHub Actions, artifact presence and
12
+ render provenance on Helm — each substrate's own kind of truth, in one picture.
13
13
 
14
14
  ```
15
15
  chant source ──build/lint──▶ graph IR ──behold──▶ live graph + drift + (delegated) actions
@@ -354,6 +354,38 @@ render and the graph loads with no tier selected — the default for any project
354
354
  that doesn't opt in. There's no other tier config surface (not
355
355
  `chant.config.ts`, not an env var behold guesses the name of).
356
356
 
357
+ ### Deploy executors — `executor`
358
+
359
+ The same file can designate which forge deploys an environment (#165):
360
+
361
+ ```json
362
+ {
363
+ "executor": {
364
+ "prod": { "forge": "github", "workflow": "deploy-prod.yml" }
365
+ }
366
+ }
367
+ ```
368
+
369
+ It names the committed **workflow**, not just the forge, because two
370
+ environments' generated pipelines carry identical job ids and a picker cannot
371
+ tell them apart. For a designated environment the Deploy button dispatches that
372
+ workflow through your own `gh` login and follows the run on the dial; a local
373
+ apply, a committed ApplyOp for that environment, and auto-sync are all refused
374
+ (`409 executor-forge`), and rollback is withheld. Any approval the workflow's
375
+ GitHub environment requires is granted on GitHub, by your identity there — the
376
+ dial links the run's page and offers nothing else, because behold holds no
377
+ identity that could clear a forge gate. A designation behold cannot honour (a
378
+ typo'd forge, a missing workflow, one without `workflow_dispatch`) disables
379
+ Deploy for that environment with the reason, and never falls back to running
380
+ it on your machine. The workflow is read from the `.github/workflows` of the
381
+ repository the project belongs to, so a project that is one directory of a
382
+ monorepo works as-is.
383
+
384
+ A dispatched run's id is kept under `~/.behold/ci-runs/` — the operator's own
385
+ state, outside the project — so a behold restarted mid-deploy re-adopts the run
386
+ and keeps following it. A follow whose stream dies is reported as `lost`, never
387
+ as a verdict; `just e2e-ci-github` proves the whole contract against GitHub.
388
+
357
389
  ### The hand-layout sidecar — `.behold/layout.json`
358
390
 
359
391
  dagre places your nodes; you can move them. Drag a card, resize a containment