mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-07 05:56:21 -10:00
docs(testbed): 51-04 step-2 L0-L5 verification runbooks (host-side, evidence-defined)
Step-2 (plan §2): the L1-L4 live legs authored as host-executed, evidence-defined runbooks (the 51-09 live-legs pattern; the VM seat has no runtime per the locus ruling). Each leg = exact commands + real-byte capture + PASS/FAIL verdict-on-bytes (never from memory) + hashed evidence dir (census-receipt discipline): - L0 resolve inputs (base-image digest + stub-assets.list census — the two host-resolved slots) - L1 PTY allocation (docker run -t + tmux capture real bytes + resize propagation) - L2 tmux server lifecycle (detach-survive, multi-pane, exact send-keys/capture round-trip) - L3 daemon-in-container (container-local sqlite, healthz on published port, rig up stub topology settles — where stub-assets.list is grounded/finalized; zero tokens) - L4 hermetic fail-closed (foreign OPENRIG_URL hard-refused INSIDE the container; grounding note flags the packaged-vs-runner-mode form honestly) - L5 multi-host (N named self-hosts on one docker network via the host registry, plan §3) + the 51-09 live-leg RIDER: executes the shipped collision/alignment RUNBOOK.md (LEG A cross-host triple, LEG B founder-collision) against the testbed containers in one session. Fences carried on every leg: no real-HOME mounts, network-only shared surface, scenarios byte-unchanged, docker-gap-watch routes to PM. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
7b6b9f8f19
commit
49a8e6b588
7 files changed
+387
No files matched your search
@@ -0,0 +1,54 @@
|
||||
# L0 — resolve the host-side build inputs (prerequisite to every build)
|
||||
|
||||
**Runs host-side.** Populates the two documented slots the build verb refuses-until-resolved, so the
|
||||
image is byte-reproducible against a pinned base + a known stub census. Do this once per base/stub
|
||||
change; commit the resolved slots so the pin is durable.
|
||||
|
||||
## L0.1 — resolve the digest-pinned base
|
||||
|
||||
The VM seat cannot pull/inspect a registry digest; do it on the host with a runtime present.
|
||||
|
||||
```bash
|
||||
# Pull the intended LTS-slim base, then read its immutable RepoDigest.
|
||||
docker pull debian:bookworm-slim
|
||||
BASE_REF="$(docker inspect --format '{{index .RepoDigests 0}}' debian:bookworm-slim)"
|
||||
echo "${BASE_REF}" # e.g. debian@sha256:<64-hex> (or docker.io/library/debian@sha256:...)
|
||||
```
|
||||
|
||||
Write the single resolved reference into the slot (replacing the comment block):
|
||||
|
||||
```bash
|
||||
printf '%s\n' "${BASE_REF}" > docker/testbed/base-image
|
||||
# Prove the fence accepts it (readBaseImage returns the digest, or exits non-zero):
|
||||
node -e 'import("./scripts/testbed-build-inputs.mjs").then(m=>console.log(m.readBaseImage("docker/testbed/base-image")))'
|
||||
```
|
||||
|
||||
**PASS:** `readBaseImage` prints `{ ref, name, digest: 'sha256:<64-hex>' }`. **FAIL:** any non-zero /
|
||||
"tag-floating" / "unresolved" — the slot is not a valid digest pin; do not proceed to a build.
|
||||
|
||||
## L0.2 — finalize the stub-assets census list
|
||||
|
||||
The zero-token stub payload (plan §1 layer 4) is the container-local stub `rig.yaml` + its agent
|
||||
fixture + `culture.md` that L3 boots. Add each repo-relative path, one per line, to
|
||||
`docker/testbed/stub-assets.list` (comments/blanks ignored). Ground the exact set against L3 (the
|
||||
`rig up` stub topology it settles) — do not guess; the list IS the census scope the manifest hashes.
|
||||
|
||||
```bash
|
||||
# After populating the list, prove the census resolves over the staged set the build will stage:
|
||||
node -e 'import("./scripts/testbed-build-inputs.mjs").then(m=>{
|
||||
const fs=require("fs");
|
||||
const files=fs.readFileSync("docker/testbed/stub-assets.list","utf8").split("\n").map(s=>s.replace(/#.*/,"").trim()).filter(Boolean);
|
||||
console.log(m.deriveStubAssetsHash(".", files).receipt);
|
||||
})'
|
||||
```
|
||||
|
||||
**PASS:** the receipt lists exactly the intended stub assets, each with a 64-hex content digest.
|
||||
**FAIL:** empty set / a missing file / a path escaping the root — fix the list, never a partial census.
|
||||
|
||||
## L0.3 — record evidence
|
||||
|
||||
```bash
|
||||
GIT_SHA="$(git rev-parse HEAD)"; EVID="dist/testbed-image/evidence/${GIT_SHA}"; mkdir -p "${EVID}"
|
||||
{ echo "docker: $(docker --version)"; echo "base: ${BASE_REF}"; echo "stub-assets.list:"; cat docker/testbed/stub-assets.list; } > "${EVID}/L0-inputs.txt"
|
||||
echo "VERDICT: PASS — base digest pinned + stub census resolved" >> "${EVID}/L0-inputs.txt"
|
||||
```
|
||||
@@ -0,0 +1,47 @@
|
||||
# L1 — PTY allocation (docker run -t + tmux capture returns real bytes; resize propagates)
|
||||
|
||||
**Runs host-side, after L0 + a build.** Proves the image gives a real PTY and that tmux inside it
|
||||
produces genuine captured bytes — the TUI-spike fixed-viewport pattern, reused. Never assert the
|
||||
observable from memory; verdict against the captured bytes.
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
GIT_SHA="$(git rev-parse HEAD)"; IMAGE="openrig-testbed:${GIT_SHA}"; EVID="dist/testbed-image/evidence/${GIT_SHA}"; mkdir -p "${EVID}"
|
||||
docker image inspect "${IMAGE}" >/dev/null # built by scripts/build-testbed-image.sh (FAIL loudly if absent)
|
||||
NAME="orig-l1-${GIT_SHA:0:8}"
|
||||
```
|
||||
|
||||
## L1.1 — allocate a PTY + start a tmux session inside
|
||||
|
||||
```bash
|
||||
# -t allocates a PTY; the entrypoint (tini) holds the container. Detached, fixed 80x24 viewport.
|
||||
docker run -d -t --name "${NAME}" "${IMAGE}"
|
||||
docker exec "${NAME}" tmux new-session -d -s l1 -x 80 -y 24
|
||||
docker exec "${NAME}" tmux send-keys -t l1 'printf "PTY-OK:%s\n" "$TERM"; tty' Enter
|
||||
sleep 1
|
||||
docker exec "${NAME}" tmux capture-pane -p -t l1 | tee "${EVID}/L1-capture.txt"
|
||||
```
|
||||
|
||||
**PASS:** the capture contains a real `PTY-OK:` line and `tty` prints a real pts device
|
||||
(`/dev/pts/...`), not "not a tty". **FAIL:** "not a tty" / empty capture.
|
||||
|
||||
## L1.2 — resize propagates
|
||||
|
||||
```bash
|
||||
docker exec "${NAME}" tmux resize-window -t l1 -x 120 -y 40
|
||||
docker exec "${NAME}" tmux display-message -p -t l1 '#{window_width}x#{window_height}' | tee "${EVID}/L1-resize.txt"
|
||||
```
|
||||
|
||||
**PASS:** reports `120x40` (the resize propagated to the in-container tmux). **FAIL:** stays `80x24`.
|
||||
|
||||
## Teardown + evidence
|
||||
|
||||
```bash
|
||||
docker rm -f "${NAME}" >/dev/null
|
||||
{ grep -q 'PTY-OK:' "${EVID}/L1-capture.txt" && grep -q '/dev/pts/' "${EVID}/L1-capture.txt" && grep -q '120x40' "${EVID}/L1-resize.txt" \
|
||||
&& echo "VERDICT: PASS — PTY allocated, real capture bytes, resize propagated" \
|
||||
|| echo "VERDICT: FAIL — see L1-capture.txt / L1-resize.txt"; } | tee "${EVID}/L1-verdict.txt"
|
||||
```
|
||||
|
||||
**Fence:** no `-v` HOME/workspace mounts; the container is disposable (fresh HOME).
|
||||
@@ -0,0 +1,55 @@
|
||||
# L2 — tmux server lifecycle (detach-survive, multi-pane, send-keys + capture round-trip)
|
||||
|
||||
**Runs host-side, after L1.** Proves the tmux server — the substrate the whole product rides —
|
||||
behaves in-container: sessions survive client detach, multiple panes coexist, and a send-keys →
|
||||
capture round-trip returns the exact bytes.
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
GIT_SHA="$(git rev-parse HEAD)"; IMAGE="openrig-testbed:${GIT_SHA}"; EVID="dist/testbed-image/evidence/${GIT_SHA}"; mkdir -p "${EVID}"
|
||||
NAME="orig-l2-${GIT_SHA:0:8}"; docker run -d -t --name "${NAME}" "${IMAGE}"
|
||||
```
|
||||
|
||||
## L2.1 — server survives detach
|
||||
|
||||
```bash
|
||||
docker exec "${NAME}" tmux new-session -d -s l2 -x 80 -y 24
|
||||
docker exec "${NAME}" tmux send-keys -t l2 'echo persist-marker-$$ > /tmp/l2.marker' Enter
|
||||
sleep 1
|
||||
# A detached session is the default with -d; confirm the server + session still list after a beat.
|
||||
docker exec "${NAME}" tmux ls | tee "${EVID}/L2-sessions.txt"
|
||||
docker exec "${NAME}" cat /tmp/l2.marker | tee "${EVID}/L2-marker.txt"
|
||||
```
|
||||
|
||||
**PASS:** `tmux ls` shows `l2` still alive; the marker file exists. **FAIL:** "no server running" / no `l2`.
|
||||
|
||||
## L2.2 — multiple panes
|
||||
|
||||
```bash
|
||||
docker exec "${NAME}" tmux split-window -t l2 -h
|
||||
docker exec "${NAME}" tmux list-panes -t l2 -F '#{pane_index}' | tee "${EVID}/L2-panes.txt"
|
||||
```
|
||||
|
||||
**PASS:** at least two pane indices (`0`,`1`). **FAIL:** one pane / error.
|
||||
|
||||
## L2.3 — send-keys + capture round-trip (exact bytes)
|
||||
|
||||
```bash
|
||||
TOKEN="roundtrip-$(date +%s 2>/dev/null || echo fixed)-marker"
|
||||
docker exec "${NAME}" tmux send-keys -t l2.0 "printf '%s\n' '${TOKEN}'" Enter
|
||||
sleep 1
|
||||
docker exec "${NAME}" tmux capture-pane -p -t l2.0 | tee "${EVID}/L2-roundtrip.txt"
|
||||
```
|
||||
|
||||
**PASS:** the capture contains `${TOKEN}` verbatim (the exact bytes round-tripped through the server).
|
||||
**FAIL:** token absent / garbled.
|
||||
|
||||
## Teardown + evidence
|
||||
|
||||
```bash
|
||||
docker rm -f "${NAME}" >/dev/null
|
||||
{ grep -q 'l2' "${EVID}/L2-sessions.txt" && [ "$(wc -l < "${EVID}/L2-panes.txt")" -ge 2 ] && grep -q "${TOKEN}" "${EVID}/L2-roundtrip.txt" \
|
||||
&& echo "VERDICT: PASS — server survives detach, multi-pane, exact round-trip" \
|
||||
|| echo "VERDICT: FAIL — see L2-*.txt"; } | tee "${EVID}/L2-verdict.txt"
|
||||
```
|
||||
@@ -0,0 +1,52 @@
|
||||
# L3 — daemon-in-container (boots vs container-local sqlite; healthz on the published port; `rig up` settles a zero-token stub topology)
|
||||
|
||||
**Runs host-side, after L2.** This is the leg that exercises the stub payload — and where
|
||||
`docker/testbed/stub-assets.list` is finalized (L0.2): the exact stub `rig.yaml` + agent fixture +
|
||||
`culture.md` this `rig up` settles ARE the census scope. Zero tokens by construction (runtime: stub).
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
GIT_SHA="$(git rev-parse HEAD)"; IMAGE="openrig-testbed:${GIT_SHA}"; EVID="dist/testbed-image/evidence/${GIT_SHA}"; mkdir -p "${EVID}"
|
||||
NAME="orig-l3-${GIT_SHA:0:8}"
|
||||
# Publish the daemon port to an ephemeral host port; container-local HOME/DB only (no mounts).
|
||||
docker run -d -t --name "${NAME}" -p 127.0.0.1:0:7433 -e OPENRIG_SELF_HOST_ID=testbed-l3 "${IMAGE}"
|
||||
HOSTPORT="$(docker port "${NAME}" 7433/tcp | head -n1 | sed 's/.*://')"
|
||||
```
|
||||
|
||||
## L3.1 — daemon boots vs a container-local sqlite + healthz answers
|
||||
|
||||
```bash
|
||||
docker exec "${NAME}" bash -lc 'rig daemon start && sleep 2 && rig status || true'
|
||||
curl -fsS "http://127.0.0.1:${HOSTPORT}/healthz" | tee "${EVID}/L3-healthz.txt"; echo
|
||||
docker exec "${NAME}" bash -lc 'ls -l "${OPENRIG_HOME:-$HOME/.openrig}"/*.sqlite* 2>/dev/null || find "$HOME" -name "*.sqlite*" 2>/dev/null' | tee "${EVID}/L3-db.txt"
|
||||
```
|
||||
|
||||
**PASS:** `/healthz` returns healthy JSON on the published port; a container-local sqlite exists.
|
||||
**FAIL:** no healthz / DB outside the container / a mounted real HOME (fence breach).
|
||||
|
||||
## L3.2 — `rig up` settles a zero-token stub topology
|
||||
|
||||
Ship a minimal `runtime: stub` `rig.yaml` (+ agent fixture + culture.md) as the staged stub assets;
|
||||
copy it into a container-local workspace and `rig up`.
|
||||
|
||||
```bash
|
||||
docker exec "${NAME}" bash -lc '
|
||||
set -e
|
||||
mkdir -p ~/work && cp -r /opt/openrig-testbed/stub-assets/* ~/work/ 2>/dev/null || true
|
||||
cd ~/work && rig up && sleep 3 && rig ps --json' | tee "${EVID}/L3-topology.txt"
|
||||
```
|
||||
|
||||
**PASS:** the stub seat(s) reach a settled/ready state (zero LLM tokens consumed — `runtime: stub`).
|
||||
**FAIL:** topology never settles / a non-stub runtime is invoked.
|
||||
|
||||
## Teardown + evidence
|
||||
|
||||
```bash
|
||||
docker exec "${NAME}" bash -lc 'rig down || true' ; docker rm -f "${NAME}" >/dev/null
|
||||
{ grep -qi 'health' "${EVID}/L3-healthz.txt" && echo "VERDICT: PASS — daemon boots vs container-local sqlite, healthz answers, stub topology settles" \
|
||||
|| echo "VERDICT: FAIL — see L3-*.txt"; } | tee "${EVID}/L3-verdict.txt"
|
||||
```
|
||||
|
||||
**Note:** the exact `rig ps` shape / ready predicate is grounded against the shipped stub adapter at
|
||||
execution time — do not assert a remembered field name; read the real `--json` output.
|
||||
@@ -0,0 +1,51 @@
|
||||
# L4 — the 51-02 hermetic contract holds INSIDE the container (fail-closed not weakened)
|
||||
|
||||
**Runs host-side, after L3.** The hermeticity floor is "fresh container = fresh HOME" **by
|
||||
construction** — no real-HOME/workspace mounts (verified in L1–L3 by the no-`-v` fence). This leg
|
||||
proves the *active* half: the 51-02 env-helper still **REFUSES an injected foreign `OPENRIG_URL`**
|
||||
inside the container. The container is not an excuse to weaken the fail-closed guard — a leaked daemon
|
||||
target must hard-refuse exactly as it does host-side (the `DAEMON_TARGET_ENV_VARS` guard class).
|
||||
|
||||
## Claim
|
||||
|
||||
With a foreign `OPENRIG_URL` (a daemon target the helper did not set itself) present in the
|
||||
environment, the 51-02 hermetic env prep **hard-refuses** (non-zero + a refusal naming the foreign
|
||||
target) rather than silently adopting it — inside the container, same as host-side.
|
||||
|
||||
## Grounding note (confirm at execution — do NOT assert from memory)
|
||||
|
||||
Where the helper runs depends on whether the 51-02 hermetic-env module is **packaged** in the openrig
|
||||
tarball the image installs (it currently lives at `packages/daemon/test/helpers/hermetic-env.ts` — a
|
||||
test helper, which may not be in the pack set):
|
||||
- **If packaged / resolvable in-container:** invoke the assertion directly in the container (L4.1).
|
||||
- **If not packaged:** this leg is exercised via the **runner's container-execution mode (step-3)**,
|
||||
which drives a scenario into the container with the foreign target injected and the helper (host-side
|
||||
in the runner) refusing — record that as the L4 form and cross-link the step-3 runbook. Either way,
|
||||
the leg is real; pick the form the shipped surface supports and say which.
|
||||
|
||||
## L4.1 — in-container assertion (when the helper is resolvable)
|
||||
|
||||
```bash
|
||||
GIT_SHA="$(git rev-parse HEAD)"; IMAGE="openrig-testbed:${GIT_SHA}"; EVID="dist/testbed-image/evidence/${GIT_SHA}"; mkdir -p "${EVID}"
|
||||
NAME="orig-l4-${GIT_SHA:0:8}"; docker run -d -t --name "${NAME}" "${IMAGE}"
|
||||
|
||||
# Inject a foreign daemon target and drive the hermetic prep; expect a LOUD non-zero refusal.
|
||||
docker exec -e OPENRIG_URL='http://foreign-daemon.invalid:9999' "${NAME}" bash -lc '
|
||||
node --input-type=module -e "
|
||||
import { prepareHermeticEnv } from \"@openrig/daemon/test/helpers/hermetic-env.js\";
|
||||
try { prepareHermeticEnv({ baseEnv: process.env }); console.log(\"NO-REFUSAL\"); process.exit(0); }
|
||||
catch (e) { console.error(\"REFUSED: \" + e.message); process.exit(7); }
|
||||
"' 2>&1 | tee "${EVID}/L4-refusal.txt"; RC=${PIPESTATUS[0]:-$?}
|
||||
docker rm -f "${NAME}" >/dev/null
|
||||
```
|
||||
|
||||
**PASS:** non-zero exit + `REFUSED:` naming the foreign `OPENRIG_URL`. **FAIL:** `NO-REFUSAL` /
|
||||
exit 0 (the guard was weakened in-container). Adjust the import specifier to the helper's real shipped
|
||||
path; if it does not resolve, switch to the step-3 form (grounding note) — do not green-pass by memory.
|
||||
|
||||
## Evidence
|
||||
|
||||
```bash
|
||||
{ grep -q 'REFUSED:' "${EVID}/L4-refusal.txt" && echo "VERDICT: PASS — foreign OPENRIG_URL hard-refused inside the container" \
|
||||
|| echo "VERDICT: FAIL / FORM-DEFERRED — see L4-refusal.txt + the grounding note"; } | tee "${EVID}/L4-verdict.txt"
|
||||
```
|
||||
@@ -0,0 +1,70 @@
|
||||
# L5 — multi-host topology (N named self-hosts on one network) + the 51-09 live-leg rider
|
||||
|
||||
**Runs host-side, after L4.** Proves plan §3: N containers each = one simulated **named host**,
|
||||
composed via the shipped **host registry over HTTP** (registry rows point at container-DNS names), one
|
||||
daemon per container, container-local DB/HOME, no real-HOME mounts. The **only** shared surface is the
|
||||
docker network.
|
||||
|
||||
**51-09 live-leg rider (orch, merge desk):** this same two-host setup is the executor for the shipped
|
||||
51-09 collision/alignment runbook. After the topology settles, run BOTH 51-09 legs **in this same
|
||||
host-side session** and return both evidence sets. Fixture + steps:
|
||||
`packages/daemon/test/fixtures/self-host-live-legs/RUNBOOK.md` — **LEG A** (cross-host stamped-triple
|
||||
round-trip: a cross-host send stores the ORIGIN host in the sender identity and the `↩ Reply:` hint
|
||||
round-trips verbatim to the origin) and **LEG B** (founder-collision: a same-named rig on both hosts;
|
||||
the received signature names the origin and the reply lands on the origin, not the local lookalike).
|
||||
|
||||
## L5.1 — boot two named self-hosts on one network
|
||||
|
||||
```bash
|
||||
GIT_SHA="$(git rev-parse HEAD)"; IMAGE="openrig-testbed:${GIT_SHA}"; EVID="dist/testbed-image/evidence/${GIT_SHA}"; mkdir -p "${EVID}"
|
||||
NET="orig-net-${GIT_SHA:0:8}"; docker network create "${NET}" >/dev/null
|
||||
|
||||
# Each container adopts a distinct self-host id via the entrypoint (plan §3 / 51-09 adopt path).
|
||||
docker run -d -t --name H_A --hostname H_A --network "${NET}" -e OPENRIG_SELF_HOST_ID=A "${IMAGE}"
|
||||
docker run -d -t --name H_B --hostname H_B --network "${NET}" -e OPENRIG_SELF_HOST_ID=B "${IMAGE}"
|
||||
for h in H_A H_B; do docker exec "$h" bash -lc 'rig daemon start && sleep 2 && rig whoami --json'; done | tee "${EVID}/L5-selfids.txt"
|
||||
```
|
||||
|
||||
**PASS:** each `rig whoami` reports its distinct self-id (`A`, `B`) — both non-`local`, registry-valid,
|
||||
and stable across a daemon restart (51-09 incr 1). **FAIL:** duplicate/`local` self-ids.
|
||||
|
||||
## L5.2 — compose via the host registry over HTTP
|
||||
|
||||
Register `H_B` in `H_A`'s host registry pointing at the container-DNS name `H_B` (reachable on `${NET}`):
|
||||
|
||||
```bash
|
||||
docker exec H_A bash -lc 'rig host add B --url http://H_B:7433 && rig host ls --json' | tee "${EVID}/L5-registry.txt"
|
||||
```
|
||||
|
||||
**PASS:** `H_A`'s registry lists host `B` at the container-DNS URL; a probe reaches `H_B`'s daemon over
|
||||
the network only. **FAIL:** unreachable / resolves off-network. (Ground the exact `rig host` flag shape
|
||||
against the shipped CLI at execution — read `--json`, don't assert a remembered field.)
|
||||
|
||||
## L5.3 — execute the 51-09 live legs (the rider) in this session
|
||||
|
||||
Using `H_A`/`H_B` as the two hosts, follow the shipped runbook end-to-end and bank its evidence here:
|
||||
|
||||
```bash
|
||||
# Source of truth for the exact seats/fixtures/steps + PASS predicates:
|
||||
sed -n '1,200p' packages/daemon/test/fixtures/self-host-live-legs/RUNBOOK.md
|
||||
# LEG A: cross-host send orch@rig-a -> dev@rig-b --host B; expect From: orch@rig-a@A + verbatim reply lands on H_A.
|
||||
# LEG B: same-named rig 'shared' on both; expect From: lead@shared@A + reply lands on H_A's lead@shared.
|
||||
# Capture each received pane + the stored source_session; write to L5-leg-a.txt / L5-leg-b.txt.
|
||||
```
|
||||
|
||||
**PASS:** LEG A signature names `A` + stored `source_session == orch@rig-a@A` + verbatim reply lands on
|
||||
`H_A`; LEG B signature names the origin `A` + reply lands on `H_A`'s lookalike, not `H_B`'s. Include the
|
||||
51-09 fail-open control (stop `H_A`'s daemon → `From:` degrades to 2-part, no crash).
|
||||
|
||||
## Teardown + evidence
|
||||
|
||||
```bash
|
||||
docker rm -f H_A H_B >/dev/null; docker network rm "${NET}" >/dev/null
|
||||
{ echo "topology: 2 named hosts A/B on ${NET}, registry-composed"; echo "51-09 rider: LEG A + LEG B executed against the testbed containers"; \
|
||||
echo "VERDICT: PASS|FAIL — per L5-selfids/registry/leg-a/leg-b captures (verdict on bytes, not memory)"; } | tee "${EVID}/L5-verdict.txt"
|
||||
```
|
||||
|
||||
**Fences:** no real-HOME mounts; the docker network is the only shared surface; scenarios byte-unchanged.
|
||||
**Docker-gap watch (plan §3):** if per-container network identity proves insufficient for a topology the
|
||||
design needs, THAT is the named concrete gap the ruling requires before any Apple-stack revisit — route
|
||||
to PM with the evidence, never install.
|
||||
@@ -0,0 +1,58 @@
|
||||
# openrig-testbed verification runbooks (51-04 step-2, plan §2)
|
||||
|
||||
These are **host-executed, evidence-defined** runbooks — the 51-09 live-legs pattern. The VM seat has
|
||||
no container runtime (locus ruling (a) host-side), so the daemon/operator lane runs them **host-side**
|
||||
against the image `scripts/build-testbed-image.sh` produces, and returns evidence per the
|
||||
census-receipt discipline. Each leg is a scripted check that never asserts from memory (the clause
|
||||
that caught the Apple-container premise): run it, capture the real bytes, verdict against them.
|
||||
|
||||
## Legs
|
||||
|
||||
| Leg | Proves | Runbook |
|
||||
|---|---|---|
|
||||
| L0 | the digest-pinned base + stub-assets census are resolved (prerequisite to a build) | `L0-resolve-inputs.md` |
|
||||
| L1 | PTY allocation: `docker run -t` + tmux capture-pane returns real bytes; resize propagates | `L1-pty-allocation.md` |
|
||||
| L2 | tmux server lifecycle: detach-survive, multi-pane, send-keys + capture round-trip | `L2-tmux-server-lifecycle.md` |
|
||||
| L3 | daemon-in-container: boots vs container-local sqlite, healthz on the published port, `rig up` settles a zero-token stub topology | `L3-daemon-in-container.md` |
|
||||
| L4 | the 51-02 hermetic contract: the env-helper still REFUSES a foreign `OPENRIG_URL` **inside** the container (fail-closed not weakened by "we're in a container anyway") | `L4-hermetic-fail-closed.md` |
|
||||
| L5 | multi-host: N containers as N distinct named self-hosts on one docker network, composed via the shipped host registry over HTTP (plan §3) — **and** the 51-09 live-leg rider | `L5-multi-host-and-51-09.md` |
|
||||
|
||||
Sequencing: **L0 → build → L1 → L2 → L3 → L4 → L5**. L3 is where the stub payload is exercised (and
|
||||
where `docker/testbed/stub-assets.list` is finalized). L4 depends on the 51-02 hermetic env-helper
|
||||
being present in the packaged image (it ships in the openrig package the Dockerfile installs).
|
||||
|
||||
**51-09 live-leg rider (orch, merge desk).** The testbed image's first multi-host run doubles as the
|
||||
51-09 live-leg executor: L5 boots the containers as the two named hosts `H_A`/`H_B` and then runs the
|
||||
shipped 51-09 collision/alignment runbook **in the same host-side session** —
|
||||
`packages/daemon/test/fixtures/self-host-live-legs/RUNBOOK.md` (LEG A cross-host stamped-triple
|
||||
round-trip, LEG B founder-collision). Execute both families together; return both evidence sets.
|
||||
|
||||
## Shared conventions
|
||||
|
||||
**Image identity.** All legs run against the image the build verb tagged, by manifest identity:
|
||||
```bash
|
||||
GIT_SHA="$(git rev-parse HEAD)"
|
||||
IMAGE="openrig-testbed:${GIT_SHA}" # == manifest.image
|
||||
```
|
||||
Cross-check the running image against the emitted manifest before trusting a leg's result:
|
||||
```bash
|
||||
test "$(docker image inspect "${IMAGE}" --format '{{.Id}}')" # exists
|
||||
cat dist/testbed-image/manifest.json # the census identity this run is comparable under
|
||||
```
|
||||
|
||||
**Evidence dir (hashed, per the census-receipt discipline).** Each leg writes its captures + a
|
||||
`VERDICT` line into a per-run evidence dir, then the operator hashes the dir so the evidence is
|
||||
tamper-evident and comparable across image versions:
|
||||
```bash
|
||||
EVID="dist/testbed-image/evidence/${GIT_SHA}"
|
||||
mkdir -p "${EVID}"
|
||||
# ... leg writes L1-capture.txt, L1-resize.txt, etc. ...
|
||||
( cd "${EVID}" && find . -type f -print0 | sort -z | xargs -0 sha256sum ) > "${EVID}.sha256"
|
||||
```
|
||||
Record in each leg's evidence: the exact command, the captured bytes, and a single
|
||||
`VERDICT: PASS|FAIL — <one line>` on the observed bytes (never from memory).
|
||||
|
||||
**Fences (binding on every leg).** No real-HOME / real-workspace volume mounts (fresh container =
|
||||
fresh HOME is the hermeticity floor); scenarios byte-unchanged; the only shared surface between
|
||||
multi-host containers is the docker network. A leg that cannot run its check reports a **loud
|
||||
blocker** with the exact command + the missing capability — it never green-passes from memory.
|
||||
Reference in new issue
Block a user