fix: refuse wrong-architecture warm-start checkpoints structurally; cap DataLoader workers (#574) - #575
Merged
Conversation
…ing with the model The #545 guard compared the provenance sidecar's model_name against the run's MODEL_NAME and refused on mismatch -- but accepted any UNLABELLED checkpoint with a warning, on the reasoning that every mirror written before provenance existed has no sidecar and refusing them would break warm-continue for existing sites for no safety gain. On 2026-09-11 that reasoning was tested. A 722 MB 1DivideAndConquer mirror left on a test host by a failed run (no sidecar) was auto-warm- started into an MST client. The guard warned and proceeded; the client loaded 450 foreign parameters with "missing keys" warnings, trained, and then failed as aggregator with "none of the 187 incoming parameters matched the local model's 450". E2, one round late -- and the two name sets were disjoint from the first byte. Nothing needed to be trained to know it. The guard now intersects the checkpoint's parameter names with the model's, whether or not a sidecar exists. An empty intersection is another architecture and is refused with WARM_START_MODEL_MISMATCH, naming both sides' first keys and the model the checkpoint's own train_conf records. A partial overlap is normal (the Lightning wrapper carries a few buffers the persistor's model lacks) and passes. Two contract changes fall out. A refusal now returns None from load_model instead of panicking and then loading anyway -- the original sidecar-mismatch path had that fall-through too. And a skipped check is logged as skipped, with the reason, rather than being silent: a guard that fails open quietly is the F10 pattern in a different coat. Keys are read with mmap=True where torch supports it, so listing the names of a multi-hundred-MB mirror does not pull it into memory. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…t can feed (#574) Sixteen workers on a 38-volume training set do nothing for throughput and maximise shared-memory churn at every epoch boundary: with batch size 1 and the default prefetch the whole epoch sits in /dev/shm at once, and the file_system sharing strategy's cleanup race then kills the pin-memory thread ("unable to open shared memory object", #574). Seen at Epoch 8 on the 1.8.0 MVP deploy test. The fault-tolerant controller retried and the client trained fine on the next attempt, but on a two-client run with min_clients=2 a retry is a whole round, and three of them cost the run. cap_loader_workers guarantees each worker at least four samples per epoch: 16 workers on 38 volumes becomes 9. On a real site the cap never binds -- the smallest hospital has 190 volumes. num_workers=0 (in-process loading) is left alone. The cap is logged when it applies. It lives in a module with no imports of its own, because env_config pulls in the dataset package and through it torch, and anything meant to be unit-tested without the training image cannot live there. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Sep 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two fixes found by running the 1.8.0 MVP deploy test on real kits. Both are product code, so they are separate from the harness work in #573.
1. The warm-start guard let an unlabelled wrong-architecture checkpoint load
The #545 guard refused a mirror whose provenance sidecar named a different model — but accepted any unlabelled mirror with a warning, reasoning that pre-provenance mirrors have no sidecar and refusing them would break warm-continue for no gain.
On 2026-09-11 that was tested. A 722 MB 1DivideAndConquer mirror left on dl2 by a failed run (no sidecar) was auto-warm-started into an MST client. The guard warned and proceeded:
E2, one round late. The two name sets were disjoint from the first byte; nothing needed to be trained to know it.
Now: the checkpoint's parameter names are intersected with the model's, sidecar or not. Empty intersection →
WARM_START_MODEL_MISMATCH, naming both sides' first keys and the model the checkpoint's owntrain_confrecords (E2's prescription). Partial overlap — the Lightning wrapper's few extra buffers — passes as before.Two contract changes fall out, both tightenings:
Nonefromload_modelinstead of panicking and then calling the parent load anyway. The original sidecar-mismatch path had that fall-through too; a test caught it.Keys are read with
mmap=Truewhere torch supports it (2.2.2 in the image does), so listing a multi-hundred-MB mirror's names does not pull it into memory.2. Cap DataLoader workers at what the data can feed (#574)
16 workers on 38 volumes maximises shm churn at every epoch boundary; the
file_systemsharing strategy's cleanup race then kills the pin-memory thread. The FT controller retried and the client was fine next attempt — but on a 2-client run withmin_clients=2a retry is a whole round.cap_loader_workersguarantees ≥4 samples per worker per epoch: 16 → 9 on 38 volumes. It never binds on a real site (VHIO, the smallest, has 190).num_workers=0is untouched. Logged when it applies. Lives in an import-free module so it is unit-testable without the training image.Tests
+5 guard tests (the 09-11 case, same-architecture-unlabelled still loads, a lying sidecar, no-model skip, string
train_conf), +6 cap tests. Full suite 545 passed, 8 skipped. None useimportorskip.🤖 Generated with Claude Code