Skip to content

V4.1 runtime image distribution by config digest (spark_v41_runtime_image_distribute) - #12353

Merged
gunbai-bot[bot] merged 2 commits into
mainfrom
session/merry-eagle-768
Sep 26, 2026
Merged

gunbai-bot[bot] merged 2 commits into
mainfrom
session/merry-eagle-768

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 26, 2026

Copy link
Copy Markdown
Contributor

What

A fleet-converge mode, spark_v41_runtime_image_distribute, moves the produced V4.1 runtime image from the host its production receipt names (srv8, run 36207135528) to the selected Group A Spark. The image is identified by its config digest sha256:ea39410ed01d396caad86339d877c1adb181a983baadffd22c722069109ea664, never by tag alone.

How: one transport, generalized rather than duplicated

The fleet has no registry. gunbc.spark.vllm_runtime_image_build distribute_vllm_image (save → scp through the executor → load) was the only image transport, but it worked only for GLM and named images by tag. It now takes a VllmImageDistributionSubject { tag, config } and a list of supplied targets:

  • Source readback. The source's docker image inspect .Id under the tag must equal the digest. A different image under the tag refuses ("the tag is not the identity").
  • Target pre-read, before any save.
    • Holds the digest → Noop. If every target is a Noop, nothing is saved at all.
    • No image under the tag → deliver.
    • A different image under the tag → refuse, because a load would move the canonical tag off that image.
    • Daemon could not be read → refuse. An unread daemon is never treated as empty.
  • Disk space stated before a byte moves. The requirement is the image Size (new extdeps.docker.cli docker_image_inspect_size_from_stdout), checked on:
    • the source's /tmp (1×);
    • the executor's /tmp (1×);
    • the target's /tmp and /var/lib/docker (2× each). Filesystem identity is not read, so this is an upper bound; it is exact when both are on one filesystem, which is what the Sparks have.
  • Delivery proof. After load, the target's inspect .Id must equal the digest, or it refuses with a type and location.
  • Nothing re-tagged. The tag travels inside the tar, and no image contents change.

The GLM entry now goes through with_spark_build_host, the same credential setup the build uses. Its digest still comes from the source's readback because no GLM production receipt has been recorded; this is stated in a comment as a declared frontier.

The V4.1 receipt row

v41_arm_a_produced_image records only what run 36207135528 established:

  • the observed source tree (head d2d649e6…, worktree diff 79ee4347…);
  • the config digest;
  • the host that holds it (srv8).

The tag is derived from the tree through the candidate recipe and the build's own key fold. A claim checks that it re-derives the printed gunbc-vllm-dsv41-gb10:d73e307009a5d716. Control: changing one hex digit of the worktree digest turns that claim red.

Admission

  • The source comes from the receipt, not from an input. target is the destination.
  • Both hosts are admitted with admit_host_held_by_subject(PairServingUnitOn { FabricGroupA }), the same check the build uses.
  • Both hosts get a claim_host_effect_live with a 2h term. The step timeout is derived from that term.
  • If the target is the source, it is claimed once and reads as the Noop.
  • The mutation domain is Group A's arm domain.
  • Host admission reads occupancy: a held Spark host running its pair worker refuses GPU/memory effects #12351 occupancy is not wired in. It admits a GPU/memory need, and a save or load has none. The need this effect does have is disk, which is checked above.

Evidence

  • Local gunbc run typecheck and regeneration of fleet-converge.yml from the model.
  • 10 claims green, run locally: Noop, needs, foreign-image refusal, unread refusal, inspect Id+Size, disk short/at/unobserved, empty or partial delivery is not success, receipt tag derivation, Size reader, digest-named tar.
  • The real path is exercised by the wet dispatch after merge (srv8 → srv5); its receipt will be posted here.

🤖 Generated with Claude Code

Brian Searls and others added 2 commits September 26, 2026 15:33
…mage_distribute

The fleet's one image transport (save, scp through the executor, load) was GLM-only and named
by tag. It now takes a VllmImageDistributionSubject { tag, config digest } and supplied targets:
the source must hold the digest under the tag before a byte moves; a target already holding it
is a Noop (settled before any save); a different image under the tag refuses rather than having
the tag moved; an unread daemon refuses; the image Size is stated against every filesystem a
copy lands on (source /tmp, executor /tmp, target /tmp and data root) before the save; and the
target's inspect Id must read back as the digest.

The V4.1 entry names the arm A image by its production receipt (run 36207135528, srv8,
sha256:ea39410e...), deriving the canonical tag from the observed source tree through the build's
own key fold. Both hosts are admitted by admit_host_held_by_subject (Group A) and claimed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…docker info, not defaulted (review 71552)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 26, 2026

Copy link
Copy Markdown
Contributor Author

Wet control passed. fleet-converge run 36252411337 dispatched spark_v41_runtime_image_distribute target=srv5 from this branch at 5f264af. It delivered gunbc-vllm-dsv41-gb10:d73e307009a5d716 from srv8 to srv5. The inspect digest on srv5 read back as sha256:ea39410ed01d396caad86339d877c1adb181a983baadffd22c722069109ea664, with already_present=false.

Review 71552: both findings fixed in the new head.

  1. Untyped byte sizes. VllmImageHeldAs.size is now ByteSize, converted from the extdeps Int at the reader. Every requirement and free-space comparison is ByteSize. The single-copy and double-copy requirements go through one vllm_image_copies(size, copies) helper instead of bare size + size.
  2. Defaulted data root. Removed docker_default_data_root. The target's data root is now read from the target daemon itself: new extdeps.docker.cli docker_info_command plus docker_info_root_dir_from_stdout, which parses the untemplated Docker Root Dir: line. It refuses when there is no such line, when there are two, or when the path is not absolute. The disk-share check runs against that path. The new claim docker_info_names_the_data_root_or_is_unreadable covers a relocated root (/data/docker), the absent case and the duplicated case. docker_info_command is added to argv_command's permitted-caller roster.

Checked locally on the new head: typecheck, the fleet-converge YAML regenerates with no change, and 11 claims pass.

Note: the wet run above executed the previous head. On the new head a repeat run would be a Noop on srv5, which already holds the digest.

— sent from merry-eagle-768

@gunbai-bot
gunbai-bot Bot added this pull request to the merge queue Sep 26, 2026
Merged via the queue into main with commit bdc4876 Sep 26, 2026
5 checks passed
@gunbai-bot
gunbai-bot Bot deleted the session/merry-eagle-768 branch September 26, 2026 22:36
@briansrls
briansrls restored the session/merry-eagle-768 branch September 26, 2026 22:40
gunbai-bot Bot pushed a commit that referenced this pull request Sep 27, 2026
…nly the reads they rebind

The unrelated-diff control (#12353) reached 13147 witnesses, the same order as
#12361's replay. Two defects, both measured on srv1:
- the flat bare channel admitted dotted field accesses (cfg.root, whose empty
  candidate set says nothing about top-level root) and ambiguous bare names;
  the global-bare lookup resolves only a BARE name to a UNIQUE declarer, so
  the execution walk now admits exactly that (fan-out ran through test-local
  helpers named root/subject/observed/standing).
- an import-region edit seeded every declaration in its file (684 seeds from
  an 11-file diff); import_rebound_declarations now seeds only the reads whose
  import binding changed between the two indexes.
Receipt gains the binding kind and one BodyReachSeed line per seed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot pushed a commit that referenced this pull request Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants