Second slice: label-free coordinate prediction
Dated design and evidence record. Original wording, line order and historical source identities are preserved; use the current guides for operation.
Record sections
- Priority correction: 2026-10-17
- Source-based rationale for the provider boundary
- Current implementation evidence
- Real-model construction update (2026-10-17)
- Selected-artifact request update (2026-10-17)
- Failure, lifecycle and portable fixture evidence (2026-10-17)
- Label-free tensor composition
- Verified model-artifact byte boundary (2026-10-17)
- Label-free forward and request orchestration (2026-10-17)
- Purpose and completion boundary
- Owners and precedents
- Proposed minimal file ownership
- Callable input/result contract
- Coordinate-unit evidence gate
- Prepared upstream clarification (not sent)
- Scientific computation and exactness
- Model reuse, containment and failure semantics
- Prospective parity and coverage proof
- Implementation and review checkpoints
- Final qualification record (2026-10-17)
- Historical ten-review challenge (2026-10-14)
Second slice: label-free coordinate prediction Date: 2026-10-14 Status: 002 qualification closed on 2026-10-17 for the retained-fixture atom-coordinate callable; provider integration, SMILES geometry and archive-wide coordinate provenance remain outside this closure Implementation update: 2026-10-17 Priority correction: 2026-10-17 =============================== Author: repository coordinator. Purpose: align the remaining work with the maintainer's confirmed goal: a reproducible, contained NMRNet++ provider for the NMR API. The coordinate callable is an enabling milestone. A standalone file CLI and a separate prediction-result publication workflow are deferred; their previous requirements no longer define completion of 002. The prior design and its review rationale remain in the committed note: https://github.com/numpde/nmrnet-repro/blob/c8b26e651a0753d0e1e6375240bab25b1e8f4da1/notes/002_label_free_coordinate_prediction_20261014.txt This is an explicit scope correction, not evidence that an unimplemented gate has passed. Scientific qualification, containment, resource bounds and useful safe error propagation remain requirements. Implementation was paused during that plan/README-only correction and resumed on 2026-10-17; see the construction update below. Priorities, in dependency order: 1. Establish the provider-facing input/result/failure boundary from the current API and sibling code. Decide what molecular information the offering accepts and how it reaches the coordinate callable; record unresolved chemistry choices before adding transformations or a new transport. This investigation runs alongside model integration so later callers shape the implementation. 2. Connect verified dictionary/checkpoint/scaler bytes to the real registered model and head, without task/dataset/loss setup. Exercise real predictions on the pinned reference fixtures in the existing contained scientific lane. 3. Qualify numerical overlap, complete atom coverage, reuse, maximum admitted workload and safe failures. The repository adopts ångströms for H/C model inputs based on the documented evidence and bounded geometry check below; retain the evidence limits in the public input contract. 4. Deliver the qualified callable and compact evidence needed by the provider. Use the next big slice, 003, for provider integration against the established contract, including its container lifecycle and recipient-specific errors. Source-based rationale for the provider boundary ----------------------------------------------- Read-only investigation on 2026-10-17 examined magnet-deploy revision c5813ee35cfbb1c98a5cc5d20a94787271f26486 and nmr-api-v1 revision 6a163b7a9cc1e2c7485b681c0b2b969c7c20e6c8. These are inspected precedents, not new runtime dependencies or a decision to copy their whole frameworks. Magnet's ProviderAnalysisProcessor receives verified Job input and returns an analysis outcome; generic provider code owns Jobs, Attempts and publication. G01's model worker loads once and serves serial requests over a private socket. These owners show why an inference callable and its actual provider adapter belong on the path; a standalone file CLI/publication protocol is not required. They do not prescribe a socket worker for NMRNet or qualify NMRNet's science: https://github.com/numpde/magnet-deploy/blob/c5813ee35cfbb1c98a5cc5d20a94787271f26486/magnet_provider/analysis_contract.py#L158-L207 https://github.com/numpde/magnet-deploy/blob/c5813ee35cfbb1c98a5cc5d20a94787271f26486/models/g_01_nmr_forward/provider/magnet_provider_bindings/g_01_nmr_forward/binding.py#L1-L5 https://github.com/numpde/magnet-deploy/blob/c5813ee35cfbb1c98a5cc5d20a94787271f26486/models/g_01_nmr_forward/runner/serving_worker.py#L58-L148 The API admits user-authored Job specification text; it does not require a SMILES or coordinate model-input schema. G01's interpreter extracts explicit SMILES/solvent without inventing chemistry. Therefore the NMRNet offering still needs a deliberate input adaptation decision: coordinates need exact extraction, units and atom correspondence; SMILES additionally needs qualified geometry generation. An interpreter is an adapter choice, not an API requirement. The API's existing outcome path accepts a result schema identity and encoded result bytes, so the provider should own publication through that contract: https://github.com/numpde/nmr-api-v1/blob/6a163b7a9cc1e2c7485b681c0b2b969c7c20e6c8/nmr_api/public/job_create_request.py https://github.com/numpde/magnet-deploy/blob/c5813ee35cfbb1c98a5cc5d20a94787271f26486/models/g_01_nmr_forward/provider/magnet_provider_bindings/g_01_nmr_forward/prompts/interpreter.md https://github.com/numpde/nmr-api-v1/blob/6a163b7a9cc1e2c7485b681c0b2b969c7c20e6c8/nmr_api/provider/execution_attempt_outcome.py#L168 The provider boundary must also preserve execution uncertainty and route safe causes to each recipient. G01 distinguishes model replies lost after transmission from interpreter unavailability before transmission, and its failure policy controls which classified diagnostics reach API clients. These are applicable ownership/error lessons; their exact classifications are not automatically the NMRNet offering's policy: https://github.com/numpde/magnet-deploy/blob/c5813ee35cfbb1c98a5cc5d20a94787271f26486/models/g_01_nmr_forward/provider/magnet_provider_bindings/g_01_nmr_forward/processor.py#L170 https://github.com/numpde/magnet-deploy/blob/c5813ee35cfbb1c98a5cc5d20a94787271f26486/models/g_01_nmr_forward/provider/magnet_provider_bindings/g_01_nmr_forward/analysis_failure_policy.py Current implementation evidence =============================== Real-model construction update (2026-10-17) ------------------------------------------ The coordinator implemented the direct registered-model constructor in nmrnet_inference/model.py::load_prediction_model_from_bytes. The shared MODEL_SETTINGS also feeds the baseline; encoder defaults remain owned by the pinned upstream architecture. The dictionary loader appends MASK as upstream does. Full state loads strictly, CPUfloat32 parameters/buffers are checked, and eval mode is established without a training Task, dataset or loss. The maintained contained make prediction/model-parity lane now loads both exact 001 checkpoints and paired scalers from verified immutable member snapshots. It matches all 64 historical positions at ATOL=RTOL=0, independently checks all H/C atom indices across all four fixtures, repeats them in reversed order, and exercises synthetic 1/128-atom and absent-head cases. Synthetic geometry proves capacity only. Constructed model args match a fresh-process resolution of the upstream baseline invocation. Each model has 47,593,795 parameters. Observed cgroup grants are two CPUs, 4 GiB memory, zero swap and 64 PIDs; peak memory was 2,194,231,296 bytes. This is local retained-fixture/capacity evidence, not unit qualification, broad chemical accuracy or a completed provider interface. Focused results: model-contract 9, launcher 17, request 26, tensor parity 23; the real-model integration test passes in 10.937 seconds. Evidence is retained under .work/qualification/002-real-model-20261017/. The initial baseline-launcher.log failed its source-list expectation; the initial model-parity.log stopped at Uni-Core's duplicate-plugin guard before numerical comparison. Their .v2.log reruns pass after test-only corrections; no tolerance was changed. Released scalers emit their saved sklearn 1.5.2 versus runtime 1.8.0 warning; it remains in the logs and the unchanged fixture computation matches exactly. The final portable observation packet and failure/lifecycle proof were pending at this construction milestone; subsequent updates below close their fixture-scoped requirements and composed loading. Unit closure remains open. Test-only unit labels leave both production unit gates unset. Selected-artifact request update (2026-10-17) -------------------------------------------- nmrnet_inference/release.py::SelectedReleaseModels supplies the existing predict_request callbacks. Construction captures paths without I/O; admission still precedes dictionary reads and vocabulary checks precede checkpoint/scaler work. Each head's two immutable snapshots are verified before either is decoded. No model is retained on the loader, so the request owner releases H before C. artifacts/prediction-models.json is the normative runnable selection: dictionary and H/C fold-0 checkpoint/scaler member sizes, hashes and archive identities. Its reviewed initial values come from artifacts/reference-cpu-float32.json (the packet hash is recorded below). This is an intentional distinction of roles: the new declaration specifies what may run; the immutable 001 packet records what was observed. Production does not read historical predictions, fixtures or training logs. The scientific lane independently compares all five members and archive identities to 001 and checks the extraction allowlists. Extraction receipts alone cannot pin qualified bytes: changing both a member and its receipt could otherwise admit different objects. The loader requires agreement with the trusted declaration as well as the receipt before decoding the same bytes. Its declaration is capped at 64 KiB; extraction receipts now use the existing regular-file/no-follow boundary with an actual 1 MiB + 1 read cap (commit 12ffa34; 30 focused preparation tests passed in 0.017 s). The maintained prediction/model-parity lane mounts the new declaration read-only and executes JSON bytes through predict_request and these actual callbacks, using only test-injected unit labels. All four fixtures and their reversed repetition return complete sorted H/C atom coverage and exact historical overlap. The unchanged direct-constructor/configuration/capacity checks also pass. Three scientific integration tests pass in 33.993 s; the final observed memory peak is 1,891,094,528 bytes within the unchanged 4 GiB/no-swap grant. Focused request/selection tests pass 32 cases, including forged member receipts, paired-scaler rejection before checkpoint decoding, no I/O for rejected requests, archive mismatch, metadata bounds and decoder-cause propagation. Launcher tests pass 17 cases. Logs under .work/qualification/002-real-model-20261017/ are prediction-selection.log, baseline-launcher-selection.log and composed-model-parity.log. This is retained-fixture wiring evidence; physical unit qualification and provider integration remain open. Failure, lifecycle and portable fixture evidence (2026-10-17) ------------------------------------------------------------ The real composed failure test completes H inference, injects a C selected-loader failure with a nested private-request sentinel, and checks that the identical exception and cause reach the authorized caller and no result returns. The sentinel is carried in the test-only unit label as well as the injected cause; the model's numerical coordinates are unchanged. Captured stdout/stderr contain neither the sentinel nor the raw request. This is real-H/injected-C failure evidence, not a C decoder fault, sanitized traceback or permission to disclose arbitrary upstream causes to a future public recipient. Existing admission tests keep molecular values out of parser diagnostics and decoder context; the provider's eventual recipient projections belong to 003. baseline.sh now uses --init, following tools/container.sh's maintained test lane (revision 40b8f81, lines 262-266). The inspected SECS precedent uses the same grant for contained work: https://github.com/numpde/secs-repro/blob/34f5915c1534278bbf61b4c6592afa44cf94ab08/Makefile#L180 The rationale is signal delivery/reaping: Python must run as an ordinary child, rather than relying on PID 1's default signal behavior. Docker documents init, default signal proxying and exact-container CID-file instrumentation here: https://docs.docker.com/reference/cli/docker/container/run/ Those mechanisms motivate the change; they are not themselves runtime proof. A one-off host-orchestration probe exercised the maintained model-parity lane. Its private Docker delegator added only --cidfile, preserving the launcher's scrubbed endpoint and other arguments. It waited for the scientific test start and inspected that exact ID as running with init enabled, then sent TERM only to its captured launcher child. The launcher returned 143. A successful exact-ID engine listing then confirmed absence, without container cleanup intervention. This qualifies TERM cancellation for this supported workload/engine, not arbitrary TERM-resistant workloads or every future launcher. Local process retirement alone still does not establish engine-side retirement. Ignored probe source and raw evidence are retained in .work/qualification/002-real-model-20261017/: probe-cancellation.sh, cancellation-launcher.v2.log, cancellation-probe.v2.log. The first probe failed in its controller's /proc disappearance race; its original cancellation-probe.log and cancellation-launcher.log remain. That was a probe failure, not evidence of successful or failed engine cancellation. No broad container matching, stop, prune or cleanup was used. The public artifacts/prediction-fixture-cpu-float32.json records the composed fixture observation: original atom lists, all 137 eligible H/C predictions, 64 exact historical overlaps, request hashes, effective model identities, original verified reference/receipt identities, runtime and cgroup facts. Source hashes and configuration files reopened after execution are explicitly observations at that time; caller-reported parent HEAD does not identify dirty source bytes. Public fixture coordinates are not copied into the packet. The scientific lane emits this observation only after composed assertions and flushes it before unittest resumes stderr reporting. Public retention requires the entire container command to exit zero and the record to parse as JSON; an emitted record alone never establishes whole-lane success. It is a historical observation, not a deterministic generated-current-state file (013 U06/U19/U20). Rerunning make prediction/model-parity produces a new observation; resource peaks may differ. This packet is not final external-input qualification. The initial four-test runs passed, but one stdout/stderr-interleaved record could not be cleanly extracted. fixture-observation-init.log preserves that run; no failed extraction was accepted. Single-record writing and flushing correct the test harness without changing model computation or numerical tolerance. The final recorded lane, fixture-observation-final.log, passes all four tests in 35.521 s. The packet includes atoms in original order so an independent reader can derive every eligible index and element without decoding LMDB. Its memory sample is 1,781,399,552 bytes immediately after composed fixture/reuse execution; the later direct-model sample reaches 1,831,641,088 bytes. Both are within the 4 GiB/no-swap grant. The packet is 27,074 bytes, SHA-256 8027e0329bde7608d8e80bb5e64e94bdcdd4add2941b1c86c1bb9680bef22496. The focused launcher rerun passes 17 tests (baseline-launcher-init.log). Independent Use/Avoid/code reviews challenge code, identity scopes, exact populations, privacy and actual evidence before the path-limited commit. The bounded, dependency-light request reader and structural parser are now in `nmrnet_inference/predict.py`, with focused coverage in `tests/test_prediction_input.py`. The reader hashes the exact bounded bytes it returns for parsing. All 26 focused tests pass via the maintained `test/unit/prediction-input` Make target, which runs only this module in the pinned `docker.io/library/python:3.12.12-slim-bookworm@sha256:593bd06efe90efa80dc4eee3948be7c0fde4134606dd40d8dd8dbcade98e669c` image with read-only source mounts and network disabled. The parser requires a nonempty caller `coordinate_unit` string and preserves it; it does not establish physical unit compatibility. At this implementation milestone, code kept `QUALIFIED_MODEL_COORDINATE_UNITS` unset for H and C independently pending evidence. The later decision below records the adopted convention. The `predict.parse_request` checks the exact unit for every H/C model required by the structurally valid request before returning it, so an unqualified or mismatched head blocks downstream artifact work. Context atoms do not select a head; a C or H atom beside any context atoms still requires its matching head. The public `model.compose_net_input` repeats the check before tensor composition. Parity tests inject a test-only matching label for only the element under test; that does not qualify either real model unit. The label-free `predict_element` forward, real model constructor and `predict_request` orchestration exist. Production selected-artifact callbacks now connect to the request orchestrator; unresolved unit values still block external H/C requests. Synthetic forward tests and the real-model fixture lane have distinct scope; neither authorizes external input units. At the time of this implementation update, external coordinate requests were blocked by unset gates. The 2026-10-17 ångström decision below supersedes that status; the exact retained-fixture dictionary remains bound to the 001 packet. Malformed JSON reports the parser's safe reason and line/column; invalid UTF-8 reports the failing byte position. The decoder exceptions are not retained as causes because they hold the full request. Focused sentinel tests verify that neither exception context nor a formatted traceback exposes the request value. Input read failures remain operational `OSError` or `BlockingIOError` outcomes. This is a diagnostic-disclosure check, not a memory-erasure guarantee: Python traceback frames can retain caller-owned locals while an exception is alive. Label-free tensor composition ----------------------------- `nmrnet_inference/model.py` composes the five CPU tensors for one admitted ordered atom list and one H or C head. It uses only atom tokens, the element selection mask, centered coordinates, pairwise distances, edge types, and right-padding. Requests above 128 atoms are rejected before scientific imports or pairwise allocation; the adapter does not apply the training dataset's random crop. Exact dictionary identity/order and coordinate-unit qualification remain separate gates. The same real-atom selection produces the `select_atom` tensor and `selected_atom_indices`, a tuple of zero-based input positions in model-row order. BOS, EOS and padding are excluded, and the mapping stays outside the five model keyword arguments. The operation order follows the pinned sources. `CroppingDataset` casts selected coordinates to float32 (`NMRNetplusplus/uninmr/data/cropping_dataset.py:27-38`), then `NormalizeDataset` subtracts their per-axis mean and retains float32 (`NMRNetplusplus/uninmr/data/normalize_dataset.py:20-26`). `UniNMRTask` orders cropping before normalization, creates tokens and element masks, computes Euclidean distances, adds BOS/EOS borders, and builds edge types (`NMRNetplusplus/uninmr/tasks/uninmr.py:348-379`). `DistanceDataset` uses the SciPy p=2 distance matrix and casts it to float32 (`NMRNetplusplus/uninmr/data/distance_dataset.py:13-24`). The task's right-pad collaters pad each tensor axis to a multiple of eight (`NMRNetplusplus/uninmr/data/pad_dataset.py:13-47,49-66,88-102`). The coordinate boundary markers are scalar `0.0` arguments in the upstream task. Uni-Core's pinned `PrependTokenDataset` and `AppendTokenDataset` create each marker with `torch.full_like(item[0], token)`, so a scalar marker has the same three-value row shape as the coordinates (`https://github.com/dptech-corp/Uni-Core/blob/ace6fae1c8479a9751f2bb1e1d6e4047427bc134/unicore/data/prepend_token_dataset.py#L14-L25`, `https://github.com/dptech-corp/Uni-Core/blob/ace6fae1c8479a9751f2bb1e1d6e4047427bc134/unicore/data/append_token_dataset.py#L14-L24`). The source pin and archive hash are recorded in `requirements/unicore-source.json`: commit `ace6fae1c8479a9751f2bb1e1d6e4047427bc134`, archive SHA-256 `ae240ff27dfc4c698081260d58ce4bee30d4f437fef23bed89d4033625f21583`. `tests/test_prediction_model.py` uses these actual wrappers as its oracle and checks exact tensor dtype, shape, and values for H and C at one and 128 atoms. Its 128-atom fixture uses fractional coordinates to exercise the float32-before-centering order. It also checks CPU placement, zero BOS/EOS borders, eight-wide padding, malformed coordinates, the pre-import and pre-distance 128-atom bound, oversized integers, and non-echoing unsupported-atom errors. It verifies that selected input indices match the real rows of the mask for repeated mixed H/C atoms, and that absent elements have no selected indices or real mask rows. Unsupported-atom errors report an atom index rather than repeating caller input, retaining an actionable location without echoing the request payload. An earlier focused module run passed seven synthetic tensor tests in the locally recorded inference image `sha256:cabb4fe3e36541b0fefe54ca7c16fb21f9a60112a1e3d47992d4aa18343ecd08` with: ```text python3 -B -m unittest discover -s /workspace/tests -p test_prediction_model.py -v ``` The run used a read-only source mount and container root filesystem, no network, UID/GID 65532, no Linux capabilities, no-new-privileges, two CPUs, 2 GiB memory and swap, 64 processes, and a 64 MiB noexec/nosuid `/tmp`. This was a focused contained test run, not a maintained prediction-parity target. It established synthetic tensor parity only; no retained 001 record, coordinate-unit meaning, checkpoint/scaler loading, model call, or chemical-shift prediction was tested. The tested implementation was the 2026-10-17 working tree based on repository commit `37fa36bd92e8cc431d173f76b6b96ebebf79353e`; the model and test changes were uncommitted during that earlier run. Retained-record tensor parity now runs as `make prediction/tensor-parity`. The target uses the recorded inference image `sha256:cabb4fe3e36541b0fefe54ca7c16fb21f9a60112a1e3d47992d4aa18343ecd08`, whose upstream label must match submodule revision `3ba8b9c2f3d0a390d00685d238618345c6ab24d7`; it also checks that the image's embedded `inference.lock` and `unicore-source.json` match the read-only checkout inputs. The oracle and production adapter are compared for both H and C on all four 001 records. `src_tokens`, `src_coord`, `src_distance`, and `src_edge_type` match exactly in dtype, shape and values. The upstream oracle receives each record's real `atoms_target_mask`; production receives only atoms, coordinates, dictionary and head, and its all-real-atom output mask is checked separately. The test admits the exact prepared receipt bound into the tracked 001 packet: `artifacts/reference-cpu-float32.json` has SHA-256 `8268206ad12003dea5ef57e1a7392de69325e110b54f2b91ee0f6314c4fef5ae`, and its recorded prepared-receipt identity is SHA-256 `b866ea1a659a699cdab5cce5f3b829dc58aa28a0a60d6e1173164554e08f7e81` (7,499 bytes), equal to the receipt read from `.work/baseline/prepared/provenance.json`. The four record keys, raw-record hashes, atom counts, selected indices and ordered dictionary are checked against that packet before tensor comparison. Only identity fields are used from the packet; labels and predictions are not passed to the adapter. The retained LMDB records are Python pickles, so the parity container resolves only the NumPy reconstruction globals required by these pinned records. A focused test rejects an unapproved Python global, pickle extension opcodes and persistent IDs before resolution. Fixture files are bounded, hash-checked against the exact receipt, and decoded only in the offline, resource-limited container. The tracked 001 packet anchors the local receipt; local hashes alone are not treated as authentication. The lane grants no checkpoint, model-output or writable host path. The maintained target passed all 13 tests in the recorded inference image. The focused launcher regression passed all 16 tests in the pinned unit image, using read-only source mounts, no network, a read-only root, UID/GID ownership, no capabilities, no-new-privileges and bounded CPU, memory, processes and `/tmp`. The parity launcher consumes the retained 001 preparation and does not require the original extracted archives to remain present. This closes retained-fixture tensor parity only. It does not establish the released coordinates' external unit, model/scaler loading, model execution, predicted shifts or chemical accuracy; the coordinate-unit gate and prediction qualification remain open. Verified model-artifact byte boundary (2026-10-17) -------------------------------------------------- `tools/baseline_prepare.py::verified_member_bytes` now reads one selected extracted member through its already-open regular-file descriptor, rejects an initial size above that selection's `max_bytes`, caps consumption at `max_bytes + 1`, and compares the consumed bytes' size and SHA-256 with the extraction receipt before returning the immutable byte snapshot and its identity. The receipt identifies the locally extracted member; it is not an independent authentication of the upstream release. The existing path-returning `verified_member` still serves preparation callers. `nmrnet_inference/model.py` adds byte-only decoder helpers for the pinned Uni-Core checkpoint (`torch.load` on a byte stream, mapped to CPU with `weights_only=False`), the paired scaler (`joblib.load` on a byte stream), and the ordered dictionary (`Dictionary.load` on a UTF-8 text stream). Their decoder behavior follows the exact pinned sources: - Uni-Core `load_checkpoint_to_cpu`: https://github.com/dptech-corp/Uni-Core/blob/ace6fae1c8479a9751f2bb1e1d6e4047427bc134/unicore/checkpoint_utils.py#L244-L251 - NMRNet++ `TargetScaler`: https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/utils/data_scaler.py#L32-L38 - Uni-Core `Dictionary.load`: https://github.com/dptech-corp/Uni-Core/blob/ace6fae1c8479a9751f2bb1e1d6e4047427bc134/unicore/data/dictionary.py#L93-L108 `tests/test_prediction_model.py` verifies receipt size and digest mismatches, initial oversize rejection before reading, bounded detection of growth during reading, and that decoder inputs remain the verified snapshots after the source path is overwritten or replaced. The decoders reject path objects. An earlier run of the maintained `make prediction/tensor-parity` target passed all 18 tests in the recorded inference image with the existing no-network, read-only source policy. These tests use synthetic checkpoint, scaler and dictionary values: they do not deserialize the released checkpoint or scaler, construct either element model, or establish model compatibility, prediction parity or scientific accuracy. Checkpoint/scaler/dictionary admission and prediction qualification remain open. Label-free forward and request orchestration (2026-10-17) --------------------------------------------------------- `nmrnet_inference.model.predict_element` composes one H or C input, calls the selected upstream head in evaluation mode with gradients disabled, and requires one CPU float32 scalar per selected atom. It applies that head's paired scaler inverse exactly once, checks the returned shape and finiteness, then pairs each ppm value with the original input atom index from the same mask traversal. Selection is already performed by the pinned UniMat node-head branch: `UniNMRTask` defaults to `nmr_head` and registers it as a node-classification head, and UniMat gathers `model_input[select_atom==1]` before applying that head. With the task's `num_classes=1`, its result is one scalar row per selected atom. The loss's later reshape does not perform this selection, so the adapter validates and preserves the model's selected-row output without masking twice. The invocation, head registration and output shape follow these exact sources; inverse scaling follows the loss path: - https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/tasks/uninmr.py#L71-L79 - https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/tasks/uninmr.py#L462-L469 - https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/models/unimat.py#L276-L280 - https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/models/unimat.py#L405-L422 - https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/losses/atom_regloss.py#L32-L35 - https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/losses/atom_regloss.py#L75-L76 `predict_request` parses and admits every present head's coordinate-unit gate, then loads the selected ordered dictionary and rejects any atom without a dedicated token before invoking a model loader. Each model callback receives that same dictionary and returns only its selected model and paired scaler. This makes vocabulary rejection precede checkpoint/scaler deserialization at the orchestration boundary. It then loads and predicts H followed by C, skipping absent heads, and returns one atom-index-sorted ppm result only after every required head succeeds. A later head exception propagates and no partial result is returned. Production artifact selection and composed retained-fixture execution now pass in the lane described above. External units and final external-input evidence remain open. The focused regressions prove unit-gate-before-loader ordering, absent-head skipping, sequential H/C calls and index merging, all-or-nothing return behavior, named-head evaluation, one inverse-scaler call, output shape/dtype and finite value checks. `make prediction/tensor-parity` passed all 23 focused model tests in the recorded inference image, and `make test/unit/prediction-input` passed all 26 request-admission and orchestration tests in the pinned unit image. The unknown-vocabulary regression uses a test dictionary that maps Xe to its UNK index and proves the model-loader callback is not called; it does not exercise the released dictionary or a release loader. Malformed JSON and unqualified model units fail before dictionary loading. These runs used the maintained contained targets. They test synthetic model/scaler behavior and retained-record tensor parity; they do not load the release checkpoint or scaler, establish coordinate units, compare predicted shifts with the 001 reference, or qualify chemical accuracy. H/C remain blocked by the unset release-unit gate. Purpose and completion boundary =============================== Implement reusable direct H/C chemical-shift prediction from an explicit ordered atom list and matching coordinates, ready for a provider adapter. The composed callable must predict every H/C atom supplied, preserve original atom indices, and qualify its computation against the CPUfloat32 fold-0 reference established in note 001. Training labels, label masks, LMDB records and loss evaluation must not enter the production prediction interface. The existing model already accepts label-free tensors; this slice removes the surrounding benchmark-data dependency. Keep the same image/dependencies, dictionary, paired scalers and fold-0 checkpoints; no new downloads/installations are expected. artifacts/reference-cpu-float32.json and its original receipts remain historical evidence, not runtime configuration. Inspect the provider contract and record its input adaptation decisions now; implementation of provider transport belongs to the next big slice, 003. SMILES parsing, hydrogen addition and conformer generation require a justified scientific/input policy before implementation. Defer a standalone prediction CLI, its result-directory protocol, solvent-aware variants, five-fold ensembles, larger tensor batches, GPU/reduced precision and persistent workers. Published Table 2 ensemble accuracy is not a claim of this slice. Owners and precedents ===================== Follow existing small inference owners, not their surrounding service frameworks: - G01: load the checkpoint and resources that interpret its heads together; accept one molecule per call and keep atom identities explicit in the result. Its projected SMILES atom order is specific to G01; this adapter instead preserves the supplied atom order. https://github.com/numpde/magnet-deploy/blob/2d4ee50b0359e71900fce9289ea01f46d0f41249/models/g_01_nmr_forward/magnet/stairway/g_01_nmr_forward/predict.py#L80-L249 https://github.com/numpde/magnet-deploy/blob/2d4ee50b0359e71900fce9289ea01f46d0f41249/models/g_01_nmr_forward/magnet/stairway/g_01_nmr_forward/predict.py#L43-L67 - SECS: one loaded inference object owns the admitted model, precision and input transformations; model construction and evaluation are separate from provider transport. Reuse this ownership pattern, not its spectrum/SMILES semantics. https://github.com/numpde/secs-repro/blob/34f5915c1534278bbf61b4c6592afa44cf94ab08/src/secs_inference/model.py#L28-L185 - NMRPeak: validate actual input against the loaded dictionary before model execution; preserve an explicit boundary around the loaded stack. https://github.com/numpde/nmrpeak-repro/blob/f8055ab1065795fd46f8c1539e894cf9773f5495/families/nmrpeak/runner_runtime.py#L43-L170 - Container grants: G01's read-only runtime and explicit resource/input grants are precedent; adapt the present repository's offline one-shot launchers rather than copying its service topology. https://github.com/numpde/magnet-deploy/blob/2d4ee50b0359e71900fce9289ea01f46d0f41249/models/g_01_nmr_forward/runner/serve.compose.yml#L6-L29 Local authorities to reuse: artifacts/*.selection.json plus verified extraction receipts identify model members; tools/artifacts.py owns archive-member checks and the staged-directory publication used by artifact extraction, not prediction receipts; tools/baseline_prepare.py owns file identity and mounted execution provenance; tools/baseline_model.py owns existing model/dictionary/load qualification checks; tools/baseline.sh and tools/inference_image.sh own current image admission and container conventions. Share only functions genuinely needed by both callers. Do not make prediction call baseline preflight just to manufacture a training task or copy its full task/loss path. Execution provenance now uses exact repository-relative source names paired with the paths mounted by the launcher. One action-specific source list drives both mounts and `--tool-source` arguments; `execution_identity` remains the single owner of duplicate-name rejection. Receipt keys therefore distinguish same-basename files in different directories, while existing baseline tool names and historical receipts remain unchanged. Focused contained coverage passed: 28 preparation tests, 11 baseline-run tests, and 16 launcher tests. The exact maintained commands are `make test/unit/baseline-prepare`, `make test/unit/baseline-run`, and `make test/unit/baseline-launcher`; each runs only its named module in the pinned unit image. The run target was added at: https://github.com/numpde/nmrnet-repro/blob/1fbcb3408977721e90ec3dfa73c3f75fc2740973/Makefile#L71-L78 The source-pair behavior is implemented in: https://github.com/numpde/nmrnet-repro/blob/54e3bc3e4a3151cdf4ee1cd084db77bd4e4bf986/tools/baseline_prepare.py#L145-L173 https://github.com/numpde/nmrnet-repro/blob/54e3bc3e4a3151cdf4ee1cd084db77bd4e4bf986/tools/baseline.sh#L102-L147 https://github.com/numpde/nmrnet-repro/blob/54e3bc3e4a3151cdf4ee1cd084db77bd4e4bf986/tools/baseline.sh#L269-L273 Tests cover equal basenames, duplicate names and exact source-to-mount pairs: https://github.com/numpde/nmrnet-repro/blob/54e3bc3e4a3151cdf4ee1cd084db77bd4e4bf986/tests/test_baseline_prepare.py#L40-L89 https://github.com/numpde/nmrnet-repro/blob/54e3bc3e4a3151cdf4ee1cd084db77bd4e4bf986/tests/test_baseline_launcher.py#L123-L139 Proposed minimal file ownership =============================== - nmrnet_inference/model.py: the loaded scientific boundary and exact label-free tensor preparation, using upstream classes/helpers wherever available. - nmrnet_inference/predict.py: bounded single-molecule JSON input, atom ordering and all-or-nothing H/C callable composition. Provider publication belongs to the later provider integration, with no parallel result protocol in 002. - Reuse the contained scientific lane for real-model qualification. Extend its launcher only where the callable's actual inputs/grants require it; no host Python or separate product CLI is needed to complete 002. - tests/test_prediction_input.py: lightweight offline admission/orchestration tests in the ordinary maintained test image; do not add Torch to that image. A fresh contained process must import the lightweight admission path with Torch and Uni-Core unavailable, and reject malformed JSON, field, shape, count and raw number inputs. Test that all input admission completes before checkpoint/scaler deserialization, model construction or forward execution. Dictionary-dependent vocabulary checks use the admitted dictionary before checkpoint loading. Keep heavy imports behind the lightweight checks; do not add an import framework. This applies 013 U11's dependency-boundary and forbidden-effects guidance; see its cited source and rationale at https://github.com/numpde/nmrnet-repro/blob/e6a8a1b1a2dd5cb854d70277f40d3275945ce933/notes/013_sibling_practices_20261016.txt#L151-L160. - tools/prediction_parity.py plus a maintained qualification target: scientific tensor/checkpoint/prediction proof in the admitted inference image, against the verified original records and the 001 reference. Test data and original labels stay here, outside the prediction interface. Consolidate filenames if fewer natural owners suffice. No generic adapter framework, registry, configurable backend factory or mirrored model architecture is needed. No fork change is currently justified. If an upstream helper needs extraction, propose the smallest fork change, prove equivalence and update the submodule; do not patch image-owned scientific source silently. Callable input/result contract =============================== The existing bounded JSON reader is the coordinate callable's admission boundary, not an NMR API Job schema or a promise of a file CLI. Qualification passes one molecule's exact bytes with coordinate_unit, atoms and coordinates. The provider will need a reviewed mapping from its admitted Job input to this boundary; this plan does not prescribe how user-authored text becomes molecular input. Checkpoints, device, precision, masks and model settings remain fixed. For example: {"coordinate_unit":"angstrom","atoms":["C","H"], "coordinates":[[0.0,0.0,0.0],[1.09,0.0,0.0]]} Every request must include `coordinate_unit`. Parsing preserves its exact spelling and does not default, normalize, rescale coordinates or infer physical meaning. Before returning, it compares that exact label with the evidence-qualified unit for each present H/C prediction head; other atoms remain context and do not select a head. Until a needed model unit is qualified, parsing rejects the request before model or artifact work. After qualification, only an exact label match is admitted. Every prediction entrypoint must call this parser before artifact access and retain the composer's second check before tensor creation. This keeps the possible training unit open without guessing. This example illustrates shape only, not a qualified molecule. Caller atom inventory and order are authoritative: no addition/removal/reordering, canonicalization, coordinate generation or repairs. These fields do not establish bond/valence validity. Proposed admission bounds: at most 1 MiB input bytes, 1-128 atoms and one 3-vector per atom. These are local resource choices, not learned model chemistry limits. Read at most the byte limit plus one, then hash and parse the same bounded buffer; never hash the input path separately and reopen it for parsing. A read-only mount does not prevent a host rewrite: provenance must identify exactly the bytes used. In contained scratch, test both an initially oversized source and one that grows past the limit during reading, with appended bytes available before the reader reaches EOF. Prove total consumption stays within the byte limit plus one and oversized input is rejected before parsing. Inject a read failure and keep it an operational error rather than classifying it as malformed user input. This applies 013 U13's consumed-work and operational-failure guidance; see its cited source and rationale at https://github.com/numpde/nmrnet-repro/blob/e6a8a1b1a2dd5cb854d70277f40d3275945ce933/notes/013_sibling_practices_20261016.txt#L176-L184. Check atom counts and row dimensions before array conversion or quadratic allocation. Reject extra fields (including labels or selection-mask overrides), duplicate JSON object keys, unknown atom symbols and model special tokens. Atom symbols must have dedicated ordinary chemical-element entries in the loaded dictionary; mapping to UNK is not allowed. Reject booleans used as numeric coordinates and nonfinite numbers. Check finite values both before and after float32 conversion: finite JSON/float64 values can overflow float32. Centering and distance construction must also produce finite tensors before model execution. Output has unit="ppm" and predictions ordered by original zero-based atom_index, each with atom_index, element and shift_ppm. Every H/C atom appears once; other atoms remain molecular context. Absent nuclei yield no entries; neither H nor C yields an empty list without an empty-tensor model call. Hydrogen completeness belongs to the caller. Qualification evidence carries artifact identities and resource facts; per-prediction entries keep atom identity and shifts. Provider result/provenance projection belongs to its later adapter. Coordinate-unit evidence gate ============================= Bounded primary-source search (2026-10-17): the release v1 metadata and Files at https://zenodo.org/records/18232165 (18 January 2026) were checked for the selected release's input convention; no checkpoint-specific unit statement was found. The official https://github.com/YongqiJin/NMRNetplusplus#readme, Datasets & Models and Running, was checked for a newer inference contract; it links the release/scripts but states no applicable coordinate unit. This moving-main observation has no recorded commit ID and is discovery evidence only, not pinned checkpoint provenance. Issue 1, https://github.com/YongqiJin/NMRNetplusplus/issues/1 (opened 1 July 2026; Description/Activity viewed 17 October 2026), was checked for release clarifications; its missing-checkpoint/split discussion supplies no applicable unit statement or displayed maintainer clarification. The paper, https://arxiv.org/html/2601.18524v1, sections 3.1, 3.3, 4.1 and appendices A/C/D, was checked for preprocessing/model-input scale; no explicit inference unit tied to the selected checkpoint bytes was found. These bounded negative findings do not establish that no other evidence exists. No author contact, new downloads or scientific execution occurred in this search. **Decision, 2026-10-17: use ångströms for H/C coordinate inputs.** The repository records this as its operational input contract, enforced by `nmrnet_inference.predict.QUALIFIED_MODEL_COORDINATE_UNITS = {"H": "angstrom", "C": "angstrom"}`. Requests must state exactly `angstrom`; no conversion is performed. This decision uses three pieces of evidence: the pinned upstream preprocessing path generates RDKit conformers, whose documented bond-distance convention is ångströms; the model dataset wrapper centers coordinates without rescaling; and `tests/test_bond_distances.py` compares two exact retained validation records with NIST gas-phase structural values. The test finds C–H distances near the references and C–Cl distances near the references at the declared tolerances. The empirical check covers only CH3Cl and CH2Cl2 from one validation LMDB, and uses a broad 0.04 Å C–Cl tolerance because the released records have C–Cl distances near 1.803 Å (the two experimental references are 1.785 and 1.767 Å). It supports an ångström-scale interpretation and catches gross scale errors; it does not prove every archived coordinate record or the exact historical training input producer used ångströms. The archive-to-training-input producer link remains unknown as documented below. This is an explicit operational choice under bounded evidence, not a claim of universal provenance or full scientific qualification. If later source evidence contradicts this convention, revise the contract and qualification record. `require_model_coordinate_unit` enforces the same selected unit for each model head and rejects every other label before artifact loading. H and C therefore admit a combined request under one explicit unit, with no implicit conversion. `parse_request` checks all H/C models needed by a structurally valid request before returning it; `nmrnet_inference.model.compose_net_input` repeats the same check before building tensors. Tensor-parity tests temporarily inject a test-only matching label for the element under test; they compare transformations only and do not qualify or authorize prediction. Each gate can close using one of the prior qualification routes below. The repository has since adopted the operational convention documented above. `tests/test_prediction_input.py::PredictionInputTests.test_non_angstrom_model_input_blocks_tensor_composition` proves both heads reject a mismatched unit at tensor composition; `tests/test_prediction_input.py::PredictionInputTests.test_request_admission_requires_units_for_every_present_model_head` proves each present head is checked when one head is explicitly unqualified, including a combined H/C request; `tests/test_prediction_input.py::PredictionInputTests.test_context_atoms_do_not_select_heads_or_hide_a_present_head` proves context-only input selects no head and a context atom cannot hide an unqualified present C head; `tests/test_prediction_input.py::PredictionInputTests.test_different_head_units_only_admit_matching_single_head_requests` proves separate H/C unit qualifications are honored independently and a combined request is rejected when one caller label cannot match both; `tests/test_prediction_input.py::PredictionInputTests.test_composition_requires_exact_qualified_unit_and_allows_any_unit_label` proves an explicitly mismatched head label is rejected before ordinary atom validation. `tests/test_bond_distances.py` checks all four proposed C–H/C–Cl bonds in each retained molecule against the NIST values documented in `tests/fixtures/small_chloromethanes.txt`. Its 0.01 Å C–H and 0.04 Å C–Cl tolerances are acceptance thresholds, not experimental uncertainties. `make test/unit/bond-distances` and `make test/unit/prediction-input` are the contained focused lanes for these checks. An earlier `make prediction/tensor-parity` run passed 18 tests using only the test-only label above; neither lane establishes the physical unit. The current tree has a label-free per-head forward, request orchestrator and real model constructor exercised separately on retained fixtures. Production artifact- selector callbacks now connect to the orchestrator. The adopted angstrom unit label is checked for H/C requests before the loader callback; parsing alone does not qualify external molecular inputs beyond the stated coordinate contract. The pinned upstream preprocessing constructs explicit-H RDKit molecules and stores `mol2coords` results as `coordinates`. The helper embeds a conformer, optimizes it with MMFF when embedding succeeds, then stores `mol.GetConformer().GetPositions()` as float32: https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/data_process/filter_nmr_data.py#L27-L39 https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/data_process/filter_nmr_data.py#L155-L171 https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/data_process/utils.py#L146-L192 RDKit's `GetBondLength` documentation defines conformer bond lengths in angstroms, which supports the RDKit conformer-coordinate convention: https://rdkit.org/docs/source/rdkit.Chem.rdMolTransforms.html The paper corroborates RDKit conformer generation (§3.1; Appendix A, “3D conformation generation”), but §3.1 identifies its constructed dataset as NMRexp, so it does not establish the producer of this released NMRShiftDB2 file: https://arxiv.org/html/2601.18524v1 The pinned `filter_nmr_data.py` describes a possible RDKit preprocessing route, but not provenance for this member. Its defaults read `data/raw_data/NMRexp_10to24_1_0811.parquet` and write `data/raw_data/NMRexp_10to24_1_0811_H_max_chiral_1_max_atoms_70_ele_C_H_O_N_S_P_F_Cl_filtered.lmdb`, not `data/nmrshiftdb2/All/valid.lmdb`: https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/data_process/filter_nmr_data.py#L155-L176 https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/data_process/filter_nmr_data.py#L180-L203 The NMRexp paper documents `NMRexp_10to24_1` as Parquet structure-spectrum records with SMILES, source-page metadata, NMR text/peaks and atom counts; its schema does not document a coordinate field. Zenodo v1 lists `NMRexp_10to24_1_0811.parquet` (MD5 `37ecb7f0ca391e67d4ab21c2cdc02a57`), and v2 lists `NMRexp_10to24_1_1004.parquet` (MD5 `2f9ed8bc533364dfb9dce96c83703dd4`). Neither record lists the logged `NMRexp_v0905/H|C` LMDBs or maps those paths to the Parquet files. These releases show related dataset versions, not the bytes or coordinate producer of the logged training inputs: https://www.nature.com/articles/s41597-025-06245-5 https://zenodo.org/records/16809534 https://zenodo.org/records/17296666 An older NMRNet archive provides a related dataset comparison, not a producer link. Its Methods say the prior `nmrshiftdb2-2018` dataset uses data “published by Chio et al.” but attach the Kwon et al. (2020) citation, whose author list includes Choi; they say it retains the train/test split from Jonas and Kuhn. The same paragraph attributes RDKit `EmbedMolecule` and `MMFFOptimizeMolecule` conformer generation to the new `nmrshiftdb2-2024` dataset. It does not identify how the 2018 benchmark's coordinates were generated. Jonas and Kuhn's 2019 source identifies NMRShiftDB revision 1624 and describes a model based on bond-order connectivity, but no 3D coordinate preprocessing or coordinate units: https://link.springer.com/article/10.1186/s13321-019-0374-3 https://arxiv.org/html/2408.15681v1 Its supplement compares the 2018 and 2024 sets (Figure 3, pp. 9–10) without adding a coordinate-generation account for 2018: https://media.springernature.com/original/springer-static/esm/art%3A10.1038%2Fs43588-025-00783-z/MediaObjects/43588_2025_783_MOESM1_ESM.pdf Zenodo v2 (record 10.5281/zenodo.14279498; `data.zip` MD5 `75fe82a1c9ef7a6749cbf3a3896f198f`) has an archive preview listing `data/nmrshiftdb2_2018/All/valid.lmdb` at rounded size 21.1 MB and a separate 2024 `valid.lmdb` at 35.2 MB: https://zenodo.org/records/14279498 https://zenodo.org/records/14279498/preview/data.zip?include_deleted=0 Zenodo v3 (record 10.5281/zenodo.19142375) reports the same `data.zip` MD5. Its archive preview lists `data/nmrshiftdb2_2018/All/train.lmdb` at rounded size 84.5 MB. The member path matches the fold-0 training log. The locally inspected release archive (record 10.5281/zenodo.18232165) has a differently rooted `data/nmrshiftdb2/All/train.lmdb` at exactly 84,492,288 bytes (`artifacts/data-archive-members.json`, bound to that archive by `artifacts/data-archive-inspection.json`), also rounding to 84.5 MB. This is a path-and-size candidate only: neither record provides a per-member hash linking its listed member to the logged training input. https://zenodo.org/records/19142375 https://zenodo.org/records/19142375/preview/data.zip?include_deleted=0 The selected member here is `data/nmrshiftdb2/All/valid.lmdb`, exactly 21,131,264 bytes with SHA-256 `a72e17e3c9293b5852ef8d493f4ede23eb8b5cd8ca8e4c827a9bd8072e6a3a04` (`artifacts/data.selection.json`, `artifacts/source-attribution.json`, and `artifacts/reference-cpu-float32.json`). The directory roots differ; the preview gives no member checksum or producer. Rounded sizes do not establish byte identity. For this exact release member, `artifacts/source-attribution.json` records the original file-producing program and revision as unknown. `artifacts/release.json` and `artifacts/data.selection.json` identify its Zenodo archive member; `artifacts/reference-cpu-float32.json` binds the member bytes. The upstream README associates the release with that Zenodo record but does not name a file producer. These sources establish artifact identity, not that `mol2coords` created its coordinates: https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/README.md#L13-L16 Therefore the RDKit unit convention is supported, but its link to the exact released coordinates is not proven. Keep the original producer and revision unknown unless a direct producer-to-release link or retained producer evidence is found. Producer attribution and empirical unit compatibility are separate claims: the former may remain unknown even if the latter is qualified. The H/C experiment-level `finetune.log` members are identified by path, size and SHA-256 in `artifacts/reference-cpu-float32.json`; each contains training argument records for folds 0–4. The manifest selects fold 0 for H and C (`elements.H.fold` and `elements.C.fold`). The fold-0 records name `./data/nmrshiftdb2_2018/All` as the labeled source and `./data/NMRexp_v0905/H` for H or `./data/NMRexp_v0905/C` for C as the respective unlabeled source. The pinned `uninmr/tasks/uninmr.py` opens `train.lmdb` under each path. The authenticated archive `data.zip` (SHA-256 `a6a23990cef1f0c28f835a4fdc9a5437d9d2f2bb13c758c07b7dc10c887c5b73`) was inspected with `make weights/inspect ARCHIVE=data.zip`. The portable summary receipt is `artifacts/data-archive-inspection.json` (SHA-256 `5de73d613702b2f0a3490c5196d413141a0e9eb71cb6201d5e4428200d6cffb1`). Raw command output is not retained. The complete member listing is `artifacts/data-archive-members.json` (SHA-256 `de16d977956cadf6432df6854df3cd656d352f68d09a34f2cc129a368f36ad6c`). It lists `data/nmrshiftdb2/All/{train,valid}.lmdb`, ShiftDB-Lit train/validation members and the NMRexp raw Parquet. No retained member hash or mapping binds those differently named archive members to the logged training inputs or establishes their coordinate producer. The selected and hashed `valid.lmdb` is a validation member, not evidence of the exact coordinates used to train either checkpoint. The prior qualification plan considered these evidence routes for establishing the expected inference unit separately for each selected H/C checkpoint: 1. A publisher's input contract tied unambiguously to the selected checkpoint paths/hashes states the coordinate unit and any input-scale transformation. 2. An auditable checkpoint/run configuration and applicable preprocessing chain establish that same inference convention. Uniform pipeline evidence can establish this without reconstructing every historical training record. The repository chose the operational convention based on the bounded evidence above rather than claiming historical producer attribution. The test is deliberately narrow and does not turn sampled evidence into an archive-wide claim. The executable gate now expresses the chosen contract; any evidence of a different producer convention requires revisiting it. Prepared upstream clarification (not sent) ----------------------------------------- The remaining missing fact is not answered by another local numerical rerun. The following question is prepared for a release-author clarification; no issue, message or other author contact has been sent. Posting it requires the maintainer's explicit instruction to contact others. For Zenodo release 18232165, what physical unit is expected for raw [N,3] inference coordinates before the published centering/distance pipeline? Please confirm separately for these exact fold-0 files in checkpoints.zip: H: checkpoints/scratch_H_pretraining_molecular_unimol_large_atom_regloss_mae_lr_0.0004_bs1_4_bs2_16_wu_0.03_ep_10_wgt_16_T__ratio_4_16_20250930_033837/cv_seed_42_fold_0/checkpoint_last.pt SHA-256 bc12977a19ddad7057c8c4d190203cdae8130b52e54f3ddbf37d875663de04e9 C: checkpoints/scratch_C_pretraining_molecular_unimol_large_atom_regloss_mae_lr_0.0004_bs1_4_bs2_16_wu_0.03_ep_10_wgt_16_T__ratio_4_16_20251004_182236/cv_seed_42_fold_0/checkpoint_last.pt SHA-256 888c2fdcdd0379ded4c6de7322414fcf98658f4092adf8687a54329643c0376a Does the same coordinate-unit convention apply to both checkpoints? Was any coordinate scale conversion applied before the published model-input pipeline? If so, which exact transformation should an inference caller apply? This asks about coordinate units, not the ppm units of the output shifts. A released validation-record match alone does not settle the expected convention for external inputs. The local request field is a caller declaration, not a check of physical scale: the input has no bond graph from which the callable could verify the declaration. When the gate closes, accept only the qualified unit spelling, reject unsupported units, and never infer or convert. Unit qualification does not establish producer identity, exact conformers, arbitrary-input chemistry or model accuracy. Retained-coordinate parity can proceed independently, without installing RDKit or generating geometry. Scientific computation and exactness =================================== Use upstream revision 3ba8b9c2f3d0a390d00685d238618345c6ab24d7 and the Uni-Core source declaration already admitted by the image. The following order matters: The expected tensors and predictions are qualified against the exact upstream revision, Uni-Core source, inference image and model members identified by the retained 001 reference. If a reference-producing source or dependency changes, keep the old expectations and evidence, and require a separately reviewed, version-keyed fixture set for the replacement. A missing matching fixture set blocks qualification; do not regenerate expected values silently to make a new version pass. This applies when a numerical reference producer changes, not to an unrelated image rebuild. See 013 U21 and the producer identities in `artifacts/reference-cpu-float32.json`. 1. Validate atom vocabulary, shape, bounds and numeric representability. Preserve every original atom index. Cast coordinates to float32 before centering. CroppingDataset does this cast even when no crop occurs; the adapter rejects oversize inputs and never enters random cropping. https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/data/cropping_dataset.py#L27-L38 2. Subtract the coordinate mean in the upstream operation order and retain float32. Do not center float64 inputs first and assume numerical equivalence. https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/data/normalize_dataset.py#L20-L26 3. Load the exact ordered dictionary and append [MASK] exactly as the task does. For this release there are 30 symbols before MASK and 31 afterwards, MASK=30. Tokenize atoms as int64 and add BOS/EOS. H/C output masks select all tokens of the corresponding element; special/padding positions are zero. The model sees all atoms, not only the atoms whose shifts are requested. https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/tasks/uninmr.py#L174-L183 https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/data/select_token_dataset.py#L10-L40 https://github.com/dptech-corp/Uni-Core/blob/ace6fae1c8479a9751f2bb1e1d6e4047427bc134/unicore/data/tokenize_dataset.py#L13-L28 4. Compute pairwise distances through the same SciPy distance_matrix operation, then cast to float32. BOS/EOS rows and columns in the distance matrix are zero. This is not the distance matrix of coordinates with zero-position special tokens inserted first. Edge IDs use token_i * dictionary_size + token_j, including the special tokens, and use int64. Separately prepend and append a zero coordinate 3-vector for BOS and EOS to the centered real-atom coordinates before right-padding src_coord. Check these two rows explicitly in the tensor oracle; they are distinct from padding added to reach a multiple of eight. https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/data/distance_dataset.py#L20-L24 https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/data/distance_dataset.py#L74-L86 https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/data/pad_dataset.py#L104-L116 https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/tasks/uninmr.py#L366-L378 5. Preserve upstream right padding to a multiple of eight, even for batch 1. Pad tokens with dictionary PAD=0, masks/coordinates/distances/edge IDs with zero. Separate 128 original atoms, 130 logical tokens and 136 padded positions at the admitted maximum; resource bounds must use the padded size. Do not truncate to the logical-token bound after padding. https://github.com/dptech-corp/Uni-Core/blob/ace6fae1c8479a9751f2bb1e1d6e4047427bc134/unicore/data/pad_dataset.py#L12-L38 https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/data/pad_dataset.py#L96-L102 6. Instantiate the real UniMatModel and its real nmr_head with the qualified architecture and dictionary; load the selected full state with no missing, unexpected or shape-mismatched keys. Use the released configuration plus explicit qualified inference choices, not a second manually reconstructed network. Verify agreement with 001's resolved architecture. No fabricated task object, dataset, target tensor or loss is required. https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/models/unimat.py#L143-L207 https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/tasks/uninmr.py#L461-L469 7. Execute model.eval() and no_grad() with CPUfloat32, batch 1 and the existing two-thread settings. Pass the per-element select_atom mask and explicitly call model(..., features_only=True, classification_head_name="nmr_head"), matching the qualified upstream invocation rather than forward's defaults. Forward selection occurs at that node head, preserving row order. Require one output row per selected atom and one scalar per row before inverse scaling. Retain an explicit index mapping back to the input atoms. https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/losses/atom_regloss.py#L32-L35 https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/models/unimat.py#L209-L288 https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/tasks/uninmr.py#L471-L477 8. Apply that element's paired StandardScaler inverse once, preserving the Torch-input conversion and final float32 cast qualified in 001. Return finite ppm values only. Do not fit a scaler, hand-substitute its mean/scale in the production path, average folds, or compute training/accuracy losses. https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/losses/atom_regloss.py#L75-L76 https://github.com/numpde/fork-of-NMRNetplusplus/blob/3ba8b9c2f3d0a390d00685d238618345c6ab24d7/uninmr/utils/data_scaler.py#L32-L54 Compose upstream helpers/collaters where they express the transform clearly; do not copy the task pipeline or hide dependence on label-bearing task setup. Model reuse, containment and failure semantics ============================================= The loaded scientific object owns one fixed element's model, dictionary and scaler, and predicts that element's atoms in one molecule at tensor batch 1. The composed callable loads H, predicts and releases H, then does the same for C. Skip an absent element without an empty model call. Merge by original atom index and return success only after both required predictions succeed; a C failure cannot return the earlier H result as a successful answer. The qualification loop also loads H once for all qualification fixtures, releases it, then loads C once for those fixtures, skipping absent elements. Exercise both loaded-model reuse and composed request calls. This permits ordinary model reuse without a public batch protocol, daemon or concurrent model copies. Use the existing admitted immutable inference image: both embedded dependency declarations must match, and its upstream revision must match the recorded submodule. Verify extraction receipts and every consumed checkpoint/scaler/ dictionary member before trusted deserialization, and ensure deserialization consumes those same verified bytes. `baseline_prepare.verified_member` currently checks a member and returns its path; a later path reopen leaves a replacement or in-place write gap. Deserialize a verified immutable byte buffer or a snapshot protected from mutation through loading. A read-only bind mount does not stop another writer to its backing file; a held descriptor prevents pathname substitution but permits same-inode writes. In the contained qualification lane, simulate both changes to the source after verification; require loading to retain the verified bytes or reject the change. This protects against concurrent source changes within the stated container/host boundary, not a hostile host or engine administrator. Record the identities of the exact bytes consumed. Dictionary ordering and full state compatibility remain hard admission requirements. A pickle from a user input path is never a supported format. Record the actual adapter/helper bytes alongside the image, repository/source revisions, model identities and the hash of the same bounded input buffer consumed by the parser. This extends the local path-returning helper contract for a specific reason; it does not establish release authenticity from a locally computed hash alone. See 013 U05 and: https://github.com/numpde/nmrnet-repro/blob/e6a8a1b1a2dd5cb854d70277f40d3275945ce933/tools/baseline_prepare.py#L132-L163 The launcher owns the local engine and exact container lifecycle. All project Python/tests/probes run contained: network=none, pull=never, nonzero UID/GID, read-only root/inputs, cap-drop ALL, no-new-privileges, two CPU quota units, 4 GiB/no swap, 64 PIDs and baseline-bounded scratch. Grant only required inputs, tools and dedicated output; no socket/home/credentials/workspace-root/devices. Cancellation removes only the owned container, identified by this attempt; verify that the engine reports it terminated. Local native-helper process retirement alone does not prove engine-side work ended. This is a cancellation acceptance check from 013 U14, not a new daemon; see the [helper's retirement boundary](https://github.com/numpde/nmrnet-repro/blob/e6a8a1b1a2dd5cb854d70277f40d3275945ce933/tools/inference_native.sh#L7-L10). Future acquisition still requires explicit command-line WLAN_INTERFACE; prediction never acquires. Use tools/container_guard.py for the new Python entrypoints and reusable scientific modules, checking before dependency imports or side effects. The launcher supplies NMRNET_IN_CONTAINER=1 explicitly and mounts and hashes the actual shared guard source with the other executed files. This prevents accidental direct host execution; engine grants enforce containment. Do not introduce a second detection scheme. Exercise missing-marker rejection only in contained subprocesses. The maintained test/repository convention suite automatically includes nmrnet_inference/ sources and enforces hierarchical Make target names; keep this slice within that existing policy. Validate the whole bounded molecule input before any model forward. Inject a C failure after H has succeeded and prove the composed callable raises instead of returning partial success. The qualification harness owns its run evidence, including input/model/tool identities and safe causal diagnostics; it must preserve prior results and report failed or incomplete runs honestly. Reuse existing evidence/publication mechanisms where applicable. A separate product result.json/complete.json protocol and its storage-fault acceptance matrix are deferred with the standalone runner. The prior design is preserved at the revision linked in the priority correction above; do not create another publication owner before the provider's actual boundary requires it. Carry failures to each actual recipient through the channel they use; stderr is only one possible surface. The callable must preserve useful safe causes; the contained qualification harness carries them to developers/operators, and the later provider adapter projects them for its actual recipients. Retained failure evidence must preserve the causal facts needed for later authorized diagnosis. Say whether input is invalid, an artifact is incompatible, or execution/publication failed; identify the affected atom, field or artifact and the non-secret cause where applicable; report actual effect state and a safe next action when known. Keep classification, causal evidence and retry/final disposition distinct. Propagate the fullest safe cause/context and add owned context across wrappers and publication without replacing a specific failure with a generic code, status or message. Redact credentials, secret values and private molecular/host/session details at each disclosure boundary without discarding adjacent useful diagnosis. Nonfinite output fails; no device/precision/scaler/geometry/checkpoint fallback or error framework. Retain qualification failures; omit private machine/session facts from committed evidence. Review callable exceptions and the qualification harness's actual diagnostic surfaces, including chained causes and effect state. The coordinate callable returns to its authorized in-process caller and owns no error logging or publication. Preserve loader/inference exceptions and causal chains, which may contain sensitive evidence; never describe raw tracebacks as safe public error text. A contained real-H/injected-C test carries a private sentinel in the request and nested cause, checks unchanged exception/cause propagation, no partial result, and no sentinel/request emitted to stdout/stderr. Admission diagnostics independently exclude raw request data, including decoder context. This is an explicit 2026-10-17 boundary correction to the broader diagnostic acceptance in revision 40b8f81 of this note, following independent Use/code review. That wording conflated causal preservation for an authorized caller with disclosure to another recipient. The qualification harness handles public release fixtures; it is not a private-request reporting interface. Provider recipient projection, nested-cause redaction and secondary failures of that reporting belong to 003's actual disclosure owner. They remain required there; they are not claimed proven here. The current callable has no competing secondary reporting operation. Qualification observation delivery still must succeed and the whole lane must exit zero before evidence is retained; printing a packet is not success. This applies 013 U08/U23 by actual owner, without adding a speculative formatter, standalone CLI or new error protocol. Controlled admission diagnostics, causal preservation and honest qualification failures remain 002 requirements. Prospective parity and coverage proof ==================================== The qualification loop owns four separate single-molecule fixtures from 001's two H and two C source records, with an explicit source-key/atom-index identity crosswalk retained in its evidence. Those identities are not public input fields. Before fixture iteration, assert the exact H and C source-key sets and the exact set of 64 `(source_key, atom_index)` pairs from the pinned 001 reference; matching the count alone can hide a substituted or omitted position. The population source is `artifacts/reference-cpu-float32.json`, SHA-256 `8268206ad12003dea5ef57e1a7392de69325e110b54f2b91ee0f6314c4fef5ae`. Add only the minimal cases needed where those records lack unlabelled eligible atoms or an absent nucleus. These fixtures qualify preprocessing, output coverage and agreement with the retained baseline; they do not establish broad chemical accuracy. Keep three proofs separate: A. Tensor parity: for each original reference molecule, compare every shared model tensor against the real upstream dataset/collater, including float bits, dtypes, shapes, coordinate cast/centering order, distances, special tokens, edge IDs and padding. Source the oracle from verified original records. Its real training labels are allowed in test-only upstream preparation, never copied into the adapter request. Selection masks intentionally differ where source labels were absent; verify the production all-H/all-C mask against atom identities, and verify the oracle mask against original reference evidence. Exact shared-tensor parity is required before comparing predictions. B. Existing-output parity: compare adapter outputs at the exact 64 labelled source record/atom positions with artifacts/reference-cpu-float32.json. This proves overlap with 001, not full output coverage. The production interface has no selection-mask override to make this test easier. Test-only diagnostic calls may compare the real upstream model at matched selection shapes, but such a diagnostic cannot substitute for testing the ordinary all-atom path. C. Complete label-free coverage: independently count all supplied H and C atoms and assert exactly those original indices appear once, in order, with finite values. Include unlabelled eligible atoms and molecules with an absent nucleus; neither label availability nor label values may decide production coverage. Compare repeated independent calls and reordered fixture execution using the qualification crosswalk to expose leaked model state. Caller labels/target/mask fields are rejected, not consumed. The `.work` run is not the only retained evidence for a published qualification claim. The reviewed observation packet is `artifacts/prediction-cpu-float32.json`. It records source keys and record hashes, derived-request hashes, the exact expected population, per-tensor digest/shape/ dtype observations, complete H/C predictions and the 64-position comparison. It binds fixture production to exact helper and upstream source bytes, model member identities, image/runtime identity, numerical policy, and purpose. The source artifact is marked `NOASSERTION`, so the packet omits its coordinate payload and states the exact archive/member hash and rerun requirement. Keep raw run logs and scratch under `.work`; publish no source data without an established distribution basis. The packet records one historical execution and is immutable; qualification commands produce new run evidence. Any later deterministic summary generated from that packet needs paired write and no-write checks from the same canonical inputs under 013 U20. This evidence policy follows 013 U06/U19 and preserves the distinction between observation and rerun. Numerical policy must be declared and reviewed before the first adapter/reference output comparison. Shared tensors and matched-selection model execution start with exact equality (ATOL=RTOL=0) in the same recorded image/CPU/thread runtime. The initial all-atom overlap comparison also uses ATOL=RTOL=0. Unlike 001's two identical executions, selecting additional atoms changes the node-head matrix shape, which can change floating-point rounding even with identical encoder features. Thus exact overlap is an explicit acceptance target, not an assumed mathematical guarantee. The owner must inspect this risk before output comparison. If a nonzero allowance is necessary, document a focused numerical justification from the head operations, floating-point precision and paired inverse scaler; have it independently reviewed before adopting a different threshold. Limit this review to the affected operations. Do not reuse 001's label roundtrip allowance as a prediction-parity bound or choose a threshold from the observed failing discrepancy. An unexplained failure blocks qualification; retain it and investigate the cause rather than relaxing the test to pass. Any revised numerical contract needs a separately reviewed rationale and fresh qualification, with the original failed attempt preserved. Implementation and review checkpoints ===================================== 1. Provider boundary and input adaptation decision. Record the inspected API and sibling processor/input/result/failure owners with pinned sources and explain their applicability. Separate the Job text from admitted model input. Record how units, atoms and original indices reach the callable, and which chemistry decisions remain open if SMILES is offered. Do this before choosing a transport or result protocol. Acceptance: the next integration slice has a concrete boundary and explicit unresolved decisions, rather than an invented API model schema. 2. Real model loading and first reference execution. Connect verified dictionary/checkpoint/scaler snapshots to the real registered model and nmr_head. Use the released configuration and qualified CPUfloat32 choices; require complete state compatibility and dictionary order. No task, dataset, labels or loss enter production. Keep H/C lifetimes sequential. Exercise real reference predictions in the contained scientific lane; freeze numerical policy before inspecting deltas. Apply the adopted ångström input contract; reference-fixture tests retain their explicit test-only controls. Acceptance: released state constructs through the production loading path and computes finite, correctly indexed reference outputs; input rejection precedes checkpoint/scaler deserialization. A load-only success does not qualify prediction or external input. 3. Scientific admission, parity, coverage and resources. Retain exact shared-tensor parity, the exact 64-position overlap population, independent complete H/C coverage, ordinary reuse, repeat/reordered calls and absent-head handling. Exercise 128 atoms / 136 padded positions with the real model and measure consumption against the existing container grants. Verify bounded same-buffer artifact reads, source growth, pathname replacement and same-inode mutation; size/digest must identify deserialized bytes. Acceptance: every scientific/input/artifact/resource gate passes under the declared policy. Unresolved units or unexplained numerical failures block qualification while earlier construction/fixture work can still proceed. 4. Actual contained callable boundary and errors. Exercise the composed callable through a maintained contained qualification entrypoint with its actual source/artifact mounts and grants. Reuse existing launchers; extend only for necessary inputs or evidence. Preserve container-only execution, image admission, offline inference and bounded resources. Verify engine-side termination for lifecycle ownership introduced by this work. Inject C failure after H success and prove no partial success is returned. Test unchanged nested causes, no partial result and no emitted private request sentinel at the authorized callable boundary. Preserve controlled admission errors and require whole-lane success before retaining public-fixture evidence. Provider recipient projection/redaction and its secondary reporting failures belong to 003; the boundary correction above records why. Acceptance: actual callable wiring, causal preservation and absence of unsolicited private output are proven, with public projection still deferred; a standalone prediction CLI/product file-publication protocol is deferred. 5. Qualification evidence, documentation and provider handoff. Run focused maintained tests and the final scientific qualification target. Independently audit the reference population, actual predictions, full-atom coverage, verified byte identities, containment/resource evidence and safe failure surfaces. Publish the compact reviewed packet described above. Document the callable, exact rerun command, limits and remaining provider work. Preserve historical 001 evidence; make coherent path-limited local commits. Acceptance: the provider can build on a qualified reusable scientific boundary, with its input adaptation and remaining integration decisions visible. Completing 002 does not claim an implemented or deployed provider. Completion is falsifiable: the maintained contained qualification lane executes the real composed callable with admitted qualified-unit atoms and coordinates, gets each supplied H/C atom's finite ppm prediction at its original index, and independently relates the results to the exact 001 population and model bytes. All active scientific, input, artifact, resource, containment and safe failure requirements above must pass. A separate prediction/run CLI and its result.json/complete.json protocol are deferred, not counted as completed. Final qualification record (2026-10-17) ======================================= `make prediction/qualify` is the maintained contained rerun command. It runs `make prediction/tensor-parity` and `make prediction/model-parity` sequentially in the pinned offline inference image. The final run passed 23 tensor tests and four real-callable tests. All 32 shared-tensor comparisons matched exactly by dtype, shape and byte digest. Four retained fixtures produced 137 finite indexed predictions (82 H, 55 C); the 64 historical reference positions matched exactly at ATOL=RTOL=0. The callable admitted the chosen `angstrom` unit for both heads, covered each eligible atom once, and produced equal repeated results in forward and reversed fixture order. Both real released heads also predicted all 128 synthetic atom positions through the model prediction path (136 padded positions); the composed-callable sample was 1,798,643,712 bytes and the full-lane peak was 1,859,960,832 bytes, both below the 4 GiB grant. Synthetic geometry qualifies the capacity boundary only, not chemical validity. The lane also passed actual H-then-C failure handling, artifact identity, and containment/resource checks documented in the tests above. The packet binds its exact 64-position comparison to `artifacts/reference-cpu-float32.json` (SHA-256 `8268206ad12003dea5ef57e1a7392de69325e110b54f2b91ee0f6314c4fef5ae`, 30,886 bytes), records eligible indices separately for H and C, and retains exact artifact/member identities, source hashes, runtime, outputs and both scoped resource readings. The run log is retained locally under `.work/qualification/002-final-20261017/`. This closes 002's stated reusable scientific boundary: the real composed callable accepts explicitly unit-qualified atom coordinates and returns predictions tied to original atom indices on the retained qualification population. It does not claim broad chemical accuracy, reproduce a published ensemble result, establish the original producer's coordinate units for every archive record, or supply a SMILES-to-geometry/atom-mapping policy. RDKit-scale evidence and the NIST CH3Cl/CH2Cl2 bond-distance test support the repository's explicit operational `angstrom` contract, while archive-wide producer provenance remains unknown. The released scaler emits scikit-learn's 1.5.2-to-1.8.0 version warning in the recorded image; this limitation is visible and the observed contained comparison passes. The data artifact's `NOASSERTION` license is why request coordinates are not redistributed in the packet; a rerun must acquire the exact public archive member and verify the recorded hashes. Provider/API integration and recipient- appropriate error projection remain slice 003, not an open 002 acceptance item. The final packet SHA-256 is `3054ace5fb17b824e114c65c25e24389eca7551e38a0aece7f347f5b98f5fa75`. Recheck it with `sha256sum artifacts/prediction-cpu-float32.json`. Future source, image, artifact, or numerical-policy changes require a fresh qualification run and a new packet. Historical ten-review challenge (2026-10-14) =========================================== This review concerned the earlier plan and does not review or qualify the 2026-10-17 priority correction. Ten independent read-only reviewers examined the same plan revision, 69b607ce8755d84d7c55dde96094bfb5318fd861. Four distinct findings were accepted: explicit BOS/EOS coordinate rows, the exact named-head invocation and output shape, repository-relative execution-source identities, and unambiguous per-element model lifetimes versus one-shot command composition. The head-call omission was independently identified twice. These requirements are integrated above; this review does not constitute implementation or scientific qualification. One finding concerned exception masking by cleanup in the existing archive extractor. It is outside this plan's change boundary: 002 consumes already verified extracted artifacts and does not call or modify extraction. It was not converted into an unrelated implementation task or treated as a defect fixed here. Follow-up reviews checked the integrated corrections, including an explicit C-failure-after-H-success proof, and found no further actionable plan issues. Only this plan changed; no adapter implementation or scientific execution was performed during the review.