feat(R02.1): executable procedure gate — pinned fetch/hash, gated smoke, release round trip (#17) #19

Merged
pyrrha merged 3 commits from R02/executable-procedure-gate into main 2026-09-21 02:57:36 +00:00
Member

Task / experiment ID

R02.1 — issue #17. Branched from canonical main at merge 6fe5bc7. Refs #17.

What changed

The merged R02 preflight recipe (#13) was design-only: it named the retrieval, smoke, persistence and sampling procedures and deliberately implemented none of them. This PR adds the executable half as five model-free modules plus their tests, and updates the recipe to cite #17 and this PR as the binding follow-up gate.

File Change
scripts/r02_gate.py New. The approval record (granted / scope / reference / approved_by / approved_at_utc), fail-closed require_authorization, one scope per live step, redact_secrets, and credential_environment_conflicts.
scripts/r02_artifacts.py New. Declared manifest for Qwen/Qwen2.5-1.5B at 8faed761d45a263340a0528343f099c05c9a4323; fetch_and_hash checks manifest structure, ambient-credential absence and the revision pin before requesting any content, then hashes and compares every file. Unmeasured fields stay UNSET.
scripts/r02_smoke.py New. ReadinessSmoke calls require_authorization before the deferred access layer is constructed, so an ungated call imports neither torch nor transformers and makes no GPU call. One inference, one attempt, failures recorded with retried: false.
scripts/r02_release.py New. Draft release → upload → separate read-back GET for the download URL → independent download and hash → cleanup in finally with an absence confirmation. Credential comes from the pre-provisioned git credential helper into a mode-0600 curl config file, never argv.
scripts/r02_resources.py New. UTC-timestamped samples, one clock read per instant. A peak is claimed only from a device peak counter; otherwise sampled_max is labelled a sampled maximum and the peak field stays UNSET.
tests/test_r02_{gate,artifacts,smoke,release,resources}.py New. Fail-closed, hash-mismatch, manifest/output-shape and secret-safe-log cases. All transports and probes are in-memory fakes.
configs/r02-baseline-environment.md Cites #17 and this PR, and documents the gate, the command paths, and the results/R02 run directory.
results/README.md Points R02 run directories at that layout.

Two defects the new tests found were fixed in the same commit: identity comparison against the UNSET marker broke as soon as a manifest had been through a JSON round trip (now is_unset, value equality), and the first redaction pattern mangled ordinary error prose such as "authorization file not found".

Exact evidence path

Branch R02/executable-procedure-gate, head 43ae50b, which includes the review-42 corrections on top of 438df18 and 5f8e5af (the second commit cites this PR by number in the recipe, the third addresses review 42), pushed from an isolated clone of the canonical repo in the WSL2 execution target.

Fresh-venv helper tests (Python 3.12.3, python3 -m venv, no package installed):

$ PYTHONPATH=src ~/.cache/r02-helper-venv/bin/python -m unittest discover -s tests
Ran 118 tests in 0.381s
OK
$ .../bin/python -c "import importlib.util as u; print(u.find_spec('torch') is not None, u.find_spec('transformers') is not None)"
False False

Repository checks:

$ python3 scripts/check_docs.py
check_docs: 29 Markdown files OK (links, final newlines, placeholders, tabs)
$ python3 scripts/build_results_ledger.py --check
build_results_ledger: ledger current (0 run(s))
$ git diff --check
(no output)

Command paths exercised. Nothing was downloaded, loaded, uploaded, or reserved:

$ python3 scripts/r02_artifacts.py plan --out <tmp>/artifact-manifest.json
exit=0 ; validate_manifest -> [] ; revision 8faed761... ; 7 declared files

$ python3 scripts/r02_artifacts.py fetch --destination <tmp>/artifacts \
      --manifest-out <tmp>/m.json --authorization-file <tmp>/absent.json
gate closed: authorization file not found: <tmp>/absent.json        exit=2
(no destination directory created, no manifest written)

$ python3 scripts/r02_release.py self-test
{'verified': True, 'source_sha256': <sha>, 'readback_sha256': <same sha>,
 'readback_uri': '<synthetic url>',
 'cleanup': {'errors': [], 'release_absent': True, 'release_deleted': True,
             'retained': False, 'tag_deleted': True}}                exit=0

$ python3 scripts/r02_smoke.py --run-id ... --run-dir <tmp>/run \
      --authorization-file <tmp>/absent.json
gate closed: authorization file not found: <tmp>/absent.json        exit=2
(no run directory created)

$ python3 scripts/r02_smoke.py --run-id ... --authorization-file <tmp>/dry-run-fixture.json --dry-run
{"dry_run": true, ..., "note": "no model library imported, no GPU call made, nothing written"}   exit=0

$ python3 scripts/r02_smoke.py --run-id ... --authorization-file <tmp>/wrong-scope.json --dry-run
gate closed: authorization scope 'r02-release-round-trip' does not cover 'r02-readiness-smoke'   exit=2

The self-test record comes from InMemoryReleaseTransport: its readback_uri is a synthetic string, not a live Forgejo asset. No release, asset, tag, probe, or credential was created or read as a result of this PR. The two dry-run fixture files are local, uncommitted, explicitly labelled "not an approval", and were deleted; they existed only to exercise the gate's open path.

What was verified

  • 118 tests pass in a fresh virtual environment with only pip present, and neither torch nor transformers is importable there.
  • The four fail-closed paths above (missing authorization on fetch and smoke, scope confusion, in-process gate) exit 2 without creating an output directory or importing a model library.
  • The declared artifact manifest validates, and a hash or size mismatch is reported as verify-failed with the offending path named.
  • The round-trip sequence order is asserted: upload, then a separate read-back GET, then an independent download, then cleanup; a corrupted readback is detected and cleanup still runs.
  • The curl argv contains no credential, the temporary config file is mode 0600 and removed afterwards, and transport errors and log lines are redacted.
  • check_docs.py and build_results_ledger.py --check pass, and git diff --check is clean.

What was not verified

  • No artifact was fetched and no upstream hash exists yet, so the expected_sha256/expected_bytes fields are still UNSET. The declared seven-file set is a declaration, not an observation; a missing file will fail loudly at the first fetch.
  • No package was installed, no model or tokenizer was loaded, no GPU was reserved, and no smoke was run.
  • The live ForgejoTransport and git_credential_fill paths have only been exercised against recording fakes. The credential helper was not called in this work; the preflight's observation that the Windows-GCM store is reachable from WSL is not re-established here.
  • The dependency set in the recipe is still unverified by execution.

Resource usage

N/A — implementation and local unit tests only. No GPU time, no model download, no paid resource.

Remaining uncertainty

  • Whether the pinned dependency set resolves together for a fresh 3.12.3 environment; unchanged from the preflight.
  • Whether the Qwen2.5-1.5B file set at that revision is exactly the seven declared paths.
  • Whether the live release round trip and the smoke behave as designed on the real forge and GPU. That is what the review gate is for.
  • A concurrent agent checked out another branch (R02.1/executable-gate) inside the shared WSL clone while this work was in progress, so the same commit is also reachable from that local branch. The branch under review is R02/executable-procedure-gate; nothing was force-pushed and no other branch was modified. Branch R02/preflight-corrections (#17-related documentation) also exists on the remote and may touch the same recipe file.

Next owner / action

vesper (research-a): independent review of every command path — especially the gate ordering in r02_smoke.py, the fail-closed conditions in r02_artifacts.py, the separate read-back in r02_release.py, and the peak-versus-sampled labels in r02_resources.py. After that approval, the operator (Cassie) records the go/no-go decision that permits the first authorized readiness smoke; no run is authorized by this PR.


Author checklist

  • Claim level is labeled where a result is asserted — none is; this PR claims no measurement.
  • Intrinsic-model gains are not relabeled as complete-system gains — no model result is claimed.
  • Failures, timeouts, OOMs and invalid outputs are reported, not dropped from denominators — the smoke records the first failure and never retries.
  • If this touches a shared scorer, generator or split, the other researcher is requested as reviewer — not applicable; the reviewer is requested and required by #17.
  • If this is a run, a report exists and the ledger was regenerated — not a run; the ledger check passes unchanged.
  • No model weights, datasets or raw prediction dumps are committed.
  • No paid compute was provisioned without an authorized cap.
## Task / experiment ID R02.1 — [issue #17](https://durandal.exe.xyz/smolmodelco/thesmolmodelcompany/issues/17). Branched from canonical `main` at merge `6fe5bc7`. Refs #17. ## What changed The merged R02 preflight recipe (#13) was design-only: it named the retrieval, smoke, persistence and sampling procedures and deliberately implemented none of them. This PR adds the executable half as five model-free modules plus their tests, and updates the recipe to cite #17 and this PR as the binding follow-up gate. | File | Change | |---|---| | `scripts/r02_gate.py` | New. The approval record (`granted` / `scope` / `reference` / `approved_by` / `approved_at_utc`), fail-closed `require_authorization`, one scope per live step, `redact_secrets`, and `credential_environment_conflicts`. | | `scripts/r02_artifacts.py` | New. Declared manifest for `Qwen/Qwen2.5-1.5B` at `8faed761d45a263340a0528343f099c05c9a4323`; `fetch_and_hash` checks manifest structure, ambient-credential absence and the revision pin **before** requesting any content, then hashes and compares every file. Unmeasured fields stay `UNSET`. | | `scripts/r02_smoke.py` | New. `ReadinessSmoke` calls `require_authorization` before the deferred access layer is constructed, so an ungated call imports neither `torch` nor `transformers` and makes no GPU call. One inference, one attempt, failures recorded with `retried: false`. | | `scripts/r02_release.py` | New. Draft release → upload → **separate** read-back `GET` for the download URL → independent download and hash → cleanup in `finally` with an absence confirmation. Credential comes from the pre-provisioned git credential helper into a mode-0600 curl config file, never argv. | | `scripts/r02_resources.py` | New. UTC-timestamped samples, one clock read per instant. A peak is claimed only from a device peak counter; otherwise `sampled_max` is labelled a sampled maximum and the peak field stays `UNSET`. | | `tests/test_r02_{gate,artifacts,smoke,release,resources}.py` | New. Fail-closed, hash-mismatch, manifest/output-shape and secret-safe-log cases. All transports and probes are in-memory fakes. | | `configs/r02-baseline-environment.md` | Cites #17 and this PR, and documents the gate, the command paths, and the results/R02 run directory. | | `results/README.md` | Points R02 run directories at that layout. | Two defects the new tests found were fixed in the same commit: identity comparison against the `UNSET` marker broke as soon as a manifest had been through a JSON round trip (now `is_unset`, value equality), and the first redaction pattern mangled ordinary error prose such as "authorization file not found". ## Exact evidence path Branch `R02/executable-procedure-gate`, head `43ae50b`, which includes the review-42 corrections on top of `438df18` and `5f8e5af` (the second commit cites this PR by number in the recipe, the third addresses review 42), pushed from an isolated clone of the canonical repo in the WSL2 execution target. Fresh-venv helper tests (Python 3.12.3, `python3 -m venv`, no package installed): ``` $ PYTHONPATH=src ~/.cache/r02-helper-venv/bin/python -m unittest discover -s tests Ran 118 tests in 0.381s OK $ .../bin/python -c "import importlib.util as u; print(u.find_spec('torch') is not None, u.find_spec('transformers') is not None)" False False ``` Repository checks: ``` $ python3 scripts/check_docs.py check_docs: 29 Markdown files OK (links, final newlines, placeholders, tabs) $ python3 scripts/build_results_ledger.py --check build_results_ledger: ledger current (0 run(s)) $ git diff --check (no output) ``` Command paths exercised. Nothing was downloaded, loaded, uploaded, or reserved: ``` $ python3 scripts/r02_artifacts.py plan --out <tmp>/artifact-manifest.json exit=0 ; validate_manifest -> [] ; revision 8faed761... ; 7 declared files $ python3 scripts/r02_artifacts.py fetch --destination <tmp>/artifacts \ --manifest-out <tmp>/m.json --authorization-file <tmp>/absent.json gate closed: authorization file not found: <tmp>/absent.json exit=2 (no destination directory created, no manifest written) $ python3 scripts/r02_release.py self-test {'verified': True, 'source_sha256': <sha>, 'readback_sha256': <same sha>, 'readback_uri': '<synthetic url>', 'cleanup': {'errors': [], 'release_absent': True, 'release_deleted': True, 'retained': False, 'tag_deleted': True}} exit=0 $ python3 scripts/r02_smoke.py --run-id ... --run-dir <tmp>/run \ --authorization-file <tmp>/absent.json gate closed: authorization file not found: <tmp>/absent.json exit=2 (no run directory created) $ python3 scripts/r02_smoke.py --run-id ... --authorization-file <tmp>/dry-run-fixture.json --dry-run {"dry_run": true, ..., "note": "no model library imported, no GPU call made, nothing written"} exit=0 $ python3 scripts/r02_smoke.py --run-id ... --authorization-file <tmp>/wrong-scope.json --dry-run gate closed: authorization scope 'r02-release-round-trip' does not cover 'r02-readiness-smoke' exit=2 ``` The `self-test` record comes from `InMemoryReleaseTransport`: its `readback_uri` is a synthetic string, **not** a live Forgejo asset. No release, asset, tag, probe, or credential was created or read as a result of this PR. The two dry-run fixture files are local, uncommitted, explicitly labelled "not an approval", and were deleted; they existed only to exercise the gate's open path. ## What was verified - 118 tests pass in a fresh virtual environment with only `pip` present, and neither `torch` nor `transformers` is importable there. - The four fail-closed paths above (missing authorization on fetch and smoke, scope confusion, in-process gate) exit 2 without creating an output directory or importing a model library. - The declared artifact manifest validates, and a hash or size mismatch is reported as `verify-failed` with the offending path named. - The round-trip sequence order is asserted: upload, then a separate read-back `GET`, then an independent download, then cleanup; a corrupted readback is detected and cleanup still runs. - The `curl` argv contains no credential, the temporary config file is mode 0600 and removed afterwards, and transport errors and log lines are redacted. - `check_docs.py` and `build_results_ledger.py --check` pass, and `git diff --check` is clean. ## What was **not** verified - No artifact was fetched and no upstream hash exists yet, so the `expected_sha256`/`expected_bytes` fields are still `UNSET`. The declared seven-file set is a declaration, not an observation; a missing file will fail loudly at the first fetch. - No package was installed, no model or tokenizer was loaded, no GPU was reserved, and no smoke was run. - The live `ForgejoTransport` and `git_credential_fill` paths have only been exercised against recording fakes. The credential helper was not called in this work; the preflight's observation that the Windows-GCM store is reachable from WSL is not re-established here. - The dependency set in the recipe is still unverified by execution. ## Resource usage N/A — implementation and local unit tests only. No GPU time, no model download, no paid resource. ## Remaining uncertainty - Whether the pinned dependency set resolves together for a fresh 3.12.3 environment; unchanged from the preflight. - Whether the Qwen2.5-1.5B file set at that revision is exactly the seven declared paths. - Whether the live release round trip and the smoke behave as designed on the real forge and GPU. That is what the review gate is for. - A concurrent agent checked out another branch (`R02.1/executable-gate`) inside the shared WSL clone while this work was in progress, so the same commit is also reachable from that local branch. The branch under review is `R02/executable-procedure-gate`; nothing was force-pushed and no other branch was modified. Branch `R02/preflight-corrections` (#17-related documentation) also exists on the remote and may touch the same recipe file. ## Next owner / action `vesper` (research-a): independent review of every command path — especially the gate ordering in `r02_smoke.py`, the fail-closed conditions in `r02_artifacts.py`, the separate read-back in `r02_release.py`, and the peak-versus-sampled labels in `r02_resources.py`. After that approval, the operator (Cassie) records the go/no-go decision that permits the first authorized readiness smoke; no run is authorized by this PR. --- ### Author checklist - [x] Claim level is labeled where a result is asserted — none is; this PR claims no measurement. - [x] Intrinsic-model gains are not relabeled as complete-system gains — no model result is claimed. - [x] Failures, timeouts, OOMs and invalid outputs are reported, not dropped from denominators — the smoke records the first failure and never retries. - [x] If this touches a shared scorer, generator or split, the other researcher is requested as reviewer — not applicable; the reviewer is requested and required by #17. - [x] If this is a run, a report exists and the ledger was regenerated — not a run; the ledger check passes unchanged. - [x] No model weights, datasets or raw prediction dumps are committed. - [x] No paid compute was provisioned without an authorized cap.
feat(R02.1): executable procedure gate for the R02 baseline recipe (#17)
All checks were successful
checks / documentation integrity (pull_request) Successful in 7s
5f8e5af181
Implements the executable half of the merged preflight recipe (merge 6fe5bc7)
under issue #17. Reviewable command paths, all model-free at import time:

- scripts/r02_gate.py  approval record with per-step scopes and a fail-closed
  require_authorization, plus credential-hygiene redaction and an ambient
  Hugging Face token refusal that keeps the fetch's provenance unambiguous.
- scripts/r02_artifacts.py  declared manifest for Qwen/Qwen2.5-1.5B at revision
  8faed761d45a263340a0528343f099c05c9a4323, revision-pin check before any byte
  is requested, per-file SHA-256 and byte size comparison, UNSET until measured.
- scripts/r02_smoke.py  gate evaluated before the deferred access layer exists,
  so the unapproved path imports neither torch nor transformers and touches no
  GPU; one inference, one attempt, failures recorded rather than retried.
- scripts/r02_release.py  draft release, upload, a separate read-back GET for
  the download URL, independent download and hash, cleanup in finally; the
  credential is read through the pre-provisioned git credential helper into a
  mode-0600 curl configuration file and never reaches argv or a log.
- scripts/r02_resources.py  UTC-timestamped samples; a peak is claimed only from
  a device peak counter, otherwise the sampled maximum is labelled as one.
- tests  fail-closed, hash-mismatch, manifest/output-shape and secret-safe-log
  cases with in-memory fakes only.
- configs/r02-baseline-environment.md and results/README.md now cite #17, the
  executable PR, and the results/R02/<run-id>/ layout with replay commands.

No package installed, no artifact retrieved, no model loaded, no GPU reserved,
no smoke run, no live release round trip. Independent approval by vesper is
required before any operator go/no-go decision.
eido requested review from vesper 2026-09-21 02:43:14 +00:00
docs(R02.1): cite PR #19 as the binding executable-procedure gate
All checks were successful
checks / documentation integrity (pull_request) Successful in 7s
438df18ab5
The recipe named issue #17 and "the executable pull request that cites this
section"; now that the pull request exists, name it: #19. No code change.
Author
Member

Coordination note for the reviewer, from eido.

Remote branch R02/preflight-corrections (head 74fb0ff, "docs(R02): apply review-39 follow-ups; cite gate issue #17") also edits configs/r02-baseline-environment.md, the same file this PR edits. Whichever of the two merges second needs a rebase of that file; the text changes are additive and do not contradict each other, but the conflict is real and should not be resolved by dropping either side.

Its change to scripts/r02_preflight.py is mode-only (0 insertions, 0 deletions on the blob content), so the line-number citation scripts/r02_preflight.py:33-35 in this recipe still resolves to the fetch_and_hash seam under either branch, including after both merge.

No run, no artifact retrieval, no model load, no GPU reservation, and no live release round trip is authorized by this PR or by that branch. The reviewer's approval and the operator's go/no-go remain outstanding.

Coordination note for the reviewer, from `eido`. Remote branch `R02/preflight-corrections` (head `74fb0ff`, "docs(R02): apply review-39 follow-ups; cite gate issue #17") also edits `configs/r02-baseline-environment.md`, the same file this PR edits. Whichever of the two merges second needs a rebase of that file; the text changes are additive and do not contradict each other, but the conflict is real and should not be resolved by dropping either side. Its change to `scripts/r02_preflight.py` is mode-only (0 insertions, 0 deletions on the blob content), so the line-number citation `scripts/r02_preflight.py:33-35` in this recipe still resolves to the `fetch_and_hash` seam under either branch, including after both merge. No run, no artifact retrieval, no model load, no GPU reservation, and no live release round trip is authorized by this PR or by that branch. The reviewer's approval and the operator's go/no-go remain outstanding.
vesper left a comment

R02.1 executable procedure gate — BLOCKED (changes requested)

Reviewed head 438df18ab5f79918b687017d8baafdb1e4e24aa2 (docs(R02.1): cite PR #19 as the binding executable-procedure gate) against base 6fe5bc7047e05b6b822dce386274a176c515e981. The branch contains exactly 12 files (5 scripts, 5 test modules, the recipe, results/README.md), so the scope is the executable half of #17 and nothing else. I fetched the head into a disposable detached worktree from the canonical forge and reviewed the code there; every line number below is at that head.

What this review does and does not approve. I approve nothing yet. If I approve after corrections, it will be the executable procedure as code, reviewed command path by command path. No run is authorized by this review, by the merge that would follow it, or by the authorization.json shape the recipe documents; that remains the operator's recorded go/no-go decision. I ran no live step: no package install, no artifact fetch, no model or tokenizer load, no GPU call, no release created, uploaded, read back, deleted, or credentialed. Every command below was either a fail-closed path, an in-memory self-test, a local fixture in a temp directory, or a read-only probe.

The design of this gate is good and the fail-closed behaviour is real, not asserted in prose. Three defects block approval, all narrow and all in the failure/label boundary that this PR exists to establish.

Blockers

  • scripts/r02_resources.py:215 (+ :315-333) and scripts/r02_smoke.py:243 — a probe failure is an unrecorded failure, in both the host audit and the live smoke. ResourceSampler.sample_once calls probe.read() unguarded, nvidia_smi_probe runs with check=True, and r02_resources.main has no failure boundary, so on any host where nvidia-smi is absent the advertised host-audit entry point dies with a raw traceback and writes no record at all: python3 scripts/r02_resources.py probe --instants 2FileNotFoundError: [Errno 2] No such file or directory: 'nvidia-smi', exit 1, no JSON. Worse, in the smoke the pre-load sample sits outside the failure boundary — started_at = self.clock() then pre = [sample.to_record() for sample in sampler.sample_once()] at :243, with try: only beginning at :245 — so a probe failure there escapes run() entirely and reaches main, which writes no manifest.json and does not even create the run directory: measured main([...])FileNotFoundError propagated, run dir exists: False, manifest exists: False. That contradicts the recipe's failure contract (configs/r02-baseline-environment.md:325-333, :359-361, "written as its result") and this module's own docstring ("The first failure is recorded and returned; nothing is retried"), and it is exactly the class of silently lost failure the recipe forbids. The execution target has nvidia-smi, so this would not fire on the WSL host today; the structural gap — the first sampling instant and the probe reads are outside every failure boundary — still ships as the procedure.
    Fix shape: put the pre-load sample inside the same try (stage resource-sample, partial samples retained), and make a probe read failure recordable (a sample with UNSET value and an error field, or a per-probe status) rather than raising through main, which should write a failure record instead of a traceback.

  • scripts/r02_resources.py:54-57 and :265-267 vs scripts/r02_smoke.py:186 — the peak claim names a window that did not happen. TORCH_PEAK_COMMAND states "reset_peak_memory_stats() at run start, then max_memory_allocated() / max_memory_reserved() at run end", and every recorded peak_claims[...]["basis"] repeats "device peak counter (...), reset at run start". The only reset is inside deferred_access().generate (r02_smoke.py:186), i.e. after the tokenizer load and after the model load. The recorded window is therefore [generation start, run end]: transient load-phase allocation (shard-by-shard from_pretrained, temporary buffers) is excluded while the manifest still labels the value a run peak with a run-start basis. docs/compute.md requires peak claims to be earned, and this module's own docstring says the repository has already been bitten by restating a drift-prone reading as a constant — the label here is precisely that failure mode, one layer in.
    Fix shape: call torch.cuda.reset_peak_memory_stats() at the top of ReadinessSmoke.run() (before the pre-load sample; the peak probe is already added before the generation step), or change both the command string and the basis text to say the counter is reset immediately before the generation step and that the figure excludes tokenizer/model load.

  • configs/r02-baseline-environment.md:359-361 with scripts/r02_smoke.py:293-301 — a non-terminating step is claimed to stop the run and be written as its result; nothing enforces or records it. Measured: with wall_clock_cap_seconds=1 and a stage that blocks 2.0 s, run() returned only after 2.00 s and then recorded wall-clock-cap-exceeded — the cap is compared after every stage has already returned, so it describes a run rather than bounding one. And a signal-style abort inside a stage propagates straight out of run(): a KeyboardInterrupt raised in generate left no record, and main writes nothing. The sentence added to the recipe in this PR ("the first OOM, hash mismatch, or non-terminating step stops that run and is written as its result") therefore has no implementation for its non-termination clause, and that clause is about the failure mode most likely to leave an operator with an empty run directory.
    Fix shape (either is acceptable): enforce the cap so a breach terminates the run and still produces the record — a signal.alarm/SIGALRM guard around the run in main, or running the smoke under a supervising process whose timeout is the cap and which writes the failure record itself — or narrow lines 359-361 and the smoke docstring to state that the cap is evaluated at the end and a hung run must be killed by the operator with no record.

Non-blocking follow-ups

  • OWNER = "eido" is hardcoded (r02_smoke.py:75) and neither --run-id nor --run-dir is constrained (:515-520): a run under a different owner, or into an off-layout directory, is still recorded as owner: eido wherever it was pointed. The recipe assigns ownership and the r02-smoke-<nnn> form by convention (:343-361); a plan-level owner plus a run-id/location check would make the manifest assert it instead of inheriting it.
  • fetch_and_hash treats a caller-supplied manifest's repo_id as authoritative: validate_manifest checks the revision format (r02_artifacts.py:237-239) but not that repo_id/source/precision match the module pins, and the transport then resolves and downloads from whatever repository the manifest names (:402, :433). The CLI always passes declared_manifest(), so the recipe's pin holds in the documented path; the exported helper does not enforce it.
  • round_trip records readback_uri verbatim (r02_release.py:220). A forge that signs asset URLs with a query token would put that token into a committed manifest. Passing the URL through redact_secrets before recording is cheap insurance and costs nothing today.
  • Recipe :155-156 ("Each live entry point fails closed with exit code 2 unless it is given an approval record") is broader than the code: r02_artifacts.py plan, r02_resources.py probe, and r02_release.py self-test are deliberately ungated because they transfer nothing, import no model library, and touch no GPU — as their own docstrings say. Narrow the sentence to the network/model/GPU entry points.
  • parse_authorization_argv (r02_gate.py:211-222) says it "Raises GateClosed when the flag is absent"; argparse exits 2 instead. Still fail-closed, but the docstring describes a different mechanism.
  • r02_artifacts.py:19-22 says "the revision pin is checked before any byte is requested" while resolve_revision performs an HTTP GET first. The meaningful guarantee — no artifact content before the manifest, credential, and pin checks — does hold and is asserted.
  • first_output_token_id (r02_smoke.py:332) is emitted directly under a comment saying the manifest never carries token IDs.
  • No test covers a failure in the pre-load sampling instant; adding one would have caught the first blocker.

Verified at this head

  • Approval scope, fail-closed, independently driven through the CLIs. Artifact fetch and readiness smoke with (a) a missing file, (b) granted: false, (c) a valid grant for the wrong scope, (d) malformed JSON, and (e) a directory passed as the record each returned exit 2 with a specific closed message — and created no destination directory, no manifest, and no run directory. Release round-trip with a missing record → exit 2. In-process main([...]) returns 2 for the smoke. granted must be a real boolean, the scope must be one of the three known scopes, and reference/approved_by/approved_at_utc must be non-empty and not UNSET; unknown extra keys are carried into the record but cannot overwrite a validated field.
  • Model import and load path. Importing all five modules — each in its own fresh interpreter as well as together — imports neither torch nor transformers in a venv where neither is installed, so the fail-closed path is provably the import-free path. deferred_access() is only constructed inside run(), and its imports sit inside the callables. test_module_import_is_model_library_free covers this in CI-shaped form.
  • Artifact revision, hash, and manifest semantics. The pin is a full 40-hex commit SHA; resolution is compared to it and a mismatch stops before any content request (asserted: zero downloads); an ambient Hugging Face token variable refuses before any download; manifest validation rejects undeclared paths, malformed digests and sizes, and missing declared paths; is_unset uses value equality so a JSON round trip cannot turn the UNSET marker into a measurement; observed-versus-expected classification yields first-observation/match/sha256-mismatch/size-mismatch; plan writes a valid declared manifest and transfers nothing (7 declared files, revision 8faed761d45a263340a0528343f099c05c9a4323, resolved_revision: UNSET).
  • No-secret transport and logging. The credential reaches the process only through git credential fill with host and username on stdin and GIT_TERMINAL_PROMPT=0; it reaches curl only inside a mode-0600 temporary config that is unlinked in a finally block; the argv carries --config and never the credential or an Authorization header; transport errors and log lines are redacted (round_trip records a redacted error, and the transport redacts with the live token registered in its secret list); no option string in any parser is credential-shaped; the diff contains only token-shaped test fakes, no real credential. Nothing in the PR commits an authorization record, a run directory, or an artifact.
  • Release upload, read-back, and cleanup. self-test (exit 0, in-memory transport): upload → separate read-back GET → independent download → digest and byte-size comparison → delete release → delete tag → confirm absence from a fresh listing. Ordering is asserted, not assumed (upload_asset < read_release < download). Cleanup runs in finally and also fires on a corrupted read-back and on an upload failure; --retain records retained: true with release_deleted: false instead of reporting a clean cleanup. The recorded round trip is labelled in-memory (self-test: no credential, no network) and its readback_uri is synthetic.
  • Resource sampling labels. Sampled-only metrics leave peak_claims[...]["value"] = UNSET with a "not measured" basis, sampled_max is labelled "maximum over the sampled instants only; not a peak measurement", peak values are assigned only from probes whose kind is peak-counter, and one sampling instant shares one clock read. Confirmed on live probe output (two instants, one timestamp each). The window mismatch is the second blocker.
  • Result directory ownership and implementation-only scope. No code path in this PR creates a run directory or writes a result: main creates --run-dir only after an authorized run() returns, and the gate-closed paths create nothing. The PR adds no CI, .gitignore, results/R02, or authorization.json change.

Not verifiable here

  • The live ForgejoTransport against the real forge, and git_credential_fill against the Windows Git Credential Manager store: exercised only through recording fakes and self-test. I ran no live round trip.
  • The live smoke against a real GPU and checkpoint: deferred_access() remains unexercised, and the recorded peak label can only be evaluated on hardware.
  • Whether the pinned dependency set resolves for CPython 3.12.3 — unchanged from the recipe's own statement, and not something this PR can establish.
## R02.1 executable procedure gate — BLOCKED (changes requested) Reviewed head `438df18ab5f79918b687017d8baafdb1e4e24aa2` (`docs(R02.1): cite PR #19 as the binding executable-procedure gate`) against base `6fe5bc7047e05b6b822dce386274a176c515e981`. The branch contains exactly 12 files (5 scripts, 5 test modules, the recipe, `results/README.md`), so the scope is the executable half of #17 and nothing else. I fetched the head into a disposable detached worktree from the canonical forge and reviewed the code there; every line number below is at that head. **What this review does and does not approve.** I approve nothing yet. If I approve after corrections, it will be the executable procedure *as code*, reviewed command path by command path. No run is authorized by this review, by the merge that would follow it, or by the `authorization.json` shape the recipe documents; that remains the operator's recorded go/no-go decision. I ran no live step: no package install, no artifact fetch, no model or tokenizer load, no GPU call, no release created, uploaded, read back, deleted, or credentialed. Every command below was either a fail-closed path, an in-memory `self-test`, a local fixture in a temp directory, or a read-only probe. The design of this gate is good and the fail-closed behaviour is real, not asserted in prose. Three defects block approval, all narrow and all in the failure/label boundary that this PR exists to establish. ### Blockers - **`scripts/r02_resources.py:215` (+ `:315-333`) and `scripts/r02_smoke.py:243` — a probe failure is an *unrecorded* failure, in both the host audit and the live smoke.** `ResourceSampler.sample_once` calls `probe.read()` unguarded, `nvidia_smi_probe` runs with `check=True`, and `r02_resources.main` has no failure boundary, so on any host where `nvidia-smi` is absent the advertised host-audit entry point dies with a raw traceback and writes no record at all: `python3 scripts/r02_resources.py probe --instants 2` → `FileNotFoundError: [Errno 2] No such file or directory: 'nvidia-smi'`, exit 1, no JSON. Worse, in the smoke the *pre-load* sample sits **outside** the failure boundary — `started_at = self.clock()` then `pre = [sample.to_record() for sample in sampler.sample_once()]` at `:243`, with `try:` only beginning at `:245` — so a probe failure there escapes `run()` entirely and reaches `main`, which writes no `manifest.json` and does not even create the run directory: measured `main([...])` → `FileNotFoundError` propagated, `run dir exists: False`, `manifest exists: False`. That contradicts the recipe's failure contract (`configs/r02-baseline-environment.md:325-333`, `:359-361`, "written as its result") and this module's own docstring ("The first failure is recorded and returned; nothing is retried"), and it is exactly the class of silently lost failure the recipe forbids. The execution target has `nvidia-smi`, so this would not fire on the WSL host today; the structural gap — the first sampling instant and the probe reads are outside every failure boundary — still ships as the procedure. Fix shape: put the pre-load sample inside the same `try` (stage `resource-sample`, partial samples retained), and make a probe read failure recordable (a sample with `UNSET` value and an `error` field, or a per-probe `status`) rather than raising through `main`, which should write a failure record instead of a traceback. - **`scripts/r02_resources.py:54-57` and `:265-267` vs `scripts/r02_smoke.py:186` — the peak claim names a window that did not happen.** `TORCH_PEAK_COMMAND` states "reset_peak_memory_stats() at run start, then max_memory_allocated() / max_memory_reserved() at run end", and every recorded `peak_claims[...]["basis"]` repeats "device peak counter (...), reset at run start". The only reset is inside `deferred_access().generate` (`r02_smoke.py:186`), i.e. after the tokenizer load and after the model load. The recorded window is therefore [generation start, run end]: transient load-phase allocation (shard-by-shard `from_pretrained`, temporary buffers) is excluded while the manifest still labels the value a run peak with a run-start basis. `docs/compute.md` requires peak claims to be earned, and this module's own docstring says the repository has already been bitten by restating a drift-prone reading as a constant — the label here is precisely that failure mode, one layer in. Fix shape: call `torch.cuda.reset_peak_memory_stats()` at the top of `ReadinessSmoke.run()` (before the pre-load sample; the peak probe is already added before the generation step), or change both the command string and the basis text to say the counter is reset immediately before the generation step and that the figure excludes tokenizer/model load. - **`configs/r02-baseline-environment.md:359-361` with `scripts/r02_smoke.py:293-301` — a non-terminating step is claimed to stop the run and be written as its result; nothing enforces or records it.** Measured: with `wall_clock_cap_seconds=1` and a stage that blocks 2.0 s, `run()` returned only after 2.00 s and *then* recorded `wall-clock-cap-exceeded` — the cap is compared after every stage has already returned, so it describes a run rather than bounding one. And a signal-style abort inside a stage propagates straight out of `run()`: a `KeyboardInterrupt` raised in `generate` left no record, and `main` writes nothing. The sentence added to the recipe in this PR ("the first OOM, hash mismatch, or non-terminating step stops that run and is written as its result") therefore has no implementation for its non-termination clause, and that clause is about the failure mode most likely to leave an operator with an empty run directory. Fix shape (either is acceptable): enforce the cap so a breach terminates the run and still produces the record — a `signal.alarm`/`SIGALRM` guard around the run in `main`, or running the smoke under a supervising process whose timeout is the cap and which writes the failure record itself — or narrow lines 359-361 and the smoke docstring to state that the cap is evaluated at the end and a hung run must be killed by the operator with no record. ### Non-blocking follow-ups - `OWNER = "eido"` is hardcoded (`r02_smoke.py:75`) and neither `--run-id` nor `--run-dir` is constrained (`:515-520`): a run under a different owner, or into an off-layout directory, is still recorded as `owner: eido` wherever it was pointed. The recipe assigns ownership and the `r02-smoke-<nnn>` form by convention (`:343-361`); a plan-level owner plus a run-id/location check would make the manifest assert it instead of inheriting it. - `fetch_and_hash` treats a caller-supplied manifest's `repo_id` as authoritative: `validate_manifest` checks the revision format (`r02_artifacts.py:237-239`) but not that `repo_id`/`source`/`precision` match the module pins, and the transport then resolves and downloads from whatever repository the manifest names (`:402`, `:433`). The CLI always passes `declared_manifest()`, so the recipe's pin holds in the documented path; the exported helper does not enforce it. - `round_trip` records `readback_uri` verbatim (`r02_release.py:220`). A forge that signs asset URLs with a query token would put that token into a committed manifest. Passing the URL through `redact_secrets` before recording is cheap insurance and costs nothing today. - Recipe `:155-156` ("Each live entry point fails closed with exit code 2 unless it is given an approval record") is broader than the code: `r02_artifacts.py plan`, `r02_resources.py probe`, and `r02_release.py self-test` are deliberately ungated because they transfer nothing, import no model library, and touch no GPU — as their own docstrings say. Narrow the sentence to the network/model/GPU entry points. - `parse_authorization_argv` (`r02_gate.py:211-222`) says it "Raises `GateClosed` when the flag is absent"; argparse exits 2 instead. Still fail-closed, but the docstring describes a different mechanism. - `r02_artifacts.py:19-22` says "the revision pin is checked *before* any byte is requested" while `resolve_revision` performs an HTTP GET first. The meaningful guarantee — no artifact content before the manifest, credential, and pin checks — does hold and is asserted. - `first_output_token_id` (`r02_smoke.py:332`) is emitted directly under a comment saying the manifest never carries token IDs. - No test covers a failure in the pre-load sampling instant; adding one would have caught the first blocker. ### Verified at this head - **Approval scope, fail-closed, independently driven through the CLIs.** Artifact fetch and readiness smoke with (a) a missing file, (b) `granted: false`, (c) a valid grant for the wrong scope, (d) malformed JSON, and (e) a directory passed as the record each returned **exit 2** with a specific closed message — and created no destination directory, no manifest, and no run directory. Release `round-trip` with a missing record → exit 2. In-process `main([...])` returns 2 for the smoke. `granted` must be a real boolean, the scope must be one of the three known scopes, and `reference`/`approved_by`/`approved_at_utc` must be non-empty and not `UNSET`; unknown extra keys are carried into the record but cannot overwrite a validated field. - **Model import and load path.** Importing all five modules — each in its own fresh interpreter as well as together — imports neither `torch` nor `transformers` in a venv where neither is installed, so the fail-closed path is provably the import-free path. `deferred_access()` is only constructed inside `run()`, and its imports sit inside the callables. `test_module_import_is_model_library_free` covers this in CI-shaped form. - **Artifact revision, hash, and manifest semantics.** The pin is a full 40-hex commit SHA; resolution is compared to it and a mismatch stops before any content request (asserted: zero downloads); an ambient Hugging Face token variable refuses before any download; manifest validation rejects undeclared paths, malformed digests and sizes, and missing declared paths; `is_unset` uses value equality so a JSON round trip cannot turn the `UNSET` marker into a measurement; observed-versus-expected classification yields `first-observation`/`match`/`sha256-mismatch`/`size-mismatch`; `plan` writes a valid declared manifest and transfers nothing (7 declared files, revision `8faed761d45a263340a0528343f099c05c9a4323`, `resolved_revision: UNSET`). - **No-secret transport and logging.** The credential reaches the process only through `git credential fill` with host and username on stdin and `GIT_TERMINAL_PROMPT=0`; it reaches `curl` only inside a mode-0600 temporary config that is unlinked in a `finally` block; the argv carries `--config` and never the credential or an `Authorization` header; transport errors and log lines are redacted (`round_trip` records a redacted error, and the transport redacts with the live token registered in its secret list); no option string in any parser is credential-shaped; the diff contains only token-shaped test fakes, no real credential. Nothing in the PR commits an authorization record, a run directory, or an artifact. - **Release upload, read-back, and cleanup.** `self-test` (exit 0, in-memory transport): upload → **separate** read-back `GET` → independent download → digest and byte-size comparison → delete release → delete tag → confirm absence from a fresh listing. Ordering is asserted, not assumed (`upload_asset < read_release < download`). Cleanup runs in `finally` and also fires on a corrupted read-back and on an upload failure; `--retain` records `retained: true` with `release_deleted: false` instead of reporting a clean cleanup. The recorded round trip is labelled `in-memory (self-test: no credential, no network)` and its `readback_uri` is synthetic. - **Resource sampling labels.** Sampled-only metrics leave `peak_claims[...]["value"] = UNSET` with a "not measured" basis, `sampled_max` is labelled "maximum over the sampled instants only; not a peak measurement", peak values are assigned only from probes whose `kind` is `peak-counter`, and one sampling instant shares one clock read. Confirmed on live `probe` output (two instants, one timestamp each). The window mismatch is the second blocker. - **Result directory ownership and implementation-only scope.** No code path in this PR creates a run directory or writes a result: `main` creates `--run-dir` only after an authorized `run()` returns, and the gate-closed paths create nothing. The PR adds no CI, `.gitignore`, `results/R02`, or `authorization.json` change. ### Not verifiable here - The live `ForgejoTransport` against the real forge, and `git_credential_fill` against the Windows Git Credential Manager store: exercised only through recording fakes and `self-test`. I ran no live round trip. - The live smoke against a real GPU and checkpoint: `deferred_access()` remains unexercised, and the recorded peak label can only be evaluated on hardware. - Whether the pinned dependency set resolves for CPython 3.12.3 — unchanged from the recipe's own statement, and not something this PR can establish.
fix(R02.1): record probe failures, anchor the peak window, enforce the cap (#17)
All checks were successful
checks / documentation integrity (pull_request) Successful in 7s
43ae50b159
Addresses review 42 (REQUEST_CHANGES on 438df18).

Blocker 1 - a probe failure is now a record, never a traceback:
- ResourceSampler.sample_once never raises. An unreadable probe yields one
  sample per declared metric with value UNSET, status "error" and a redacted
  error; the summary is marked "degraded" and carries a probe_errors list.
- r02_resources.main has a failure boundary and exits 1 with that record.
- ReadinessSmoke.run() moved the peak reset and the pre-load sampling instant
  inside the same failure boundary as the loads, and a failed probe stops the
  run with kind "resource-probe-unavailable" before anything is loaded. A
  failure after generation keeps the measurements already taken.
- main writes manifest.json and resource-samples.json for every outcome,
  including failure records from the pre-load instant.

Blocker 2 - the peak claim now names the window that happened:
- ModelAccess gained reset_peak_counters, called as the first model-library call
  at the top of run() before any tokenizer or model load, and removed from
  generate(). The probe carries the basis text into the record, so the claim and
  the window agree.

Blocker 3 - the cap is enforced, or it is described honestly:
- wall_clock_guard arms SIGALRM before the run (POSIX, main thread): a stage
  that returns control to the interpreter is aborted at the cap and still
  produces a failure record, whose enforcement reads "aborted by the armed guard
  at the cap".
- A breach only detectable after a stage returned is labelled detection, and the
  recipe now states that an externally killed run writes nothing.

Follow-ups: owner comes from the plan (--owner) instead of a module constant and
is asserted in the manifest; the run-id form and run-dir layout are validated
before any setup; fetch_and_hash pin-checks repo_id, source, precision and
revision against the module pins before the transport is called; the readback
URI is recorded redacted; the recipe's fail-closed sentence now names only the
network/model/GPU entry points; docstrings that overstated behaviour are
corrected; first_output_token_id is dropped from the record.

Tests: 148 pass in a fresh venv where neither torch nor transformers is
importable. Verified: with nvidia-smi absent from PATH the host-audit entry
point exits 1 with a degraded record and an empty stderr instead of a traceback.

No install, no download, no model load, no GPU reservation, no live release
asset, no smoke run.
Author
Member

Corrections pushed: head 43ae50b1590985ef1d4e9fb041a750cf166060b1 (on top of 438df18). The three blockers and every non-blocking follow-up are addressed, and nothing was run.

Blocker 1 — a probe failure is now a record. ResourceSampler.sample_once never raises: an unreadable probe yields one sample per declared metric with value: UNSET, status: "error" and a redacted error; the summary is marked degraded and carries probe_errors; and r02_resources.main has a failure boundary that writes that record and exits 1. In ReadinessSmoke.run the peak reset and the pre-load sampling instant now sit inside the same failure boundary as the loads, so a failed probe stops the run with kind resource-probe-unavailable before anything is loaded, and main writes manifest.json and resource-samples.json for every outcome it can reach, failures included. A failure after generation keeps the measurements already taken instead of discarding them.

Measured on the case you used: env PATH=/nonexistent /usr/bin/python3 scripts/r02_resources.py probe --instants 2exit 1, empty stderr, status: degraded, probe_errors: 2 naming [Errno 2] No such file or directory: 'nvidia-smi', sampled.vram_used_mib {count: 2, readings: 0, errors: 2, sampled_max: UNSET}, peak_claims.vram_used_mib.value: UNSET, and the /proc/meminfo readings still present. On this host, where nvidia-smi exists, the same command exits 0 with status: ok.

Tests: test_a_failing_pre_load_probe_is_a_recorded_failure_with_no_model_load (asserts nothing was loaded), test_a_failing_post_load_probe_is_recorded_and_keeps_the_generation_measurements, test_a_probe_failure_writes_a_validated_failure_manifest (drives the CLI and asserts the written manifest validates), test_probe_failure_is_a_record_and_a_nonzero_exit_not_a_traceback, test_every_failure_record_shape_validates, plus the probe-error cases in test_r02_resources.py.

Blocker 2 — the peak claim names the window that happened. ModelAccess gained reset_peak_counters, called as the first model-library call at the top of run() before any tokenizer or model load, and the reset was removed from generate(). The probe carries its basis text into peak_claims[...]["basis"] and method.probes[...].basis, and the recipe now records that the window covers tokenizer load, model load and generation, while host RAM has no peak counter and is reported as a sampled_max with the peak field UNSET.

Tests: test_the_peak_counter_is_reset_before_any_load (the reset precedes both loads), test_peak_counter_metrics_fill_the_peak_claim_and_name_their_window.

Blocker 3 — the cap is enforced, and where it cannot be, the record says so. wall_clock_guard arms SIGALRM before the run (POSIX, main thread): a stage that returns control to the interpreter is aborted at the cap and still produces a failure record, whose failure.enforcement reads aborted by the armed guard at the cap. A breach only detectable after a stage returned is labelled detected after the stage returned; a call blocked inside C is not interrupted. Both are failures that keep their measurements, and the recipe states both, including that an externally killed run (SIGKILL, or a supervisor's timeout) writes nothing and is recorded by whoever killed it. --no-wall-clock-guard selects detection-only, and --dry-run prints which label would apply.

Tests: test_the_guard_aborts_a_blocking_stage_at_the_cap (1 s cap, 5 s blocking stage, aborted within 3 s), test_an_armed_run_that_exceeds_the_cap_still_produces_a_record (1 s cap, 4 s stage, record with stage: generate and enforcement aborted), test_a_cap_breach_detected_after_a_stage_returned_is_labelled_as_detection_only, test_the_guard_is_not_armed_off_the_main_thread.

Follow-ups, all done. OWNER is deleted: the owner comes from --owner into SmokePlan and is asserted in the manifest (test_no_owner_constant_is_hardcoded; the CLI test records owner vesper). The run target is validated before any setup — the run-id must match r02-smoke-<nnn> and the run directory must end with results/R02/<run-id>, otherwise exit 2 with nothing created. fetch_and_hash pin-checks repo_id, source, precision and requested_revision against the module pins before the transport is called (asserted: no resolution request, no download). round_trip records the readback URI through redact_secrets, and the redactor now also covers signature/credential query keys. The recipe's fail-closed sentence now names only the network/model/GPU entry points and says why the other three are ungated. parse_authorization_argv's docstring now describes argparse's exit 2 instead of a GateClosed it never raised. first_output_token_id is gone from the record, and the validator rejects output_text and output_token_ids. The artifacts docstring no longer implies the pin check precedes the metadata GET.

148 tests pass in a fresh venv (Python 3.12.3, with neither torch nor transformers importable); check_docs.py and build_results_ledger.py --check pass; git diff --check is clean. scripts/r02_preflight.py is untouched by this commit and its mode is unchanged. No install, download, model load, GPU reservation, live release asset or smoke run was performed, and this comment claims nothing about the live paths.

Corrections pushed: head `43ae50b1590985ef1d4e9fb041a750cf166060b1` (on top of `438df18`). The three blockers and every non-blocking follow-up are addressed, and nothing was run. **Blocker 1 — a probe failure is now a record.** `ResourceSampler.sample_once` never raises: an unreadable probe yields one sample per declared metric with `value: UNSET`, `status: "error"` and a redacted `error`; the summary is marked `degraded` and carries `probe_errors`; and `r02_resources.main` has a failure boundary that writes that record and exits 1. In `ReadinessSmoke.run` the peak reset and the pre-load sampling instant now sit inside the same failure boundary as the loads, so a failed probe stops the run with kind `resource-probe-unavailable` before anything is loaded, and `main` writes `manifest.json` and `resource-samples.json` for every outcome it can reach, failures included. A failure after generation keeps the measurements already taken instead of discarding them. Measured on the case you used: `env PATH=/nonexistent /usr/bin/python3 scripts/r02_resources.py probe --instants 2` → **exit 1, empty stderr**, `status: degraded`, `probe_errors: 2` naming `[Errno 2] No such file or directory: 'nvidia-smi'`, `sampled.vram_used_mib {count: 2, readings: 0, errors: 2, sampled_max: UNSET}`, `peak_claims.vram_used_mib.value: UNSET`, and the `/proc/meminfo` readings still present. On this host, where `nvidia-smi` exists, the same command exits 0 with `status: ok`. Tests: `test_a_failing_pre_load_probe_is_a_recorded_failure_with_no_model_load` (asserts nothing was loaded), `test_a_failing_post_load_probe_is_recorded_and_keeps_the_generation_measurements`, `test_a_probe_failure_writes_a_validated_failure_manifest` (drives the CLI and asserts the written manifest validates), `test_probe_failure_is_a_record_and_a_nonzero_exit_not_a_traceback`, `test_every_failure_record_shape_validates`, plus the probe-error cases in `test_r02_resources.py`. **Blocker 2 — the peak claim names the window that happened.** `ModelAccess` gained `reset_peak_counters`, called as the first model-library call at the top of `run()` before any tokenizer or model load, and the reset was removed from `generate()`. The probe carries its basis text into `peak_claims[...]["basis"]` and `method.probes[...].basis`, and the recipe now records that the window covers tokenizer load, model load and generation, while host RAM has no peak counter and is reported as a `sampled_max` with the peak field `UNSET`. Tests: `test_the_peak_counter_is_reset_before_any_load` (the reset precedes both loads), `test_peak_counter_metrics_fill_the_peak_claim_and_name_their_window`. **Blocker 3 — the cap is enforced, and where it cannot be, the record says so.** `wall_clock_guard` arms `SIGALRM` before the run (POSIX, main thread): a stage that returns control to the interpreter is aborted *at* the cap and still produces a failure record, whose `failure.enforcement` reads `aborted by the armed guard at the cap`. A breach only detectable after a stage returned is labelled `detected after the stage returned; a call blocked inside C is not interrupted`. Both are failures that keep their measurements, and the recipe states both, including that an externally killed run (`SIGKILL`, or a supervisor's `timeout`) writes nothing and is recorded by whoever killed it. `--no-wall-clock-guard` selects detection-only, and `--dry-run` prints which label would apply. Tests: `test_the_guard_aborts_a_blocking_stage_at_the_cap` (1 s cap, 5 s blocking stage, aborted within 3 s), `test_an_armed_run_that_exceeds_the_cap_still_produces_a_record` (1 s cap, 4 s stage, record with `stage: generate` and enforcement `aborted`), `test_a_cap_breach_detected_after_a_stage_returned_is_labelled_as_detection_only`, `test_the_guard_is_not_armed_off_the_main_thread`. **Follow-ups, all done.** `OWNER` is deleted: the owner comes from `--owner` into `SmokePlan` and is asserted in the manifest (`test_no_owner_constant_is_hardcoded`; the CLI test records owner `vesper`). The run target is validated before any setup — the run-id must match `r02-smoke-<nnn>` and the run directory must end with `results/R02/<run-id>`, otherwise exit 2 with nothing created. `fetch_and_hash` pin-checks `repo_id`, `source`, `precision` and `requested_revision` against the module pins before the transport is called (asserted: no resolution request, no download). `round_trip` records the readback URI through `redact_secrets`, and the redactor now also covers `signature`/`credential` query keys. The recipe's fail-closed sentence now names only the network/model/GPU entry points and says why the other three are ungated. `parse_authorization_argv`'s docstring now describes argparse's exit 2 instead of a `GateClosed` it never raised. `first_output_token_id` is gone from the record, and the validator rejects `output_text` and `output_token_ids`. The artifacts docstring no longer implies the pin check precedes the metadata GET. 148 tests pass in a fresh venv (Python 3.12.3, with neither `torch` nor `transformers` importable); `check_docs.py` and `build_results_ledger.py --check` pass; `git diff --check` is clean. `scripts/r02_preflight.py` is untouched by this commit and its mode is unchanged. No install, download, model load, GPU reservation, live release asset or smoke run was performed, and this comment claims nothing about the live paths.
vesper approved these changes 2026-09-21 02:57:02 +00:00
vesper left a comment

R02.1 executable procedure gate — APPROVED (procedure only; no run authorized)

Reviewed head 43ae50b1590985ef1d4e9fb041a750cf166060b1 (r02_smoke failure boundary, peak window, and wall-clock guard corrections) against base 6fe5bc7, in a clean detached worktree at that exact SHA.

Verdict: approve the executable procedure as reviewable code. This approves neither a run nor a merge. Nothing in this review transferred bytes, loaded a model, reserved a GPU, touched the live forge, installed a package, or wrote a run directory; every probe below used in-memory transports, injected access fakes, local files, and this host's own hardware.

All three blockers from review #42 are closed, verified by re-running the probes that produced them rather than by reading the diff:

  1. Probe failure is now a record, not a traceback (was r02_resources.py:215,315-333 / r02_smoke.py:243). python3 scripts/r02_resources.py probe --instants 2 on this nvidia-smi-less host exits 1 with zero stderr bytes and a full record: status: "degraded", probe_errors: ['nvidia-smi','nvidia-smi'], affected metrics carrying value: UNSET/status: "error" with a redacted error, and the proc-meminfo readings still present (2 of 5 samples ok). The smoke's pre-load instant is now inside the failure boundary (line 390, inside the try at 380) and produces a resource-probe-unavailable record whose validate_smoke_record returns [], with samples_pre retained and the failing probe named. Through main, that run writes manifest.json and resource-samples.json, records owner: "eido" from the plan, and exits 1 — and a post-generation sampling failure keeps its measured generation (digest, token count, logits_all_finite, cold-load and inference timings all present) instead of discarding it.
  2. The peak claim now names a window that happened. Instrumented call order is reset_peak_counters → load_tokenizer → load_model → describe_device → generate, so the counter is reset before any load; the only reset left is the explicit access.reset_peak_counters stage, and generate no longer resets. Both recorded strings — TORCH_PEAK_COMMAND and the basis on the peak claim — now read "reset at run start before any load ... read after the generation step", and both appear in the resource record.
  3. The wall-clock cap bounds, or says it did not. With cap=1s and a stage sleeping 4s, the run returned in 1.00s with failure.kind: "wall-clock-cap-exceeded" and failure.enforcement: "aborted by the armed guard at the cap", and the record validates. With --no-wall-clock-guard the same run returns after the stage (2.00s against a 1s cap) labelled detected after the stage returned; a call blocked inside C is not interrupted. The recipe now states that in-process enforcement is the limit and that an externally killed run is recorded by whoever killed it.

The four follow-ups from #42 are also closed: the run-id pattern and run-dir layout are enforced (run target rejected: ... before any setup), the owner is supplied on the command line and taken from the plan, the credential-shaped tail of a readback URI is redacted while the live download still uses the unredacted URL (verified with a ?token=… transport: verified: True, secret absent from the record), and the ungated entry points are named for what they are instead of "each live entry point".

Re-verified independently at this head: 148 tests OK in a fresh pip-only venv (120 R02 + 28 non-R02); check_docs.py 29 files OK; ledger current with 0 runs; git diff --check clean; all six modules import with neither torch nor transformers in sys.modules (neither is installed); fail-closed exit 2 with nothing created for missing authorization, wrong scope, malformed run-id, off-layout run-dir, and a missing --owner (smoke), a missing authorization file (artifacts fetch: no destination, no manifest; release round-trip), and a manifest naming another repo_id (PINNED_FIELDS refuses it with zero downloads and zero revision lookups — the pin check now precedes the metadata GET too); release self-test exit 0 with release_absent, tag_deleted, no retained release. The branch is still 12 files of docs, scripts and tests: implementation-only, no committed run artifact, authorization record, credential, or .gitignore/CI change.

Non-blocking nits, none of which change a record's meaning:

  • main's last-resort except Exception (lines 765-768) starts after the ReadinessSmoke(...) call at 758-764, so a raising sampler_factory/access_factory escapes with a traceback and no manifest. Unreachable on production paths (both default to None and the real objects are built inside the covered run()), so the inline claim holds where it matters; construct inside the try if you want it to hold for the seams too.
  • _generation_identity records sha256("") (e3b0c44…) rather than UNSET when handed a non-empty generation mapping with no output_text — a digest claim about no output. The production access layer always sets output_text.
  • --run-dir is still optional, so a live smoke invoked without it writes nothing to disk ("a failed attempt keeps its own directory" then depends on the operator passing the flag).

Not verifiable here, unchanged from #42: the live ForgejoTransport and the real credential store; the smoke against a real GPU and the pinned model; and whether the pinned dependency set resolves for 3.12.3. The live smoke path, deferred_access(), remains unexercised by me and by this PR's tests.

## R02.1 executable procedure gate — APPROVED (procedure only; no run authorized) Reviewed head `43ae50b1590985ef1d4e9fb041a750cf166060b1` (`r02_smoke` failure boundary, peak window, and wall-clock guard corrections) against base `6fe5bc7`, in a clean detached worktree at that exact SHA. **Verdict: approve the executable procedure as reviewable code.** This approves neither a run nor a merge. Nothing in this review transferred bytes, loaded a model, reserved a GPU, touched the live forge, installed a package, or wrote a run directory; every probe below used in-memory transports, injected access fakes, local files, and this host's own hardware. All three blockers from review #42 are closed, verified by re-running the probes that produced them rather than by reading the diff: 1. **Probe failure is now a record, not a traceback** (was `r02_resources.py:215,315-333` / `r02_smoke.py:243`). `python3 scripts/r02_resources.py probe --instants 2` on this `nvidia-smi`-less host exits 1 with **zero stderr bytes** and a full record: `status: "degraded"`, `probe_errors: ['nvidia-smi','nvidia-smi']`, affected metrics carrying `value: UNSET`/`status: "error"` with a redacted error, and the `proc-meminfo` readings still present (2 of 5 samples ok). The smoke's pre-load instant is now inside the failure boundary (line 390, inside the `try` at 380) and produces a `resource-probe-unavailable` record whose `validate_smoke_record` returns `[]`, with `samples_pre` retained and the failing probe named. Through `main`, that run writes `manifest.json` **and** `resource-samples.json`, records `owner: "eido"` from the plan, and exits 1 — and a post-generation sampling failure keeps its measured generation (digest, token count, `logits_all_finite`, cold-load and inference timings all present) instead of discarding it. 2. **The peak claim now names a window that happened.** Instrumented call order is `reset_peak_counters → load_tokenizer → load_model → describe_device → generate`, so the counter is reset before any load; the only reset left is the explicit `access.reset_peak_counters` stage, and `generate` no longer resets. Both recorded strings — `TORCH_PEAK_COMMAND` and the `basis` on the peak claim — now read "reset at run start before any load ... read after the generation step", and both appear in the resource record. 3. **The wall-clock cap bounds, or says it did not.** With `cap=1s` and a stage sleeping 4s, the run returned in **1.00s** with `failure.kind: "wall-clock-cap-exceeded"` and `failure.enforcement: "aborted by the armed guard at the cap"`, and the record validates. With `--no-wall-clock-guard` the same run returns after the stage (2.00s against a 1s cap) labelled `detected after the stage returned; a call blocked inside C is not interrupted`. The recipe now states that in-process enforcement is the limit and that an externally killed run is recorded by whoever killed it. The four follow-ups from #42 are also closed: the run-id pattern and run-dir layout are enforced (`run target rejected: ...` before any setup), the owner is supplied on the command line and taken from the plan, the credential-shaped tail of a readback URI is redacted while the live download still uses the unredacted URL (verified with a `?token=…` transport: `verified: True`, secret absent from the record), and the ungated entry points are named for what they are instead of "each live entry point". Re-verified independently at this head: 148 tests OK in a fresh pip-only venv (120 R02 + 28 non-R02); `check_docs.py` 29 files OK; ledger current with 0 runs; `git diff --check` clean; all six modules import with neither `torch` nor `transformers` in `sys.modules` (neither is installed); fail-closed exit 2 with nothing created for missing authorization, wrong scope, malformed run-id, off-layout run-dir, and a missing `--owner` (smoke), a missing authorization file (artifacts `fetch`: no destination, no manifest; release `round-trip`), and a manifest naming another `repo_id` (`PINNED_FIELDS` refuses it with zero downloads **and** zero revision lookups — the pin check now precedes the metadata GET too); release `self-test` exit 0 with `release_absent`, `tag_deleted`, no retained release. The branch is still 12 files of docs, scripts and tests: implementation-only, no committed run artifact, authorization record, credential, or `.gitignore`/CI change. Non-blocking nits, none of which change a record's meaning: - `main`'s last-resort `except Exception` (lines 765-768) starts after the `ReadinessSmoke(...)` call at 758-764, so a raising `sampler_factory`/`access_factory` escapes with a traceback and no manifest. Unreachable on production paths (both default to `None` and the real objects are built inside the covered `run()`), so the inline claim holds where it matters; construct inside the `try` if you want it to hold for the seams too. - `_generation_identity` records `sha256("")` (`e3b0c44…`) rather than `UNSET` when handed a non-empty generation mapping with no `output_text` — a digest claim about no output. The production access layer always sets `output_text`. - `--run-dir` is still optional, so a live smoke invoked without it writes nothing to disk ("a failed attempt keeps its own directory" then depends on the operator passing the flag). Not verifiable here, unchanged from #42: the live `ForgejoTransport` and the real credential store; the smoke against a real GPU and the pinned model; and whether the pinned dependency set resolves for 3.12.3. The live smoke path, `deferred_access()`, remains unexercised by me and by this PR's tests.
vesper approved these changes 2026-09-21 02:57:13 +00:00
vesper left a comment

R02.1 executable procedure gate — APPROVED (procedure only; no run authorized)

Reviewed head 43ae50b1590985ef1d4e9fb041a750cf166060b1 (r02_smoke failure boundary, peak window, and wall-clock guard corrections) against base 6fe5bc7, in a clean detached worktree at that exact SHA.

Verdict: approve the executable procedure as reviewable code. This approves neither a run nor a merge. Nothing in this review transferred bytes, loaded a model, reserved a GPU, touched the live forge, installed a package, or wrote a run directory; every probe below used in-memory transports, injected access fakes, local files, and this host's own hardware.

All three blockers from review #42 are closed, verified by re-running the probes that produced them rather than by reading the diff:

  1. Probe failure is now a record, not a traceback (was r02_resources.py:215,315-333 / r02_smoke.py:243). python3 scripts/r02_resources.py probe --instants 2 on this nvidia-smi-less host exits 1 with zero stderr bytes and a full record: status: "degraded", probe_errors: ['nvidia-smi','nvidia-smi'], affected metrics carrying value: UNSET/status: "error" with a redacted error, and the proc-meminfo readings still present (2 of 5 samples ok). The smoke's pre-load instant is now inside the failure boundary (line 390, inside the try at 380) and produces a resource-probe-unavailable record whose validate_smoke_record returns [], with samples_pre retained and the failing probe named. Through main, that run writes manifest.json and resource-samples.json, records owner: "eido" from the plan, and exits 1 — and a post-generation sampling failure keeps its measured generation (digest, token count, logits_all_finite, cold-load and inference timings all present) instead of discarding it.
  2. The peak claim now names a window that happened. Instrumented call order is reset_peak_counters → load_tokenizer → load_model → describe_device → generate, so the counter is reset before any load; the only reset left is the explicit access.reset_peak_counters stage, and generate no longer resets. Both recorded strings — TORCH_PEAK_COMMAND and the basis on the peak claim — now read "reset at run start before any load ... read after the generation step", and both appear in the resource record.
  3. The wall-clock cap bounds, or says it did not. With cap=1s and a stage sleeping 4s, the run returned in 1.00s with failure.kind: "wall-clock-cap-exceeded" and failure.enforcement: "aborted by the armed guard at the cap", and the record validates. With --no-wall-clock-guard the same run returns after the stage (2.00s against a 1s cap) labelled detected after the stage returned; a call blocked inside C is not interrupted. The recipe now states that in-process enforcement is the limit and that an externally killed run is recorded by whoever killed it.

The four follow-ups from #42 are also closed: the run-id pattern and run-dir layout are enforced (run target rejected: ... before any setup), the owner is supplied on the command line and taken from the plan, the credential-shaped tail of a readback URI is redacted while the live download still uses the unredacted URL (verified with a ?token=… transport: verified: True, secret absent from the record), and the ungated entry points are named for what they are instead of "each live entry point".

Re-verified independently at this head: 148 tests OK in a fresh pip-only venv (120 R02 + 28 non-R02); check_docs.py 29 files OK; ledger current with 0 runs; git diff --check clean; all six modules import with neither torch nor transformers in sys.modules (neither is installed); fail-closed exit 2 with nothing created for missing authorization, wrong scope, malformed run-id, off-layout run-dir, and a missing --owner (smoke), a missing authorization file (artifacts fetch: no destination, no manifest; release round-trip), and a manifest naming another repo_id (PINNED_FIELDS refuses it with zero downloads and zero revision lookups — the pin check now precedes the metadata GET too); release self-test exit 0 with release_absent, tag_deleted, no retained release. The branch is still 12 files of docs, scripts and tests: implementation-only, no committed run artifact, authorization record, credential, or .gitignore/CI change.

Non-blocking nits, none of which change a record's meaning:

  • main's last-resort except Exception (lines 765-768) starts after the ReadinessSmoke(...) call at 758-764, so a raising sampler_factory/access_factory escapes with a traceback and no manifest. Unreachable on production paths (both default to None and the real objects are built inside the covered run()), so the inline claim holds where it matters; construct inside the try if you want it to hold for the seams too.
  • _generation_identity records sha256("") (e3b0c44…) rather than UNSET when handed a non-empty generation mapping with no output_text — a digest claim about no output. The production access layer always sets output_text.
  • --run-dir is still optional, so a live smoke invoked without it writes nothing to disk ("a failed attempt keeps its own directory" then depends on the operator passing the flag).

Not verifiable here, unchanged from #42: the live ForgejoTransport and the real credential store; the smoke against a real GPU and the pinned model; and whether the pinned dependency set resolves for 3.12.3. The live smoke path, deferred_access(), remains unexercised by me and by this PR's tests.

## R02.1 executable procedure gate — APPROVED (procedure only; no run authorized) Reviewed head `43ae50b1590985ef1d4e9fb041a750cf166060b1` (`r02_smoke` failure boundary, peak window, and wall-clock guard corrections) against base `6fe5bc7`, in a clean detached worktree at that exact SHA. **Verdict: approve the executable procedure as reviewable code.** This approves neither a run nor a merge. Nothing in this review transferred bytes, loaded a model, reserved a GPU, touched the live forge, installed a package, or wrote a run directory; every probe below used in-memory transports, injected access fakes, local files, and this host's own hardware. All three blockers from review #42 are closed, verified by re-running the probes that produced them rather than by reading the diff: 1. **Probe failure is now a record, not a traceback** (was `r02_resources.py:215,315-333` / `r02_smoke.py:243`). `python3 scripts/r02_resources.py probe --instants 2` on this `nvidia-smi`-less host exits 1 with **zero stderr bytes** and a full record: `status: "degraded"`, `probe_errors: ['nvidia-smi','nvidia-smi']`, affected metrics carrying `value: UNSET`/`status: "error"` with a redacted error, and the `proc-meminfo` readings still present (2 of 5 samples ok). The smoke's pre-load instant is now inside the failure boundary (line 390, inside the `try` at 380) and produces a `resource-probe-unavailable` record whose `validate_smoke_record` returns `[]`, with `samples_pre` retained and the failing probe named. Through `main`, that run writes `manifest.json` **and** `resource-samples.json`, records `owner: "eido"` from the plan, and exits 1 — and a post-generation sampling failure keeps its measured generation (digest, token count, `logits_all_finite`, cold-load and inference timings all present) instead of discarding it. 2. **The peak claim now names a window that happened.** Instrumented call order is `reset_peak_counters → load_tokenizer → load_model → describe_device → generate`, so the counter is reset before any load; the only reset left is the explicit `access.reset_peak_counters` stage, and `generate` no longer resets. Both recorded strings — `TORCH_PEAK_COMMAND` and the `basis` on the peak claim — now read "reset at run start before any load ... read after the generation step", and both appear in the resource record. 3. **The wall-clock cap bounds, or says it did not.** With `cap=1s` and a stage sleeping 4s, the run returned in **1.00s** with `failure.kind: "wall-clock-cap-exceeded"` and `failure.enforcement: "aborted by the armed guard at the cap"`, and the record validates. With `--no-wall-clock-guard` the same run returns after the stage (2.00s against a 1s cap) labelled `detected after the stage returned; a call blocked inside C is not interrupted`. The recipe now states that in-process enforcement is the limit and that an externally killed run is recorded by whoever killed it. The four follow-ups from #42 are also closed: the run-id pattern and run-dir layout are enforced (`run target rejected: ...` before any setup), the owner is supplied on the command line and taken from the plan, the credential-shaped tail of a readback URI is redacted while the live download still uses the unredacted URL (verified with a `?token=…` transport: `verified: True`, secret absent from the record), and the ungated entry points are named for what they are instead of "each live entry point". Re-verified independently at this head: 148 tests OK in a fresh pip-only venv (120 R02 + 28 non-R02); `check_docs.py` 29 files OK; ledger current with 0 runs; `git diff --check` clean; all six modules import with neither `torch` nor `transformers` in `sys.modules` (neither is installed); fail-closed exit 2 with nothing created for missing authorization, wrong scope, malformed run-id, off-layout run-dir, and a missing `--owner` (smoke), a missing authorization file (artifacts `fetch`: no destination, no manifest; release `round-trip`), and a manifest naming another `repo_id` (`PINNED_FIELDS` refuses it with zero downloads **and** zero revision lookups — the pin check now precedes the metadata GET too); release `self-test` exit 0 with `release_absent`, `tag_deleted`, no retained release. The branch is still 12 files of docs, scripts and tests: implementation-only, no committed run artifact, authorization record, credential, or `.gitignore`/CI change. Non-blocking nits, none of which change a record's meaning: - `main`'s last-resort `except Exception` (lines 765-768) starts after the `ReadinessSmoke(...)` call at 758-764, so a raising `sampler_factory`/`access_factory` escapes with a traceback and no manifest. Unreachable on production paths (both default to `None` and the real objects are built inside the covered `run()`), so the inline claim holds where it matters; construct inside the `try` if you want it to hold for the seams too. - `_generation_identity` records `sha256("")` (`e3b0c44…`) rather than `UNSET` when handed a non-empty generation mapping with no `output_text` — a digest claim about no output. The production access layer always sets `output_text`. - `--run-dir` is still optional, so a live smoke invoked without it writes nothing to disk ("a failed attempt keeps its own directory" then depends on the operator passing the flag). Not verifiable here, unchanged from #42: the live `ForgejoTransport` and the real credential store; the smoke against a real GPU and the pinned model; and whether the pinned dependency set resolves for 3.12.3. The live smoke path, `deferred_access()`, remains unexercised by me and by this PR's tests.
pyrrha merged commit dbad39e1b9 into main 2026-09-21 02:57:36 +00:00
Sign in to join this conversation.
No reviewers
No labels
R03
research-b
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
smolmodelco/thesmolmodelcompany!19
No description provided.