rein-aharness/workplans/REINAH-WP-0008-simpler-metered-retry.md
tegwick 98d2840506
Some checks failed
Governed runtime contract / contract (push) Failing after 12s
REINAH-WP-0008-T07: record rebuilt owner runtime 818b1b7c as install candidate
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
2026-10-15 00:39:13 +02:00

12 KiB
Raw Blame History

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
REINAH-WP-0003
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:

  1. The runtime is built on the workstation and copied to railiance01 by hand.
  2. owner.json and the spend policy hold five digests that the owner edits by hand.
  3. A failed attempt keeps its full hold even when no provider request was made, so every failure needs a new envelope, ledger and pins.
  4. Every attempt needs six native approvals.
  5. The launcher bug that failed run 6efa9436 was 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 6efa9436 did.
  • 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:

  1. It refuses unless every reservation in the current ledger is settled.
  2. It writes the new spend policy, initializes a fresh ledger, and updates the runtime and spend-policy pins in owner.json together. Old files are kept as backups. Existing ledgers are never reset.
  3. 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 SpendPolicy validation 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 the exec_owner files 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:

  1. Rebuild ee3d0a53…, or the then-current corrected runtime, from the pinned commits.
  2. Install it on railiance01.
  3. Run the T01 rehearsal.
  4. Renew with T03 to the owner-authorized new envelope (EUR 10, authorized 2026-10-13).
  5. 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:

  1. Accept the memo in the attended sitting.
  2. Rebuild ee3d0a53 from the pinned commits (done 2026-10-15 as 818b1b7c…, see below) and install it on railiance01.
  3. Run metered-once --rehearse.
  4. Run owner renew with the memo's terms, then update the Secrets Engine file pins.
  5. 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.