feat(R02.1): executable procedure gate — pinned fetch/hash, gated smoke, release round trip (#17) #19
Loading…
Reference in a new issue
No description provided.
Delete branch "R02/executable-procedure-gate"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Task / experiment ID
R02.1 — issue #17. Branched from canonical
mainat merge6fe5bc7. 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.
scripts/r02_gate.pygranted/scope/reference/approved_by/approved_at_utc), fail-closedrequire_authorization, one scope per live step,redact_secrets, andcredential_environment_conflicts.scripts/r02_artifacts.pyQwen/Qwen2.5-1.5Bat8faed761d45a263340a0528343f099c05c9a4323;fetch_and_hashchecks manifest structure, ambient-credential absence and the revision pin before requesting any content, then hashes and compares every file. Unmeasured fields stayUNSET.scripts/r02_smoke.pyReadinessSmokecallsrequire_authorizationbefore the deferred access layer is constructed, so an ungated call imports neithertorchnortransformersand makes no GPU call. One inference, one attempt, failures recorded withretried: false.scripts/r02_release.pyGETfor the download URL → independent download and hash → cleanup infinallywith an absence confirmation. Credential comes from the pre-provisioned git credential helper into a mode-0600 curl config file, never argv.scripts/r02_resources.pysampled_maxis labelled a sampled maximum and the peak field staysUNSET.tests/test_r02_{gate,artifacts,smoke,release,resources}.pyconfigs/r02-baseline-environment.mdresults/README.mdTwo defects the new tests found were fixed in the same commit: identity comparison against the
UNSETmarker broke as soon as a manifest had been through a JSON round trip (nowis_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, head43ae50b, which includes the review-42 corrections on top of438df18and5f8e5af(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):Repository checks:
Command paths exercised. Nothing was downloaded, loaded, uploaded, or reserved:
The
self-testrecord comes fromInMemoryReleaseTransport: itsreadback_uriis 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
pippresent, and neithertorchnortransformersis importable there.verify-failedwith the offending path named.GET, then an independent download, then cleanup; a corrupted readback is detected and cleanup still runs.curlargv contains no credential, the temporary config file is mode 0600 and removed afterwards, and transport errors and log lines are redacted.check_docs.pyandbuild_results_ledger.py --checkpass, andgit diff --checkis clean.What was not verified
expected_sha256/expected_bytesfields are stillUNSET. The declared seven-file set is a declaration, not an observation; a missing file will fail loudly at the first fetch.ForgejoTransportandgit_credential_fillpaths 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.Resource usage
N/A — implementation and local unit tests only. No GPU time, no model download, no paid resource.
Remaining uncertainty
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 isR02/executable-procedure-gate; nothing was force-pushed and no other branch was modified. BranchR02/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 inr02_smoke.py, the fail-closed conditions inr02_artifacts.py, the separate read-back inr02_release.py, and the peak-versus-sampled labels inr02_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
Coordination note for the reviewer, from
eido.Remote branch
R02/preflight-corrections(head74fb0ff, "docs(R02): apply review-39 follow-ups; cite gate issue #17") also editsconfigs/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.pyis mode-only (0 insertions, 0 deletions on the blob content), so the line-number citationscripts/r02_preflight.py:33-35in this recipe still resolves to thefetch_and_hashseam 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.
R02.1 executable procedure gate — BLOCKED (changes requested)
Reviewed head
438df18ab5f79918b687017d8baafdb1e4e24aa2(docs(R02.1): cite PR #19 as the binding executable-procedure gate) against base6fe5bc7047e05b6b822dce386274a176c515e981. 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.jsonshape 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-memoryself-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) andscripts/r02_smoke.py:243— a probe failure is an unrecorded failure, in both the host audit and the live smoke.ResourceSampler.sample_oncecallsprobe.read()unguarded,nvidia_smi_proberuns withcheck=True, andr02_resources.mainhas no failure boundary, so on any host wherenvidia-smiis 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()thenpre = [sample.to_record() for sample in sampler.sample_once()]at:243, withtry:only beginning at:245— so a probe failure there escapesrun()entirely and reachesmain, which writes nomanifest.jsonand does not even create the run directory: measuredmain([...])→FileNotFoundErrorpropagated,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 hasnvidia-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(stageresource-sample, partial samples retained), and make a probe read failure recordable (a sample withUNSETvalue and anerrorfield, or a per-probestatus) rather than raising throughmain, which should write a failure record instead of a traceback.scripts/r02_resources.py:54-57and:265-267vsscripts/r02_smoke.py:186— the peak claim names a window that did not happen.TORCH_PEAK_COMMANDstates "reset_peak_memory_stats() at run start, then max_memory_allocated() / max_memory_reserved() at run end", and every recordedpeak_claims[...]["basis"]repeats "device peak counter (...), reset at run start". The only reset is insidedeferred_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-shardfrom_pretrained, temporary buffers) is excluded while the manifest still labels the value a run peak with a run-start basis.docs/compute.mdrequires 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 ofReadinessSmoke.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-361withscripts/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: withwall_clock_cap_seconds=1and a stage that blocks 2.0 s,run()returned only after 2.00 s and then recordedwall-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 ofrun(): aKeyboardInterruptraised ingenerateleft no record, andmainwrites 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/SIGALRMguard around the run inmain, 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-idnor--run-diris constrained (:515-520): a run under a different owner, or into an off-layout directory, is still recorded asowner: eidowherever it was pointed. The recipe assigns ownership and ther02-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_hashtreats a caller-supplied manifest'srepo_idas authoritative:validate_manifestchecks the revision format (r02_artifacts.py:237-239) but not thatrepo_id/source/precisionmatch the module pins, and the transport then resolves and downloads from whatever repository the manifest names (:402,:433). The CLI always passesdeclared_manifest(), so the recipe's pin holds in the documented path; the exported helper does not enforce it.round_triprecordsreadback_uriverbatim (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 throughredact_secretsbefore recording is cheap insurance and costs nothing today.: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, andr02_release.py self-testare 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 "RaisesGateClosedwhen the flag is absent"; argparse exits 2 instead. Still fail-closed, but the docstring describes a different mechanism.r02_artifacts.py:19-22says "the revision pin is checked before any byte is requested" whileresolve_revisionperforms 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.Verified at this head
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. Releaseround-tripwith a missing record → exit 2. In-processmain([...])returns 2 for the smoke.grantedmust be a real boolean, the scope must be one of the three known scopes, andreference/approved_by/approved_at_utcmust be non-empty and notUNSET; unknown extra keys are carried into the record but cannot overwrite a validated field.torchnortransformersin a venv where neither is installed, so the fail-closed path is provably the import-free path.deferred_access()is only constructed insiderun(), and its imports sit inside the callables.test_module_import_is_model_library_freecovers this in CI-shaped form.is_unsetuses value equality so a JSON round trip cannot turn theUNSETmarker into a measurement; observed-versus-expected classification yieldsfirst-observation/match/sha256-mismatch/size-mismatch;planwrites a valid declared manifest and transfers nothing (7 declared files, revision8faed761d45a263340a0528343f099c05c9a4323,resolved_revision: UNSET).git credential fillwith host and username on stdin andGIT_TERMINAL_PROMPT=0; it reachescurlonly inside a mode-0600 temporary config that is unlinked in afinallyblock; the argv carries--configand never the credential or anAuthorizationheader; transport errors and log lines are redacted (round_triprecords 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.self-test(exit 0, in-memory transport): upload → separate read-backGET→ 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 infinallyand also fires on a corrupted read-back and on an upload failure;--retainrecordsretained: truewithrelease_deleted: falseinstead of reporting a clean cleanup. The recorded round trip is labelledin-memory (self-test: no credential, no network)and itsreadback_uriis synthetic.peak_claims[...]["value"] = UNSETwith a "not measured" basis,sampled_maxis labelled "maximum over the sampled instants only; not a peak measurement", peak values are assigned only from probes whosekindispeak-counter, and one sampling instant shares one clock read. Confirmed on liveprobeoutput (two instants, one timestamp each). The window mismatch is the second blocker.maincreates--run-dironly after an authorizedrun()returns, and the gate-closed paths create nothing. The PR adds no CI,.gitignore,results/R02, orauthorization.jsonchange.Not verifiable here
ForgejoTransportagainst the real forge, andgit_credential_fillagainst the Windows Git Credential Manager store: exercised only through recording fakes andself-test. I ran no live round trip.deferred_access()remains unexercised, and the recorded peak label can only be evaluated on hardware.Corrections pushed: head
43ae50b1590985ef1d4e9fb041a750cf166060b1(on top of438df18). 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_oncenever raises: an unreadable probe yields one sample per declared metric withvalue: UNSET,status: "error"and a redactederror; the summary is markeddegradedand carriesprobe_errors; andr02_resources.mainhas a failure boundary that writes that record and exits 1. InReadinessSmoke.runthe 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 kindresource-probe-unavailablebefore anything is loaded, andmainwritesmanifest.jsonandresource-samples.jsonfor 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: 2naming[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/meminforeadings still present. On this host, wherenvidia-smiexists, the same command exits 0 withstatus: 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 intest_r02_resources.py.Blocker 2 — the peak claim names the window that happened.
ModelAccessgainedreset_peak_counters, called as the first model-library call at the top ofrun()before any tokenizer or model load, and the reset was removed fromgenerate(). The probe carries its basis text intopeak_claims[...]["basis"]andmethod.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 asampled_maxwith the peak fieldUNSET.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_guardarmsSIGALRMbefore the run (POSIX, main thread): a stage that returns control to the interpreter is aborted at the cap and still produces a failure record, whosefailure.enforcementreadsaborted by the armed guard at the cap. A breach only detectable after a stage returned is labelleddetected 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'stimeout) writes nothing and is recorded by whoever killed it.--no-wall-clock-guardselects detection-only, and--dry-runprints 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 withstage: generateand enforcementaborted),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.
OWNERis deleted: the owner comes from--ownerintoSmokePlanand is asserted in the manifest (test_no_owner_constant_is_hardcoded; the CLI test records ownervesper). The run target is validated before any setup — the run-id must matchr02-smoke-<nnn>and the run directory must end withresults/R02/<run-id>, otherwise exit 2 with nothing created.fetch_and_hashpin-checksrepo_id,source,precisionandrequested_revisionagainst the module pins before the transport is called (asserted: no resolution request, no download).round_triprecords the readback URI throughredact_secrets, and the redactor now also coverssignature/credentialquery 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 aGateClosedit never raised.first_output_token_idis gone from the record, and the validator rejectsoutput_textandoutput_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
torchnortransformersimportable);check_docs.pyandbuild_results_ledger.py --checkpass;git diff --checkis clean.scripts/r02_preflight.pyis 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.R02.1 executable procedure gate — APPROVED (procedure only; no run authorized)
Reviewed head
43ae50b1590985ef1d4e9fb041a750cf166060b1(r02_smokefailure boundary, peak window, and wall-clock guard corrections) against base6fe5bc7, 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:
r02_resources.py:215,315-333/r02_smoke.py:243).python3 scripts/r02_resources.py probe --instants 2on thisnvidia-smi-less host exits 1 with zero stderr bytes and a full record:status: "degraded",probe_errors: ['nvidia-smi','nvidia-smi'], affected metrics carryingvalue: UNSET/status: "error"with a redacted error, and theproc-meminforeadings still present (2 of 5 samples ok). The smoke's pre-load instant is now inside the failure boundary (line 390, inside thetryat 380) and produces aresource-probe-unavailablerecord whosevalidate_smoke_recordreturns[], withsamples_preretained and the failing probe named. Throughmain, that run writesmanifest.jsonandresource-samples.json, recordsowner: "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.reset_peak_counters → load_tokenizer → load_model → describe_device → generate, so the counter is reset before any load; the only reset left is the explicitaccess.reset_peak_countersstage, andgenerateno longer resets. Both recorded strings —TORCH_PEAK_COMMANDand thebasison the peak claim — now read "reset at run start before any load ... read after the generation step", and both appear in the resource record.cap=1sand a stage sleeping 4s, the run returned in 1.00s withfailure.kind: "wall-clock-cap-exceeded"andfailure.enforcement: "aborted by the armed guard at the cap", and the record validates. With--no-wall-clock-guardthe same run returns after the stage (2.00s against a 1s cap) labelleddetected 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.py29 files OK; ledger current with 0 runs;git diff --checkclean; all six modules import with neithertorchnortransformersinsys.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 (artifactsfetch: no destination, no manifest; releaseround-trip), and a manifest naming anotherrepo_id(PINNED_FIELDSrefuses it with zero downloads and zero revision lookups — the pin check now precedes the metadata GET too); releaseself-testexit 0 withrelease_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-resortexcept Exception(lines 765-768) starts after theReadinessSmoke(...)call at 758-764, so a raisingsampler_factory/access_factoryescapes with a traceback and no manifest. Unreachable on production paths (both default toNoneand the real objects are built inside the coveredrun()), so the inline claim holds where it matters; construct inside thetryif you want it to hold for the seams too._generation_identityrecordssha256("")(e3b0c44…) rather thanUNSETwhen handed a non-empty generation mapping with nooutput_text— a digest claim about no output. The production access layer always setsoutput_text.--run-diris 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
ForgejoTransportand 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_smokefailure boundary, peak window, and wall-clock guard corrections) against base6fe5bc7, 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:
r02_resources.py:215,315-333/r02_smoke.py:243).python3 scripts/r02_resources.py probe --instants 2on thisnvidia-smi-less host exits 1 with zero stderr bytes and a full record:status: "degraded",probe_errors: ['nvidia-smi','nvidia-smi'], affected metrics carryingvalue: UNSET/status: "error"with a redacted error, and theproc-meminforeadings still present (2 of 5 samples ok). The smoke's pre-load instant is now inside the failure boundary (line 390, inside thetryat 380) and produces aresource-probe-unavailablerecord whosevalidate_smoke_recordreturns[], withsamples_preretained and the failing probe named. Throughmain, that run writesmanifest.jsonandresource-samples.json, recordsowner: "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.reset_peak_counters → load_tokenizer → load_model → describe_device → generate, so the counter is reset before any load; the only reset left is the explicitaccess.reset_peak_countersstage, andgenerateno longer resets. Both recorded strings —TORCH_PEAK_COMMANDand thebasison the peak claim — now read "reset at run start before any load ... read after the generation step", and both appear in the resource record.cap=1sand a stage sleeping 4s, the run returned in 1.00s withfailure.kind: "wall-clock-cap-exceeded"andfailure.enforcement: "aborted by the armed guard at the cap", and the record validates. With--no-wall-clock-guardthe same run returns after the stage (2.00s against a 1s cap) labelleddetected 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.py29 files OK; ledger current with 0 runs;git diff --checkclean; all six modules import with neithertorchnortransformersinsys.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 (artifactsfetch: no destination, no manifest; releaseround-trip), and a manifest naming anotherrepo_id(PINNED_FIELDSrefuses it with zero downloads and zero revision lookups — the pin check now precedes the metadata GET too); releaseself-testexit 0 withrelease_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-resortexcept Exception(lines 765-768) starts after theReadinessSmoke(...)call at 758-764, so a raisingsampler_factory/access_factoryescapes with a traceback and no manifest. Unreachable on production paths (both default toNoneand the real objects are built inside the coveredrun()), so the inline claim holds where it matters; construct inside thetryif you want it to hold for the seams too._generation_identityrecordssha256("")(e3b0c44…) rather thanUNSETwhen handed a non-empty generation mapping with nooutput_text— a digest claim about no output. The production access layer always setsoutput_text.--run-diris 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
ForgejoTransportand 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.