Owner-approved rebuild of the corrected runtime from the same pinned inputs as ee3d0a53 (not retained). Digest differs only by embedded build-source paths. bwrap startup smoke passed (sandbox 28df7a1d); not installed on railiance01 (still b6e4e8a4) or admitted. T07 stays wait. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 358047@bnt-lap001 Assistant-Session: fad68125-297a-449d-bfa7-cf9cdd49ca1d
12 KiB
| id | type | title | domain | repo | status | owner | topic_slug | priority | created | updated | related | state_hub_workstream_id | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| REINAH-WP-0008 | workplan | Simpler metered retry: free rehearsal, zero-cost hold release, one-command renewal | infotech | rein-aharness | blocked | claude-code | rein-aharness | high | 2026-10-13 | 2026-10-14 |
|
187c2599-84a6-5764-a14f-7e9121975bf2 |
REINAH-WP-0008 — Simpler metered retry
Origin
On 2026-10-13 the owner reconciled the 2026-09-27 hold for run 6efa9436
(docs/evidence/2026-10-13-held-liability-reconciliation.json). A corrected
runtime ee3d0a53… was built
(docs/evidence/2026-10-13-corrected-owner-runtime-build.json). The owner
authorized a new envelope and one retry.
Preparing that retry took too many owner ceremonies:
- The runtime is built on the workstation and copied to railiance01 by hand.
owner.jsonand the spend policy hold five digests that the owner edits by hand.- A failed attempt keeps its full hold even when no provider request was made, so every failure needs a new envelope, ledger and pins.
- Every attempt needs six native approvals.
- The launcher bug that failed run
6efa9436was found only by spending money. A free check on the host would have caught it.
The owner agreed (2026-10-13) to fix items 5, 3 and 2 here, and to record the rest as decisions instead of blocking on them.
Goal: a retry becomes: install the artifact, rehearse for free, approve once, run.
A. Free rehearsal on the host
id: REINAH-WP-0008-T01
status: done
priority: high
state_hub_task_id: "c6536c8d-7862-5de6-ad2b-401077b60c04"
Add a free rehearsal to the owner check. It runs everything
metered-once --check does, then executes the admitted runtime's
rein-aharness and glas-harness launchers inside bwrap from the mount
path, with no network, credentials, provider route or ledger write. The
sandbox profile is already resolved by --check. It prints one receipt with
the runtime digest and each launcher's exit code.
Today's launcher check reads only the first line of each launcher. This rehearsal runs them, the same way the failing run did.
Done when: tests prove the rehearsal fails on a runtime whose launchers
point at a missing build directory (the 6efa9436 failure) and passes on a
relocated runtime. It must make no network calls and write nothing. The
owner procedure in docs/owner-bootstrap.md makes the rehearsal a required
step before any paid attempt.
Implemented — 2026-10-13
rein-aharness metered-once --owner-config <owner.json> --rehearse (the flag
implies --check, so it never reads a provider key or claims). The code is in
rein_aharness/owner_rehearsal.py. It uses sand-boxer's isolation flags and
read-only system binds, with the runtime read-only at /opt/sandboxer/runtime,
--unshare-net and --clearenv. It runs each Python launcher with --help,
plus claude --version when build-info.json declares Claude. A missing
bwrap fails closed.
Tests (tests/test_owner_rehearsal.py, tests/test_owner_bootstrap.py):
- With real bwrap, a relocated runtime passes. The uv trampoline that
re-executes a deleted build interpreter fails with 127, as run
6efa9436did. - The rehearsal never claims and never reads the key.
- The command line has no writable bind and no credential.
The corrected runtime ee3d0a53… rehearses clean: rein-aharness 0,
glas-harness 0, claude 0.
B. Release holds that provably cost nothing
id: REINAH-WP-0008-T02
status: done
priority: high
state_hub_task_id: "6a413f5a-4ac2-5d5e-9b19-71b33445c0a9"
SpendLedger.reconcile keeps the full hold (max(held, observed)). Add an
evidence-backed release. If the run has zero provider-request reservations
and its request route is revoked, reconciliation may set the liability to
the observed amount. Otherwise the current rule stands. The release records
which evidence allowed it, and the CLI flag says so, for example
--release-unused-hold.
This changes a deliberate safety rule, so it needs owner sign-off on the exact rule before merge. That sign-off is recorded in T04's decision note.
Done when: tests cover all four cases. A run with no request reservations and a revoked route releases. A run with any request reservation, or with a live route, refuses the release. A second, identical reconcile changes nothing. The envelope is never marked breached by a release.
Widened to observed-usage settlement — 2026-10-14
On 2026-10-13 the owner asked for three attempts within EUR 10 and chose option B: settle to actual usage instead of releasing only unused holds. Under full-hold accounting each request holds USD 4.64 (1M context plus 64k output), so EUR 10 cannot fit three attempts.
Implemented as an opt-in envelope term (settlement: observed-usage) plus
max_runs; see docs/owner-bootstrap.md. Under it:
- a completed request is charged its gateway-metered cost;
- runs and reconciliations are charged
max(metered requests, reported or attested cost); - unfinished requests keep their full hold, and overruns still freeze the envelope.
The run 6efa9436 shape (failure before any request) now reconciles to its
attested cost. Defaults and existing digests are unchanged. Tests:
tests/test_spend_settlement.py, plus renewal cases in
tests/test_owner_renew.py.
Merge-ready, but the rule still needs the owner's acceptance in decision
a93619a9.
D. One owner command for renewal
id: REINAH-WP-0008-T03
status: done
priority: high
state_hub_task_id: "87457216-2371-51ca-b3d4-3917e6f180a2"
Add rein-aharness owner renew --owner <owner.json> --runtime <digest> --envelope <envelope.json>. It does three things:
- It refuses unless every reservation in the current ledger is settled.
- It writes the new spend policy, initializes a fresh ledger, and updates
the runtime and spend-policy pins in
owner.jsontogether. Old files are kept as backups. Existing ledgers are never reset. - It prints one summary for the owner to review: old to new runtime, old to new policy digest, envelope cap and attempt limit, and ledger path.
Add a dry-run mode that only prints the summary. Afterwards, run the T01 rehearsal.
Done when: a test renews from the 2026-09-27 layout to a new envelope. The owner check passes on the result. A second renew with an unsettled reservation refuses. No digest has to be typed by hand.
Implemented — 2026-10-13
The command is rein-aharness owner renew --owner-config --policy --ledger --runtime-path --envelope [--apply], in rein_aharness/owner_renew.py.
Without --apply it is a dry run. Choices:
- Envelope file: only the envelope fields may change. Worker, target,
profile and grant pins carry over, and
SpendPolicyvalidation runs on the result. - Paths stay the same: the old ledger is renamed to
.retired-<old envelope>, never reset, and the old policy and owner.json are kept as.bak. Secrets Engine pins theexec_ownerfiles by sha256 and the ledger path through its fixed environment, so keeping the paths means the catalog needs only new file hashes. The command prints them. - Refusals: a held reservation, an open request route, an open SQLite transaction, an owner.json that doesn't match the policy, a reused envelope id, a disallowed field, or leftovers from an earlier renewal.
Tests (tests/test_owner_renew.py):
- A dry run changes nothing.
- An applied renewal passes the real owner check (
prepare) at the unchanged paths. - A charged reservation, the railiance01 state after the 2026-10-13 reconciliation, renews, and the row survives in the retired ledger.
- Bad envelopes and a second apply both refuse.
Finding for T06: renewal still changes the pinned file hashes, so each renewal needs a Secrets Engine catalog update and fresh approvals.
C. Envelopes that cover several attempts (decision)
id: REINAH-WP-0008-T04
status: done
decision_id: "a93619a9-8a70-40d8-aaf9-c29357d2a043"
priority: medium
state_hub_task_id: "898e02af-51bb-5e8d-93d0-ae908466c612"
Owner decision: should future envelopes be admitted as "up to N attempts
within EUR X" instead of one attempt? The EUR 10 cap already fits two
USD 4.64 request holds. Record the choice, together with T02's sign-off,
as a decision note in docs/. T03's envelope format then reflects it.
Decided — 2026-10-14
The owner chose up to three attempts within EUR 10 (EUR 5 per attempt) and
observed-usage settlement, and resolved hub decision a93619a9 with that
choice. Record: docs/decisions/2026-10-14-spend-envelope-settlement.md.
The terms are in spend memo infd-20261014-b01.
E. Ship the owner runtime as a container image (decision)
id: REINAH-WP-0008-T05
status: wait
needs_human: true
decision_id: "e6ca2f07-2708-4013-bfba-bf89fa0f1cb5"
priority: low
state_hub_task_id: "b486e86b-9d0f-501d-a394-771b40f771ee"
Decision for later, with Platform (railiance) and Sand-boxer: should the
owner runtime ship through the registry and GitOps, using the existing
make image path, instead of rsync and a manual digest check? Out of scope
here. If accepted, hand it off as a residual intake to the owning repos.
F. Approvals per envelope (decision)
id: REINAH-WP-0008-T06
status: wait
needs_human: true
decision_id: "e8986e40-f6f8-4c87-b704-bff50cc4958b"
priority: low
state_hub_task_id: "4cf721e6-724b-5c64-91bb-67b098bb97af"
Decision for later, with Secrets Engine: should one approval set cover an envelope's attempts instead of six native approvals per attempt? The work lives outside this repo. If accepted, hand it off as a residual intake.
Retry on the corrected runtime
id: REINAH-WP-0008-T07
status: wait
needs_human: true
blocking_reason: "Owner: accept spend memo infd-20261014-b01, then attended install, rehearsal, renew and approvals on railiance01"
priority: high
state_hub_task_id: "80318842-ccaa-5484-a54a-3d9981eaec8b"
Waits on T01 and T03. It does not need T02.
Steps:
- Rebuild
ee3d0a53…, or the then-current corrected runtime, from the pinned commits. - Install it on railiance01.
- Run the T01 rehearsal.
- Renew with T03 to the owner-authorized new envelope (EUR 10, authorized 2026-10-13).
- Run exactly one attended attempt, which consumes fresh native approvals.
Record the result in REINAH-WP-0003-T05/T06.
Waiting on the owner — 2026-10-14
Everything this repo can do is done: the rehearsal (T01), the settlement rule
(T02, T04) and renewal (T03) are in place. Draft memo infd-20261014-b01 is
in informed-decision ee6a65b. What remains needs the owner, in order:
- Accept the memo in the attended sitting.
Rebuild(done 2026-10-15 asee3d0a53from the pinned commits818b1b7c…, see below) and install it on railiance01.- Run
metered-once --rehearse. - Run
owner renewwith the memo's terms, then update the Secrets Engine file pins. - Approve the native actions and run the first attempt.
Sand-boxer's steps 2–4 in message 82faf25c are this same sequence.
Runtime rebuilt — 2026-10-15
Step 1 is done. The owner approved a rebuild because ee3d0a53… was not kept
on the workstation. Sand-boxer built 818b1b7c… from the same pinned inputs
and builder. The digest differs only because the build-source paths are
embedded in the artifact. The bwrap startup smoke passed (sandbox 28df7a1d)
with no credentials and no model run. 818b1b7c… is now the install
candidate. It is not installed on railiance01 (still b6e4e8a4…) or admitted.
Receipt: docs/evidence/2026-10-15-rebuilt-owner-runtime.json. The memo still
still has to be created, admitted, loaded and accepted first. The owner steps
are in informed-decision docs/batches/2026-10-14-three-attempts/OPERATOR.md.