The validation metric improves, so the training loop records the step and assigns best_state = model.state_dict(). Training continues. Later, the engineer saves that mapping, loads it into a fresh model, and sees a successful result from load_state_dict(strict=True). Everything appears orderly until the restored tensors are inspected: they contain values from after the selected step.
The selection decision survived. The selected contents did not survive in that retained mapping. A meaningful metric label and a loadable state dictionary can therefore describe different moments in the same process. No concurrent training thread, complicated optimizer, or distributed checkpoint system is needed to reproduce the mismatch.
This playbook tests that interval with a runnable harness using one CPU parameter and one persistent buffer. At selection, w=[2.0] and offset=[1.0] produce 7 for x=[3.0]. After controlled updates, the live values become 7 and 11, producing 32. Seven capture methods face the same update, followed by independent content checks and fresh-module reloads.
The release question is precise: does the candidate still contain both selected tensors, and can the receiving module restore them? Storage diagnostics explain failures; selection metadata identifies the intended state; reload evidence qualifies the handoff. The experiment evaluates preservation of a supplied selection, without establishing whether the metric justified choosing that model. Its synthetic values make each failure inspectable, and its explicit decisions distinguish an incomplete test from selected contents that are already lost. The local publication controls also check that rejected attempts leave the previously accepted file’s bytes unchanged.
Separate the selection decision from the state artifact
Define the selection record before discussing checkpoint filenames. Here it identifies synthetic-best-step-0, source module tiny-single-writer-0, selected step 0, metric label synthetic_validation_loss, and metric value 0.125. Those identifiers and that metric are synthetic fixture metadata. The script does not calculate a validation loss or compare competing models.
The state associated with that record is concrete: parameter w contains 2.0, persistent buffer offset contains 1.0, and the float32 input 3.0 produces 7.0. Both tensors have shape [1] and reside on the CPU. These facts define what must survive. A release owner can inspect them without interpreting the word “best.”
During handoff, reconcile the source identifier and selection event explicitly instead of treating a plausible label as evidence of identity.
For the reasoning behind choosing a metric, see statistical evaluation and model selection. This fixture accepts the selection record as an input. Even a perfectly preserved checkpoint would leave questions about leakage, sampling, and the selection criterion unresolved.
The official PyTorch saving and loading tutorial warns that retaining the returned best-model state can follow subsequent updates, and recommends serialization or an independent deep copy. That guidance concerns when the contents are captured. Assigning the metric and assigning a mapping are separate operations; their proximity in the source file does not make an immutable transaction.
Use two acceptance statements. First, after the deliberate later mutation, the candidate must still contain w=2 and offset=1. Second, loading it into a fresh identical module must restore both values and produce 7 at 3. The output is a useful witness alongside the complete two-tensor comparison.
The selection owner should name the boundary explicitly: capture finishes before another permitted write. If the application records “best” first and postpones capture until a convenient save interval, that interval needs independent state ownership. Otherwise, the eventual file can accurately preserve the wrong moment.
Create a tiny model with a parameter and a buffer
Save the following complete file as snapshot_fixture.py. It uses no random initialization, dataset, model download, or optimizer. All model tensors and test inputs explicitly use CPU float32. The owned process supplies every write, and the controlled operations use a PyTorch CPU intra-operation thread count of one.
The buffer makes the test harder to pass accidentally. The PyTorch 2.10 Module reference describes a shallow state mapping containing parameter and persistent-buffer references. In this model, offset changes inference just as directly as w. A check over only model.parameters() would miss half the declared state.
Readers seeking an overview of machine-learning model families can explore that separately. Here, the tiny arithmetic module keeps every retained value visible.
Prerequisites are an owned working directory, CPython 3.13.5, and the CPU build of PyTorch 2.10.0. With that Python interpreter available, prepare an isolated environment and run the file:
python3.13 -m venv .venv
.venv/bin/python -m pip install \
--index-url https://download.pytorch.org/whl/cpu \
'torch==2.10.0+cpu'
.venv/bin/python snapshot_fixture.py > snapshot_fixture_evidence.jsonCheck the emitted runtime rather than assuming python3.13 selects patch 3.13.5. A different label is a separately recorded run.
import copy
import io
import json
import platform
import torch
from torch import nn
torch.set_num_threads(1)
torch.use_deterministic_algorithms(True)
KEYS = ("w", "offset")
INDEPENDENT = {"deepcopy", "clone", "saved_then_loaded"}
def check(condition, message):
if not condition:
raise RuntimeError(message)
class Tiny(nn.Module):
def __init__(self):
super().__init__()
self.w = nn.Parameter(torch.tensor([2.0], dtype=torch.float32, device="cpu"))
self.register_buffer("offset", torch.tensor([1.0], dtype=torch.float32, device="cpu"))
def forward(self, x):
return self.w * x + self.offset
def predict(model):
with torch.no_grad():
return model(torch.tensor([3.0], dtype=torch.float32, device="cpu")).item()
def describe(state):
check(set(state) == set(KEYS), "Unexpected state keys")
result = {}
for key in KEYS:
tensor = state[key]
check(tensor.device.type == "cpu", f"{key}: non-CPU state")
check(tensor.dtype == torch.float32, f"{key}: wrong dtype")
check(list(tensor.shape) == [1], f"{key}: wrong shape")
result[key] = {"dtype": str(tensor.dtype), "shape": [1], "value": tensor.item()}
return result
def selection_record(model):
tensors = describe(model.state_dict())
check([tensors[k]["value"] for k in KEYS] == [2.0, 1.0], "Selection must precede mutation")
check(predict(model) == 7.0, "Wrong selection output")
return {
"selection_label": "synthetic-best-step-0", "step": 0,
"metric_label": "synthetic_validation_loss", "metric_value": 0.125,
"source_module": "tiny-single-writer-0",
"runtime": {"python": platform.python_version(), "python_implementation": platform.python_implementation(), "torch": str(torch.__version__), "device": "cpu", "torch_threads": torch.get_num_threads()},
"tensors": tensors, "buffer_keys": ["offset"],
"input_x": 3.0, "expected_output": 7.0,
}
def mutate(model):
with torch.no_grad():
model.w.add_(5.0)
model.offset.add_(10.0)
def inspect_reload(candidate):
candidate_content = describe(candidate)
fresh = Tiny()
with torch.no_grad():
fresh.w.fill_(-101.0)
fresh.offset.fill_(-202.0)
fresh.load_state_dict(candidate, strict=True)
fresh.eval()
return {"candidate": candidate_content, "reloaded": describe(fresh.state_dict()), "output_x3": predict(fresh)}
def require_selected(observation, selected, label):
actual = [observation["candidate"][k]["value"] for k in KEYS]
expected = [selected["tensors"][k]["value"] for k in KEYS]
check(observation["candidate"] == selected["tensors"], f"{label}: selected tensor mismatch: {actual} != {expected}")
check(observation["reloaded"] == selected["tensors"], f"{label}: reloaded parameter/buffer mismatch")
check(observation["output_x3"] == selected["expected_output"], f"{label}: output mismatch")
def validate_envelope(envelope, selected, route):
check(envelope["manifest"] == selected | {"artifact_route": route}, "Manifest disagrees with the trusted selection record")
observation = inspect_reload(envelope["state"])
require_selected(observation, selected, route)
return observation
def main():
model = Tiny()
selected = selection_record(model) # Plain scalars, not tensor aliases.
state = model.state_dict()
serialized = io.BytesIO()
torch.save(state, serialized) # Finish saving before the next update.
candidates = {
"alias": state, "shallow": copy.copy(state), "dict": dict(state),
"detach": {k: v.detach() for k, v in state.items()},
"deepcopy": copy.deepcopy(state),
"clone": {k: v.detach().clone() for k, v in state.items()},
}
before_eval = {k: getattr(model, k).data_ptr() for k in KEYS}
model.eval()
check(not model.training, "eval did not change the mode flag")
check(all(getattr(model, k).data_ptr() == before_eval[k] for k in KEYS), "Unexpected storage change at eval")
mutate(model)
check(predict(model) == 32.0, "Wrong live output after mutation")
check([describe(model.state_dict())[k]["value"] for k in KEYS] == [7.0, 11.0], "Wrong live parameter/buffer values")
serialized.seek(0)
candidates["saved_then_loaded"] = torch.load(serialized, weights_only=True, map_location="cpu")
rows = []
for name, candidate in candidates.items():
independent = name in INDEPENDENT
shared = {k: candidate[k].data_ptr() == getattr(model, k).data_ptr() for k in KEYS}
check((candidate is state) == (name == "alias"), f"{name}: unexpected mapping identity")
check(all(value == (not independent) for value in shared.values()), f"{name}: unexpected parameter/buffer storage identity")
observation = inspect_reload(candidate)
expected_values = [2.0, 1.0] if independent else [7.0, 11.0]
for location in ("candidate", "reloaded"):
check([observation[location][k]["value"] for k in KEYS] == expected_values, f"{name}: wrong {location} content")
check(observation["output_x3"] == (7.0 if independent else 32.0), f"{name}: wrong fresh output")
rejection = None
try:
require_selected(observation, selected, name)
except RuntimeError as error:
rejection = str(error)
check((rejection is None) == independent, f"{name}: selection gate gave the wrong decision")
rows.append({"case": name, "same_mapping": candidate is state, "shares_storage": shared, **observation, "decision": "ACCEPT" if independent else "REPAIR", "expected_rejection": rejection})
print(json.dumps({"selection": selected, "live_output_x3": 32.0, "eval_training_flag": model.training, "cases": rows}, indent=2))
if __name__ == "__main__":
main()The completed fixture was run for this article on CPython 3.13.5 and PyTorch 2.10.0+cpu, with CPU tensors and a reported CPU intra-operation thread count of one. These match the preparation probe’s labels and describe this execution, without a newest-release claim. Documentation was rechecked October 17, 2026: API and serialization pages are pinned to 2.10; the rolling tutorial carries a 2.14.0+cu130 build label, and the Python 3.13 copy page carries 3.13.16. Those documentation labels are not measurements of the tested runtime.
Define immutable witnesses before any later update
The witness contains ordinary numbers, strings, and shape lists, independently recorded before mutation. The program treats this metadata as write-once; a Python dictionary itself is still mutable. Calling item() captures each scalar value. Merely assigning the same tensor to an “expected” variable would leave the oracle exposed to the same writes.
For larger tensors, first copy the selected contents into owned storage, then derive any content digest from that copy. Record each key, dtype, and shape beside the digest. Hashing a reference at the end of training would describe its later contents, however convincing the filename looked.
The script also fills both tensors in every fresh receiving module with deliberately wrong values before loading. Because Tiny() initially contains the selected values, this extra step prevents a missing or ineffective load from appearing successful simply through matching initialization.
Retain state_dict and then change the live model
Read the fixture as a timeline. At the first point, the source has w=2 and offset=1. The script records independent scalar witnesses and obtains state = model.state_dict(). Its alias candidate is the retained mapping corresponding to best_alias = model.state_dict() in an application.
At the second point, every candidate has been captured. At the third, the program enters torch.no_grad(), adds 5 to w, and adds 10 to offset. The live computation is now 7 × 3 + 11 = 32. At the fourth, it inspects the retained mappings and loads their contents into fresh modules. Nothing else is allowed to update the source between those points.
The direct candidate now contains w=7 and offset=11. Its name has not changed; its selection label can still say step 0. The values reachable through that name have changed. Saving that mapping at this point would serialize the later values.
This is specifically an in-place mutation test. Replacing a module attribute with a new tensor is a different operation and may leave an earlier mapping referring to older storage. Do not generalize this schedule into a claim that every future modification automatically appears in every retained dictionary.
The versioned no_grad reference describes disabling gradient calculation. In this experiment, the explicit in-place additions remain effective inside the context. Gradient recording and permission to mutate data are separate concerns. The update deliberately avoids an optimizer so that an optimizer configuration cannot explain the observed change.
This schedule is also a useful debugging discipline. An engineer investigating a suspected alias should identify the last verified selected contents, the capture operation, the first subsequent write, and the eventual serialization. If those events are compressed into “saved the best model,” the missing ownership boundary stays hidden.
Do not interpret the larger output as deterioration in model quality. There is no measured validation result after the mutation and no target distribution in this fixture. The number 32 is evidence that the retained state changed, with both tensor changes accounting for it exactly.
The immediate repair question is therefore historical: does an independent copy of the original state still exist? The current live module cannot answer that merely because it remains compatible with the same class. Once its values have moved, taking a new snapshot faithfully captures the present.
Compare new mappings with independent tensor snapshots
The seven candidates must originate at the same selection point. The fixture retains the original mapping, shallow-copies it, converts it with dict, detaches each tensor, deep-copies the mapping, clones each detached tensor, and immediately serializes the selected state to BytesIO. Only after those operations finish does the live mutation occur.
The byte buffer is deserialized after mutation, which deliberately separates capture time from load time. Its successful result depends on serialization having already consumed the selected values. Moving that torch.save call below the update changes the experiment and the identity of the saved artifact.
Python’s copy documentation for the 3.13 family distinguishes a binding, a shallow container copy, and a recursive deep copy. Applied here, copying a container does not establish independent ownership of its tensor contents. The experiment determines what each resulting candidate actually retains.
Treat the comparison as a capture-method test with a fixed source. Creating a fresh model separately for each method can be valid, but it introduces another setup obligation. Capturing some methods before mutation and others afterward would confound copying behavior with different selected values. This script instead puts the methods under one observable schedule.
Checking values only at capture would let all seven methods appear correct. The later write is the challenge that distinguishes retention of the selected contents from temporary agreement with them. Keep that challenge in the test whenever the capture implementation changes.
Keep mapping identity and storage identity in separate columns
The evidence distinguishes whether a candidate is the original mapping from whether its w and offset point to the source tensors’ storage. Only alias is the original mapping. Nevertheless, shallow, dict, and detach also share both source storage locations in this fixture.
A distinct mapping identity therefore answers a narrower question than an engineer usually intends when saying “I made a copy.” It demonstrates a different container. The parameter and buffer columns determine whether later source writes remain visible through that container.
The Tensor.data_ptr API reports the first element’s address. Here, all compared tensors are live, contiguous, one-element CPU tensors, making equal pointers a useful same-process diagnostic. Addresses are neither persistent identifiers nor a general test for overlapping arbitrary views: different first-element addresses can still belong to overlapping storage.
The independent candidates have different source pointers for both keys and preserve both selected values after mutation. Those content observations matter more than an address alone. Retain the source and candidates during diagnosis so allocation lifetime does not confuse pointer interpretation.
Finally, independence permits separate ownership; it does not make a candidate read-only. A later write through the copied candidate can still corrupt its selected contents. The artifact owner must therefore preserve the copy after capture, and the release owner must inspect what is actually handed over.
Show why eval and detach do not freeze a candidate
The fixture calls model.eval() before the later mutation, verifies the training flag changes, and checks that both live tensor pointers remain unchanged. The subsequent additions still move the source to w=7 and offset=11. The direct and detached candidates follow those changes despite evaluation mode.
The Module API’s evaluation-mode description concerns the behavior of affected modules. Tiny contains no dropout or batch normalization and has no mode-dependent branch in forward. Calling eval here records an inference convention without creating another state owner.
Detachment is a separate operation. The PyTorch 2.10 detach reference explicitly states that the returned tensor shares storage with its source. In the prepared detach candidate, both storage comparisons are true, both contents change, and fresh reload produces 32.
Keep three questions visible during review. Does the computation record gradients? Which mode controls the module’s forward behavior? Who owns the data being retained? A sensible answer to either of the first two questions does not settle the third. The candidate must survive the later write to demonstrate the needed ownership.
Tensor object identity can be misleading too. A newly returned tensor object can still describe the same data storage. The detached candidate tests this distinction separately from dictionary identity. Inspecting the actual parameter and buffer values after the write prevents either kind of new object from being mistaken for independent contents.
Under the default state-dictionary behavior, returned tensors are already detached through keep_vars=False. Applying detach again adds no independent data allocation to this example. The comparison is useful precisely because the resulting code looks deliberate: the comprehension creates a new dictionary, and each entry is explicitly detached, yet the selected contents still move.
By contrast, the clone candidate copies data for both entries. The clone API describes producing a copy; the preceding detachment makes the intent explicit in this tensor-only comparison. Its acceptance still comes from the checks after mutation, including the buffer and the independent receiving module.
When repairing application code, keep evaluation mode wherever inference needs it. Replace the defective capture operation at its own boundary. Removing eval or adding more detachment would not address the evidence observed here. The useful review question is whether the retained parameter and buffer values remain the selected ones after the next allowed source write.
Capture a stable state at the selection boundary
Two operational routes satisfy this fixture. The first deep-copies the selected state synchronously and gives the artifact owner that independent copy. Serialization can then occur later, provided nobody modifies the copy. The second serializes the selected state immediately, finishing before the training owner allows another source update.
Both routes require control over the interval they read. In this single-writer process, ordinary sequential execution supplies that control. The source is at the selected state throughout capture. This experiment provides no qualification for concurrent writers or a background save that reads tensors while they are changing.
Save the following as capture_routes.py beside the first file. Each route carries a manifest together with the state, then faces the same source mutation and selection gate.
import copy
import io
import json
import torch
from snapshot_fixture import (Tiny, check, mutate, predict, selection_record,
validate_envelope)
def capture_deepcopy(model, selected):
return {"manifest": copy.deepcopy(selected) | {"artifact_route": "deepcopy"},
"state": copy.deepcopy(model.state_dict())}
def save_immediately(model, selected, destination, route="immediate_memory"):
envelope = {"manifest": selected | {"artifact_route": route},
"state": model.state_dict()}
torch.save(envelope, destination) # Synchronous, before any next update.
def main():
model = Tiny()
selected = selection_record(model)
copied = capture_deepcopy(model, selected)
stream = io.BytesIO()
save_immediately(model, selected, stream)
mutate(model)
check(predict(model) == 32.0, "Live model did not advance")
stream.seek(0)
loaded = torch.load(stream, weights_only=True, map_location="cpu")
rows = {"deepcopy": validate_envelope(copied, selected, "deepcopy"),
"immediate_memory": validate_envelope(
loaded, selected, "immediate_memory")}
print(json.dumps({"selection": selected, "routes": rows}, indent=2))
if __name__ == "__main__":
main()Run it independently:
.venv/bin/python capture_routes.py > capture_routes_evidence.jsonExpected assertions are that the live model advances to output 32 and both retained routes reload w=2, offset=1, and output 7. The reproduced run satisfied those assertions. A failed copy, save, load, manifest comparison, or tensor check prevents a successful command result; no exception is converted into an accepted candidate.
The ownership handoff differs between routes. With deep copy, the artifact owner receives mutable tensors whose storage is separate from the live module. With immediate serialization, the owner receives completed serialized bytes and must protect that byte artifact from later overwrite. Neither route permits casually reusing the retained candidate as another workspace for model updates.
Handle capture failure as failure of that candidate. A selection record may already exist when a copy or save raises an exception; that record does not establish a completed artifact. Keep the previous accepted candidate available, retain the error, and resolve the failed capture before presenting the new selection as ready for release review.
Respect metadata, buffers, and serialization boundaries
The tensor comprehension is intentionally narrower than the supported module-state interface. The Module API supports extra state and state hooks, while a state mapping can carry metadata used during loading. Rebuilding only key-value entries can discard mapping metadata, and calling tensor methods on every entry assumes every value is a tensor.
For Tiny, that assumption is inspectable: there are exactly two ordinary tensors. Its cloning result establishes a useful comparison without supplying a universal replacement for copying or serializing an arbitrary module’s state.
The PyTorch 2.10 serialization notes describe storage and view relationships in serialized tensors. Per-entry cloning can alter relationships that exist between source tensors. Equality of this fixture’s two independent scalar values does not validate every possible shared-storage layout.
Before adapting either route, inventory the actual module state: registered persistent buffers, extra state, custom hooks, and any representation requirements the consumer depends on. Deep-copy support is something to execute and verify for that state contract. A successful Tiny run does not establish that every custom object copies correctly or every chosen format preserves identical relationships.
Reload every candidate into a fresh identical module
A retained mapping is useful to the release owner only if the receiving module can recover the intended state. The harness therefore creates a new Tiny for every candidate. It deliberately poisons w and offset, loads the candidate with strict=True, sets evaluation mode, inspects the restored tensors, and evaluates the fixed input.
This order rules out an easy false positive. Fresh initialization already contains 2 and 1. Without poisoning it, a test that accidentally skipped the load could still report the expected output 7. Filling the receiving tensors with unrelated values forces the load to do observable work.
All seven candidates are compatible with the same module definition. In the reproduced run, the four shared-storage methods restore 7 and 11, yielding 32. Deep copy, tensor cloning, and the state serialized before mutation restore 2 and 1, yielding 7. The numerical split matches the inspected candidate contents.
The load_state_dict contract requires matching keys with strict=True. It does not receive the selected step or metric. Default assign=False also preserves receiving tensor properties, so explicit candidate dtype checks remain necessary.
The harness inspects both sides of the boundary. Before loading, describe requires exactly w and offset, CPU placement, float32, and shape [1]. After loading, it repeats those checks on the fresh module and compares both contents with the independent manifest. An output comparison completes the bounded behavioral witness.
Exact equality is deliberate here: the small integer-valued float32 tensors and arithmetic are exactly representable. There is no approximation budget to negotiate. For a different state contract, document any allowed transformations explicitly; accepting a numerical tolerance without understanding it could conceal the very content change the gate should reject.
A successful load therefore establishes less than the complete acceptance result. It tells us that this receiving module accepted the provided state. The separate content gate determines whether that accepted state corresponds to the supplied selection. Keeping those outcomes separate makes a failure actionable.
If a receiving definition changes, rerun compatibility and content checks together. Do not respond to a missing buffer by relaxing strictness and accepting the default offset. That would change the tested state contract. Where the candidate remains intact, the correct next step is to establish what definition and complete state the release actually require.
Reconcile the artifact with the metric-selection record
The manifest binds a human-readable selection record to inspectable contents. It records the selection label, step, metric label and value, source module identifier, runtime, two tensor descriptions, buffer keys, input, expected output, and artifact route. The route is added when the state is packaged for transfer.
Keep the trusted selection record outside the candidate being validated. In these scripts, selected contains independently captured plain data; validate_envelope checks the candidate’s manifest against that record, then tests the candidate state. Merely reading a manifest from an artifact and agreeing with its own claims would leave a mislabeled artifact unchallenged.
For this fixture, scalar values are a complete content description. The release owner can reconcile w=2 and offset=1 directly, without a digest, a database, or an artifact registry. The source identifier is synthetic bookkeeping; it is useful within the controlled run and is not an authentication mechanism.
Two selection events could happen to contain identical tensors. Content equality would establish identical tested values, while the event labels still answer which decision authorized the handoff. Keep both kinds of evidence instead of expecting one identifier to serve every purpose.
A larger implementation needs a defined content representation before adopting hashing. Clone the selected tensors while the writer is stopped at the boundary. Then establish ordered keys, dtype, shape, element order, and an explicit byte-order convention for supported tensor types. Specify how unusual values and representations are handled instead of silently converting everything through a convenient output format.
That is a proposed extension of the scalar content gate, not an executed universal hashing function. Its reproducibility depends on the defined representation. A digest of a checkpoint ZIP file identifies particular serialized bytes; it should not be described as a canonical digest of the model’s semantic tensor contents. The byte comparison in the publication fixture has a narrower purpose: checking that the previously accepted file’s bytes remain unchanged.
Test a correct-shaped artifact from the wrong selection
The disk fixture below executes this negative control. It mutates a new Tiny to w=7 and offset=11, then serializes it while attaching the unchanged selected-step manifest. Both keys, shapes, and dtypes remain correct. The resulting state loads into the receiving module.
The selection-content gate rejects it because the two candidate values disagree with the independently retained record. This demonstrates why an accurate-looking filename and a plausible metric field cannot substitute for reconciliation.
Immediate saving is therefore only useful when its timing is correct. Serialization after mutation successfully captures the later state. It cannot recover the earlier selection through the copied label. The negative control tests that distinction without needing an invalid file or a malformed state dictionary.
Keep inference-state recovery separate from training resume
The deliverable tested here is selected model state that an identical module can reload: one parameter, one persistent buffer, and the expected inference result. The process does not claim to reproduce a training trajectory. There is no optimizer or training data pipeline in the fixture.
A receiving engineer should be able to state that scope from the manifest and reproducer alone. If the handoff merely says “checkpoint,” different owners may assume different contents. One may expect selected inference weights; another may expect to continue training from an exact historical position. Successful recovery of w and offset resolves only the first requirement.
The saving and loading tutorial’s general-checkpoint discussion distinguishes model state from additional information needed when resuming training, including optimizer state. That broader checkpoint is outside this experiment. The two selected tensors do not establish restoration of scheduler state, RNG state, sampler position, data-loader progress, or the exact sequence of previous updates.
Likewise, pretrained models and fine-tuning choices involve decisions about what model to start from and how to adapt it. Those decisions cannot establish that an in-memory candidate captured the selected moment. The snapshot test applies to the concrete state handed to its consumer.
When someone adds a resume requirement, write a separate contract describing the additional state and the behavior that must be reproduced. Do not extend an ACCEPT from this fixture by changing its label. The evidence has not become broader because the same file could hold more fields.
This distinction also makes failures easier to assign. A selected-state mismatch is an artifact identity problem. Missing evidence about training continuation is a scope gap requiring a different test. Neither should be hidden behind an output that looks reasonable after one forward call.
For the present release, keep the claim narrow and complete: the declared module definition receives the declared parameter and persistent-buffer values from the selected artifact. The script demonstrates that claim under recorded conditions and leaves a clean boundary for any future resume qualification.
Publish only a validated local artifact
The memory-buffer route isolates capture timing. Writing a real file adds staging, readback, replacement, and cleanup outcomes. The admission question is concrete: does the selected state pass the independent gate, and do both rejected attempts preserve the previously accepted bytes? Save this third file as publish_local.py beside the others. It creates an owned temporary directory and publishes only within that directory.
import json
import os
from pathlib import Path
import tempfile
import torch
from capture_routes import save_immediately
from snapshot_fixture import (Tiny, check, mutate, selection_record,
validate_envelope)
def publish(model, selected, destination, after_capture=None, fail=False):
descriptor, name = tempfile.mkstemp(dir=destination.parent,
prefix=".candidate-", suffix=".pt")
staged = Path(name)
try:
with os.fdopen(descriptor, "wb") as output:
save_immediately(model, selected, output, "immediate_disk")
output.flush()
os.fsync(output.fileno())
if after_capture is not None:
after_capture()
envelope = torch.load(staged, weights_only=True, map_location="cpu")
observation = validate_envelope(envelope, selected, "immediate_disk")
if fail:
raise OSError("Synthetic failure before publication")
os.replace(staged, destination)
return observation
finally:
staged.unlink(missing_ok=True)
def main():
model = Tiny()
selected = selection_record(model)
with tempfile.TemporaryDirectory(prefix="pytorch-snapshot-") as directory:
accepted = Path(directory) / "accepted.pt"
positive = publish(model, selected, accepted,
after_capture=lambda: mutate(model))
validate_envelope(torch.load(accepted, weights_only=True,
map_location="cpu"), selected, "immediate_disk")
prior_bytes = accepted.read_bytes() # Preservation, not semantic hashing.
controls = {}
expected_errors = {
"wrong_selection": (RuntimeError, "immediate_disk: selected tensor "
"mismatch: [7.0, 11.0] != [2.0, 1.0]"),
"failure_before_publish": (OSError,
"Synthetic failure before publication"),
}
for name in ("wrong_selection", "failure_before_publish"):
candidate = Tiny()
if name == "wrong_selection":
mutate(candidate) # Same claimed manifest, later tensor values.
try:
publish(candidate, selected, accepted,
fail=(name == "failure_before_publish"))
except (RuntimeError, OSError) as error:
error_type, message = expected_errors[name]
check(type(error) is error_type and str(error) == message,
f"{name}: unexpected failure: {error!r}")
controls[name] = str(error)
else:
raise RuntimeError(f"{name}: negative control unexpectedly passed")
check(accepted.read_bytes() == prior_bytes,
f"{name}: replaced the previous accepted artifact")
check(list(Path(directory).iterdir()) == [accepted],
f"{name}: staged file was not removed")
print(json.dumps({"selection": selected, "published": positive,
"controls": controls,
"previous_artifact_preserved": True,
"staging_cleanup_passed": True}, indent=2))
if __name__ == "__main__":
main()Run the local publication fixture:
.venv/bin/python publish_local.py > publish_local_evidence.jsonThe positive path serializes the state and manifest as one envelope, finishes writing, deliberately advances the source, reloads the staged envelope, and checks its contents against the trusted selection record. Only then does os.replace publish the staged file as accepted.pt. The script reloads that published artifact too.
The two negative controls were executed. A later-state candidate with the selected manifest was rejected. An injected exception after validation but before replacement also prevented publication. Both controls required the previous accepted file’s exact bytes to remain unchanged and their staging files to disappear.
State and manifest travel in one envelope so that the publication unit is explicit. Writing them as two unrelated files would introduce a second agreement problem during replacement. This example avoids that split; a different layout would need its own tested publication rule before inheriting the same acceptance claim.
Stage and destination share a directory. The Python 3.13 os.replace documentation describes replacement semantics and the same-filesystem constraint relevant to this pattern. The demonstrated result is bounded to the exercised filesystem; the file flush and fsync do not establish power-loss durability of directory changes. No crash or power-loss test was run.
For the wider notebook-to-production workflow, this is one admission gate. Its publisher owns the destination exclusively. The fixture’s temporary checkpoints are removed automatically when the context exits, and the JSON evidence remains in the lab directory.
Do not turn a checkpoint test into an unsafe loader
Every load targets bytes produced by these local scripts and explicitly supplies map_location="cpu" and weights_only=True. The serialization notes on restricted loading explain that this narrows permitted unpickling behavior while leaving security limitations. Provenance remains a separate acceptance requirement.
Do not extend this exercise by substituting an arbitrary downloaded checkpoint. If loading fails, retain the failure and investigate the owned artifact and runtime. Disabling restrictions to make the test turn green would change the tested loading contract.
Read the full evidence table rather than one prediction
The preparation probe supplied the seven-way result on CPython 3.13.5 and PyTorch 2.10.0+cpu. The completed scripts reproduced that result on the same runtime labels. These are deterministic local observations of capture behavior, without performance measurements or production outcomes.
In the table, “same map” compares each candidate with the original state mapping. Storage comparisons use the live module. Parameter and buffer values are inspected after mutation, and both are checked again after fresh loading.
Candidate | Mapping construction; same map? | Shares live storage: w / offset | Parameter w | Buffer offset | Fresh-load output at 3 | Selection decision |
alias | Retain state; yes | Yes / Yes | 7 | 11 | 32 | REPAIR |
shallow | copy.copy(state); no | Yes / Yes | 7 | 11 | 32 | REPAIR |
dict | dict(state); no | Yes / Yes | 7 | 11 | 32 | REPAIR |
detach | New mapping, detached entries; no | Yes / Yes | 7 | 11 | 32 | REPAIR |
deepcopy | copy.deepcopy(state); no | No / No | 2 | 1 | 7 | ACCEPT |
clone | New mapping, detached cloned entries; no | No / No | 2 | 1 | 7 | ACCEPT |
saved_then_loaded | Deserialize immediately saved bytes; no | No / No | 2 | 1 | 7 | ACCEPT |
REPAIR in this controlled comparison means replacing the capture method using a verified original state. Independent selected candidates remain available in the same run. The label would become HOLD for a real candidate whose selected contents no longer exist anywhere trusted.
The exact rejection emitted for the direct alias is:
alias: selected tensor mismatch: [7.0, 11.0] != [2.0, 1.0]The executable failure assertion is the check call in require_selected comparing the complete candidate description with selected["tensors"]. Its diagnostic names both actual values and both expected values. The harness deliberately catches that expected rejection for shared candidates, then verifies that only the intended candidates failed.
Consequently, a zero exit from the seven-case harness means the experiment behaved as asserted. It does not mean all seven candidates passed the selection gate. Read the per-candidate decisions and the rejection fields alongside command status.
The disk controls also check their intended exception type and message. A permissions error cannot masquerade as successful detection of the wrong selection. Similarly, the main harness verifies structure, retained values, and reload output before classifying the expected rejection. A broken environment that prevents every load must fail the experiment rather than look like seven correctly rejected candidates.
One matching prediction would be weak evidence by itself. For example, w=1 and offset=4 also produce 7 at 3, although neither tensor matches the selection. That arithmetic counterexample explains why output equality cannot replace complete content comparison, even in a tiny model.
The evidence package should therefore preserve mapping diagnostics, both storage comparisons, candidate contents, reloaded contents, and the output witness together. The parameter and buffer explain what changed; the reload demonstrates what the recipient actually receives; the manifest establishes what the handoff was supposed to preserve.
Make promotion decisions explicit
Use the four decisions as instructions for the next owner. Each decision should identify the candidate, selected record, observed parameter and buffer contents, fresh-load result, and the unresolved issue, if any. A status without those references is difficult to review or reproduce.
ACCEPT when the capture is independent under the tested ownership schedule, both selected tensors match the manifest, and a fresh identical module restores both tensors and the expected output. For a published file, include successful readback of that file. Keep the accepted claim limited to the recorded module definition and runtime.
REPAIR when the capture operation aliases live storage and a verified original selected state remains available. Replace the defective operation with qualified deep copying or immediate serialization at the original boundary, then rerun the complete gate. Copying the already changed alias preserves the later values and does not repair selection identity.
RERUN when an existing candidate remains available but required evidence is missing: the buffer was never inspected, candidate dtype was not recorded, compatibility was not tested, or the fresh-load witness is absent. Name the missing check so the owner can close the gap without inventing a new selection.
HOLD when the original selected contents cannot be established, when no trusted artifact survives, or when ownership permits unexplained changes between qualification and handoff. Preserve the available evidence and stop the claim that the candidate represents the recorded selection. A scalar metric log cannot recreate overwritten tensors.
Distinguish recoverable evidence gaps from lost state
The difference is whether trustworthy content still exists. A forgotten buffer check can be performed against an intact candidate. An independent serialized artifact from the selected point can be loaded and reconciled with an independently retained selection record. Either gives the team something concrete to test.
An aliased mapping whose values have already followed the live model provides no equivalent historical evidence. Another deep copy of that mapping will faithfully preserve what it contains now. Its filename, step label, and metric field cannot reverse the earlier writes.
In this synthetic fixture, the original constants are supplied, so rebuilding Tiny gives a known original state. Treat that as a property of the exercise. A real selected model generally cannot be reconstructed from a short metric record or a remembered output.
The owner should document what supports recapture: an earlier trusted file, a protected independent copy, or another explicitly verified source. Without such evidence, HOLD is the honest outcome, even when a later checkpoint loads successfully and produces plausible predictions.
Assign ownership of selection, capture, and promotion
The training owner defines the selection criterion and records when it was satisfied. For this exercise, that owner supplies the synthetic step and metric metadata and confirms that the source contains 2 and 1 at capture. In an application, the same responsibility includes identifying which model instance produced the selected evaluation result.
The artifact owner controls capture timing, independent storage, and the retained manifest. That owner completes the copy or save before the source advances, protects the resulting candidate from further writes, and records the route used. A deferred serializer must receive an already independent candidate when later source updates are allowed.
The release owner checks the actual candidate being handed over. That means matching the manifest to the selection record, inspecting every declared parameter and persistent buffer, and reviewing fresh-load evidence. The release owner should not have to infer capture timing from an ambiguous variable name or an assurance that someone called deepcopy.
In a small team, one person can perform all three roles. The boundaries still matter because the obligations occur at different times. Selection establishes intent; capture preserves contents; promotion admits the artifact supported by the evidence. Naming those responsibilities helps explain where a mismatch first became possible.
For documenting reproducible data-science work, include the minimal reproducer, complete commands, recorded environment, manifest, candidate evidence, and decision. A receiving engineer should be able to follow the handoff without an oral explanation of which “best” file was meant.
Record which exact candidate was validated and which owner can still modify it. If another write occurs after qualification, the earlier evidence describes the earlier artifact. Recheck the changed candidate before using that evidence to support promotion.
Revalidate after changing the module’s state contract, custom hooks, serialization options, device path, or library version. The reason is specific: each change can affect what is captured, how it is represented, or what the receiving module restores. Record the new observations as a new qualification run instead of silently inheriting the earlier result.
For safe teardown, the disk fixture removes its temporary checkpoint directory automatically, including on ordinary exceptions. Keep the source files and JSON evidence while they support a review. When that evidence is no longer needed, delete only the named files from the owned lab directory:
.venv/bin/python - <<'PY'
from pathlib import Path
for name in (
"snapshot_fixture_evidence.json",
"capture_routes_evidence.json",
"publish_local_evidence.json",
):
Path(name).unlink(missing_ok=True)
PYPractice the model-artifact boundary in AI engineering
A model that loads successfully has passed one useful check. A selected model whose identity survives later source updates has passed a more specific contract: independent capture, complete parameter and persistent-buffer accounting, agreement with the selection record, and fresh recovery.
This small exercise makes that distinction practical. The shared candidates restore later values despite plausible metadata and compatible keys. The independent candidates preserve the selected pair, and the local publication gate refuses a correctly shaped artifact from the wrong moment. Those observations give the artifact and release owners a concrete basis for decisions.
Practice the boundary by changing one ownership decision at a time while retaining the witnesses. Move serialization after the update and expect rejection. Restore capture before the update and require both tensor checks again. Keep the selected record independent throughout, so the experiment continues to test the intended state.
The useful habit is preserving enough evidence that another engineer can explain both acceptance and rejection from the recorded contents.
For structured study, inspect the curriculum of Refonte Learning’s AI Engineering Program. Its page lists three months at 12–14 hours weekly, neural networks, deep learning, model development and optimization, scaling, ethics, and a capstone; its FAQ names PyTorch, TensorFlow, and Keras. These published topics provide curriculum context. The page does not establish that this exact aliasing lab or runtime is taught.
