Phase 8: convert -O vhdx --subformat fixed¶
Phase 8 of PLAN-bat-population.md. Read its
Situation ("What qemu-img actually emits", "What instar does today" and
"Large outputs and the host"), open question 3 (convert writes blocks in
place, no block_state_zero on convert), and the "Phases 8 and 9"
paragraph of "Sequencing rationale" first. Also read
the phase 6 plan's decisions 3
to 5, which define vhdx::layout, VhdxLayout::fixed_data_start /
fixed_file_length and vhdx::fill_fixed_bat, and
the phase 7 plan's decisions 1
and 5, which this phase mirrors for convert. This file assumes them.
Planning effort: high, as the master plan sets for phase 8 (the convert
writer's size and the guest/host boundary it crosses). The output's only
external check is a structural comparison against a live qemu-img
convert.
Decisions already taken by the operator¶
Every open question in the master plan was resolved with its recommendation adopted (Michael Still, 2026-10-21). The ones this phase carries out:
- Open question 3:
convert -O vhdx --subformat fixedwrites blocks in place -- every payload BAT entryFULLY_PRESENTatfixed_data_start + block_idx * block_size, all-zero blocks left as holes, file lengthfixed_data_start + virtual_size. This is qemu's-o subformat=fixed,block_state_zero=offlayout, not qemu's default convert-to-fixed layout (which appends new data after the preallocated region and leaves the restZERO). Convert exposes noblock_state_zero; the divergence from qemu's default is documented. - Open question 4: never materialise a fixed BAT. The guest streams
the BAT entries one buffer at a time with
vhdx::fill_fixed_bat, ascreatedoes, instead of the dynamic path's in-memorybat_buf. The output device's capacity comes from the layout.
None of these is revisited here.
Scope¶
In:
- The host CLI.
--subformat fixed(anddynamic) accepted for-O vhdx. The-o subformat=fixedpath throughapply_create_overridesis acreate-only parser and does not apply to convert; convert takes only--subformat(survey). - The config. A new
ConvertConfigflag bit carries the fixed signal to the guest. Convert has no subformat field in its config and needs none (decision 1). The struct layout does not change. - The host output file. For a fixed vhdx convert the host sizes and
opens the output at
vhdx::layout(...).fixed_file_length, sparse, before the guest runs, and mapsEFBIGto a by-name refusal, exactly as phase 7's create does (decision 2). - The guest.
convert_to_vhdxgains a fixed branch that streams the BAT through one buffer withvhdx::fill_fixed_batand writes each non-zero payload block at its fixed offset, leaving zero blocks as holes (decision 3). The dynamic path is unchanged byte for byte. - Tests. Structural comparison of instar's fixed convert output
against a live
qemu-img convert -O vhdx -o subformat=fixed,block_state_zero=offof the same input, throughtests/helpers/vhdx_structure.py;qemu-img compareidentical;instar checkclean; the 2 TiB case; the qemu-unreadable partial-last-block-past-first-chunk geometry (structure +instar checkonly). Mutation cases. - Documentation.
docs/convert.md,docs/quirks.md(the in-place vs qemu-default convert-to-fixed divergence),convert --help, andCHANGELOG.md.
Out:
- measure.
measure -O vhdx -o subformat=fixedis phase 9 and keeps its "not yet supported" refusal until then. Convert's output is measure's oracle, which is why measure follows convert. - Cross-version baselines. The fixed convert cases across every
profile are phase 10. This phase proves it against the host
qemu-img(one version); phase 1 found the structure version-independent. - Fuzzing. The differential fuzzer's convert picker learns the fixed subformat in phase 11.
docs/format-coverage.md. Its convert output-side rows and notes are phase 12's.block_state_zeroon convert. Future work (open question 3). The fixed convert layout is always the in-place one.convert -O vpcfixed output. instar converts to dynamic VHD only; Future work.- The dynamic convert writer's in-memory-BAT silent failure
(
src/operations/convert/src/main.rs:4462). Fixed convert avoids it by streaming, but the dynamic path keeps its in-memorybat_buf. The survey confirms whether the dynamic silent path is reachable from the CLI and records the answer (decision 6); fixing it is Future work.
What the survey found¶
All references are to origin/develop at d88c6e37 (phase 7 merged as
03269d7f, #858). qemu claims are cited to
instar-testdata/qemu-sources/7.0.0/block/vhdx.c or measured with the
host qemu-img 10.0.13. No instar binary was built for this survey;
step 8a's brief measures convert.bin.
Phase 6 and 7 left convert almost everything it needs¶
vhdx::layout(virtual_size, block_size, false)already returnsfixed_data_startandfixed_file_length(src/crates/vhdx/src/lib.rs:4134,:4141), andvhdx::fill_fixed_bat(&layout, state, first_entry, buf)(src/crates/vhdx/src/lib.rs:4374) fills a buffer of BAT entries from an index range. Both are in the vhdx crate, which convert already links (convert_to_vhdxcallsvhdx::layout,vhdx::build_bat_entry,vhdx::PAYLOAD_BLOCK_FULLY_PRESENT,vhdx::METADATA_ITEMS_MIN_OFFSET).VhdxSubformat,VhdxPlanandplan_vhdxlive in the create crate, which convert does not link; convert does not use them and computes its geometry directly fromvhdx::layout, as it does today.- The host's
compute_output_capacity(src/vmm/src/main.rs:13957) already sizes VHDX convert output fromvhdx::layout(vsize, block_size, false)(phase 5). For a dynamic convert it returnspayload_blocks * block_size + data_start. For a fixed convert the capacity isfixed_file_length; this is the only new branch the capacity needs (the master plan's "capacity estimate replaced by the shared layout" is already done for dynamic).
convert_to_vhdx builds its BAT in memory and packs payload¶
convert_to_vhdx (src/operations/convert/src/main.rs:4382):
- allocates
bat_bufafter the staging buffer and zero-fills it to allNOT_PRESENT(:4459-4470), refusing with "convert: VHDX BAT too large for scratch" and a successful completion if it does not fit (:4462-4466); - packs payload blocks sequentially from
payload_start(next_free_offset), writing only non-zero blocks, and records each as aFULLY_PRESENTBAT entry at the packed offset (block_file_offset = align_up(next_free_offset, MB_ALIGN),vhdx_payload_bat_index(block_idx, chunk_ratio),vhdx::build_bat_entry(PAYLOAD_BLOCK_FULLY_PRESENT, block_file_offset),:4756-4762); - writes
bat_bufout to the BAT region last, inoss-sized chunks ("Rewrite BAT with actual offsets", from:4900).
The fixed path replaces all three: the BAT entries are a pure function
of the index (FULLY_PRESENT at fixed_data_start + block_idx *
block_size), so they stream through one buffer with fill_fixed_bat
and need no bat_buf; payload blocks are written at their fixed offsets,
not packed; and the file length is fixed_file_length, not the end of
the last packed block.
The host refuses --subformat for anything but vmdk¶
--subformat is validated at src/vmm/src/main.rs:14080-14102: a
non-empty --subformat on non-vmdk output is refused with "--subformat
is only supported with VMDK (-O vmdk) output", and the vmdk arm admits
monolithicSparse, streamOptimized, monolithicFlat.
ConvertArgs.subformat (src/vmm/src/main.rs:7382-7386, struct at
:4978) has a value_parser of ["", "monolithicSparse",
"streamOptimized", "monolithicFlat"] -- it does not accept
dynamic or fixed, so convert --subformat fixed is refused by clap
before the host code runs. CreateArgs.subformat (:7478-7485) already
carries dynamic and fixed (phase 7). Phase 8 adds them to
ConvertArgs.subformat's parser and its doc comment, and opens the host
gate to vhdx.
Convert carries no subformat field to the guest¶
ConvertConfig (src/shared/src/lib.rs:2606) has no vhd_subformat or
vmdk_subformat field. The vmdk monolithicFlat subformat is handled
entirely on the host (is_vmdk_flat_output retargets the guest at raw
and the host writes the descriptor, :14439); monolithicSparse and
streamOptimized are distinguished by other means in the sparse writer.
So the fixed signal must travel as a flag bit. ConvertConfig's
flags use bits 0-5 (FLAG_SKIP_ZEROS .. FLAG_DD_WINDOW) and 31
(FLAG_VERBOSE), so bits 6-30 are free (:2697-2718). One bit,
FLAG_VHDX_FIXED = 1 << 6, is all this phase needs: convert's fixed
layout is always block_state_zero=off, so no second bit for the block
state (unlike create, which carries FLAG_VHDX_FULLY_PRESENT for its
block_state_zero=off variant).
The error paths keep a zero-byte output¶
Every convert_to_vhdx failure arm calls send_complete(..., false)
(:4463, :4475, and the layout-error arm at :4428), whose comment
notes that without a failed completion the host keeps an empty output
file. A failed instar convert already leaves a zero-byte file (master
plan Future work); the fixed path inherits that and does not change it.
The tests and docs that see this¶
TestConvertToVhdx(tests/test_convert.py:3426) holds convert's VHDX round-trip tests;tests/test_vhdx_structure.pyandtests/test_vhdx_fixed_fixtures.pyhold the structural comparator and the fixed-fixture builders. The new fixed-convert class goes besideTestConvertToVhdxand reads throughtests/helpers/vhdx_structure.py.docs/convert.md:85-103describes vhdx as dynamic-only output and lists--subformatas a vmdk option;:110-133cover differencing. The--subformathelp onConvertArgs(:7382-7386) names vmdk subformats only.docs/quirks.mdcarries the VHDX partial-last-block entry (phase 7 added instar's create to it); convert at the same sizes writes the same qemu-unreadable image, so the entry gains convert.- No test or fuzzer expects
convert -O vhdx --subformat fixedto be refused;scripts/differential-fuzz.pynever asks for it.
Master plan refinements made in this commit¶
- The "Phases 8 and 9" paragraph says convert's capacity estimate is
"replaced by the shared layout". The survey found phase 5 already did
that for the dynamic case; phase 8 adds only the fixed
fixed_file_lengthbranch. The Execution table and thedocs/plans/index.mdrow link this file and record phase 7's landing.
Decisions¶
-
The fixed signal is a
ConvertConfigflag bit, not a subformat field.ConvertConfig::FLAG_VHDX_FIXED: u32 = 1 << 6insrc/shared/src/lib.rs, with its host mirrorCONVERT_CONFIG_FLAG_VHDX_FIXEDbeside the others atsrc/vmm/src/main.rs:119-127. The host sets it only for-O vhdxwith--subformat fixed. Convert's fixed layout is alwaysblock_state_zero=off(open question 3), so one bit is enough and noblock_state_zeroflag is added.ConvertConfig's size and field offsets do not change, and the existing layout assertions must still pass unchanged. -
The host extends the fixed output to
fixed_file_lengthat open, and mapsEFBIGby name. This mirrors phase 7 decision 1, for the same three reasons (the filesystem failure is the host's to report; the guest cannot make the file the wrong length when the device capacity is the layout's file length; it is what qemu does). For a fixed vhdx convert the host computesvhdx::layout(out_vsize, block_size, false)and opens the output with capacitylayout.fixed_file_length, sparse (set_lenbefore the guest runs). AnEFBIGfrom that becomesconvert: a fixed VHDX of <virtual size> bytes needs a <file length>-byte file, which the filesystem holding '<filename>' cannot store, followed by the OS error, matching phase 7's create message in shape.compute_output_capacitygains a fixed argument (or a sibling that returnsfixed_file_length); for dynamic output it is unchanged.
This is the decision a reviewer is most likely to dispute, for the same reason it was in phase 7: the master plan's prose leaves file extension unspecified for convert, and a reader might expect the guest to extend the file as the dynamic packed writer effectively does by writing its last block. Host extension is chosen to keep the create and convert fixed paths identical in where the file length is decided, and because the guest's capacity check then catches a host/guest layout disagreement.
-
The guest streams the BAT and writes payload in place. When
FLAG_VHDX_FIXEDis set,convert_to_vhdx: -
computes
layout.fixed_data_startandfixed_file_lengthfrom the samevhdx::layoutcall it already makes; - checks the device capacity is at least
fixed_file_lengthbefore writing anything, failing with a failed completion if not; - streams the BAT with
vhdx::fill_fixed_bat(&layout, PAYLOAD_BLOCK_FULLY_PRESENT_state, first_entry, buf)one buffer at a time, writing each slot's sectors into the BAT region, exactly ascreate'semit_vhdx_fixed_batdoes -- nobat_buf, noALLOC_HEAP_BASEceiling check, so the fixed path cannot hit the dynamic path's "VHDX BAT too large for scratch" failure; - writes each non-zero payload block at
fixed_data_start + block_idx * block_size(not packed), leaving zero blocks as holes so the file stays sparse; - reports
fixed_file_lengthas the bytes-written / file-size-after the host prints.
The streaming lives in one #[inline(never)] function beside the
dynamic path, for the x86_64-unknown-none codegen reason the
create, commit and bitmap ops document
(src/operations/amend/src/main.rs:211). The dynamic path keeps its
bat_buf and packing, byte for byte unchanged.
-
Convert exposes no
block_state_zero, and the divergence is documented.instar convert -O vhdx --subformat fixedalways produces the in-place (block_state_zero=off, every entryFULLY_PRESENT) layout. qemu's defaultqemu-img convert -O vhdx -o subformat=fixedappends new data after the preallocated region and leaves the untouched entriesZERO(Situation: a write to aZERO-state block allocates at end of file). instar's layout equals qemu's-o subformat=fixed,block_state_zero=off, which is the oracle the tests use.docs/quirks.mdrecords that instar's convert matches qemu'sblock_state_zero=offconvert, not its default. -
The oracle is a live
qemu-img convert, compared structurally. A new classTestConvertVhdxFixedintests/test_convert.py, besideTestConvertToVhdx, for each input (a raw and a qcow2 source with a mix of zero and non-zero blocks): -
runs
instar convert -O vhdx --subformat fixedandqemu-img convert -O vhdx -o subformat=fixed,block_state_zero=offof the same source to separate outputs; - asserts
structural_diffthroughtests/helpers/vhdx_structure.pyis empty for every structural field (region table, File ParametersLeaveBlocksAllocated, block size, every BAT entry, file length), with no masking; - asserts
qemu-img comparereports the two identical, andinstar checkandqemu-img checkboth exit 0; -
asserts
--subformat fixedon a non-vhdx target, and a--subformatvalue the parser does not know, are refused. -
The dynamic silent-failure reachability is settled, not fixed. Step 8a determines whether the dynamic
bat_bufceiling (:4462) is reachable from the CLI (a large dynamic VHDX at a small--block-sizeof a source big enough to need a BAT past the scratch budget), by computing the smallest--block-size/ source size that trips it and checking the host does not refuse first. The finding is recorded in the master plan's Future work (and an issue filed if the operator's permission allows); fixed convert's streaming is not a fix for the dynamic path.
Steps¶
| Step | Effort | Model | Isolation | Brief for sub-agent |
|---|---|---|---|---|
| 8a | high | opus | none | Convert to a fixed VHDX. First read decisions 1 to 4 and 6, and the survey sections "Phase 6 and 7 left convert almost everything", "convert_to_vhdx builds its BAT in memory", "The host refuses --subformat", "Convert carries no subformat field" in docs/plans/PLAN-bat-population-phase-08-convert.md. Also read phase 6 plan decisions 3-5 (vhdx::layout, fixed_data_start, fixed_file_length, fill_fixed_bat) and phase 7 plan decisions 1 and 5 (host extension, emit_vhdx_fixed_bat), and src/operations/create/src/main.rs's emit_vhdx_fixed_bat as the model to mirror. Shared (src/shared/src/lib.rs): add ConvertConfig::FLAG_VHDX_FIXED = 1 << 6 (:2697-2718) with a doc comment; existing ConvertConfig layout assertions must still pass. Host (src/vmm/src/main.rs): add CONVERT_CONFIG_FLAG_VHDX_FIXED at :119-127. Add "dynamic" and "fixed" to ConvertArgs.subformat's value_parser and update its doc comment (:7382-7386). In the --subformat validation (:14080-14102), accept dynamic/fixed for -O vhdx (set a local fixed flag), keep the vmdk arm, and keep refusing --subformat on other targets and unknown values. Set FLAG_VHDX_FIXED when assembling the convert flags for a fixed vhdx. For a fixed vhdx output, size the output device from vhdx::layout(out_vsize, block_size, false).fixed_file_length and open it sparse with set_len to that length, mapping EFBIG to decision 2's message (raw_os_error() == Some(libc::EFBIG)); extend compute_output_capacity (:13957) for the fixed case. Guest (src/operations/convert/src/main.rs): in convert_to_vhdx (:4382), add an #[inline(never)] fixed branch per decision 3 -- stream the BAT with vhdx::fill_fixed_bat, no bat_buf, write non-zero blocks at fixed_data_start + block_idx*block_size, leave zeros as holes, report fixed_file_length. Dynamic path unchanged. Tests (tests/test_convert.py): TestConvertVhdxFixed per decision 5, comparing through helpers.vhdx_structure.structural_diff, never qemu-img info. Add decision 9-style mutants to tools/mutate-differencing.sh (fixed flag ignored so a dynamic image is written; BAT streamed one slot short; payload written packed instead of in place; host opening the fixed output with the dynamic capacity). Settle decision 6 and report. Build only with make instar; run make test-rust, make check-binary-sizes (report convert.bin size before and after -- it is the larger binary and gains the filler), make fuzz-build; run stestr for test_convert, test_vhdx_structure, test_vhdx_fixed_fixtures, test_check_formats, test_compare. Prove the dynamic convert output is unchanged with a cmp of instar convert -O vhdx of a fixed input against a build of d88c6e37. Commit subject: Convert to a fixed VHDX. |
| 8b | high | opus | none | Fixed VHDX convert at large and awkward sizes. First read decisions 2, 4 and 5 and phase 7 plan's TestCreateVhdxFixedLarge (decision 7) and docs/quirks.md's partial-last-block entry. Add TestConvertVhdxFixedLarge to tests/test_convert.py: a 2 TiB fixed convert at 1 MiB blocks (source a sparse qcow2 with a few non-zero blocks), structure equal to the host qemu-img convert -O vhdx -o subformat=fixed,block_state_zero=off, instar check and qemu-img check clean; the partial-last-block-past-first-chunk geometry (reuse tests/test_vhdx_fixed_fixtures.py's PARTIAL_PAST_FIRST_CHUNK build args), structure vs host qemu + instar check only (qemu-img check/info not used, for the reason phase 7's plan gives); and the 16 TiB filesystem-ceiling refusal behind a probe-based skip, plus an RLIMIT_FSIZE variant that exercises the EFBIG path on any filesystem (as phase 7's test_file_size_limit_is_refused_like_a_filesystem_ceiling does). Every image sparse; none allocates a data region. Check tools/ci/check-test-partition.sh placement. Add the 8b mutant (EFBIG mapping removed). Commit subject: Test fixed VHDX convert at large and awkward sizes. |
| 8c | medium | sonnet | none | Document fixed VHDX convert. First read decision 4 and the survey section "The tests and docs that see this". In docs/convert.md: update the vhdx target description to dynamic + fixed, add --subformat fixed to the vhdx examples and the per-target option list, and state that fixed convert writes blocks in place. In docs/quirks.md: extend the VHDX partial-last-block entry to say instar convert --subformat fixed writes qemu's block_state_zero=off layout at those sizes too (one qemu cannot reopen; instar check reads it), and add that instar's fixed convert matches qemu's block_state_zero=off convert, not qemu's default convert-to-fixed (which appends). Update the ConvertArgs.subformat help text (:7382-7386) to name vhdx. Add a CHANGELOG.md entry under [Unreleased] / ### Added. Do not touch docs/format-coverage.md (phase 12). Every qemu claim comes from the plan's survey or measurement; add no new ones. Run pre-commit run --all-files. Commit subject: Document fixed VHDX convert. |
The management session runs the steps in order. 8b's tests need 8a's binary, and 8c documents the messages 8a wrote. 8a is one step rather than split host/guest, because neither half is testable alone (the guest is unreachable without the host gate, and the host alone produces a dynamic image). It runs on opus at high effort: the arithmetic is phase 6's, already pinned and mutated, so what is new is the guest branch and the call-table wiring, which is opus's ground.
Every brief also carries the session's standing rules:
- Build only with
make instar(Docker); never install a Rust toolchain on the host. - Run
make test-rust, redirecting cargo's target dir to an owned directory if pre-commit has leftsrc/targetroot-owned. - Do not run
make test-rustat the same time as a mutation run. - Run the touched test modules with stestr from
tests/, withINSTAR_BINARY_PATHpointed at the worktree's build, and report "Ran: N". Replay any failure alone (--concurrency 1) before attributing it. - Run
pre-commit run --all-filesaftergit add -Non new files, andtools/ci/check-test-partition.sh. - Keep plan, phase, step and decision references out of code and docs.
- Keep a mutation script under the step's scratch directory, and run
each mutant under
timeout. - Every qemu behaviour a comment or doc states is cited to
instar-testdata/qemu-sources/7.0.0/by function name, or was measured. Say which. - Tests stay under ext4's 16 TiB file-size ceiling by staying sparse: no test allocates a data region.
- Leave the work uncommitted.
Execution notes¶
- Back brief, 2026-11-04 (AEDT). The operator kept decision 2 as
recommended: the host extends the fixed convert output to its
fixed_file_lengthat open, mirroring phase 7's create. They kept the planned models (opus/high for 8a and 8b, sonnet/medium for 8c) and allowed this session to push the branch and open the pull request once the definition of done passes. The steps run in order. - 8a. Implemented as planned. Two management-session corrections,
both in the step commit (
86ffcebd): - the brief carried a wrong line reference for
ConvertArgs.subformat(:7382-7386isMeasureArgs, notConvertArgs), so the agent addeddynamic/fixedto measure's--subformatvalue_parser, which is out of scope (measure is phase 9). Reverted.ConvertArgshas novalue_parser, so convert already accepts any--subformatstring and the hostmatchvalidates it; the convert path needed no parser change. Its stale "Only valid with -O vmdk" doc comment was corrected to name vhdx instead. - one over-long line in
docs/testing.mdwas rewrapped. - 8a verification (management session, built at
86ffcebd).make instarclean;make check-binary-sizesOK (convert.bin375,800 B, 47% of the 768 KiB cap, up from 370,960 B atd88c6e37);pre-commit run --all-filespasses;tools/mutate-differencing.sh --check-patternsreports 149 land, 0 drifted.TestConvertVhdxFixed(4 tests) passes, including the structural comparison against liveqemu-img convert -o subformat=fixed,block_state_zero=off,qemu-img compareidentical and bothchecks clean. The dynamic convert output is unchanged: the agent'scmpagainst ad88c6e37build was byte-identical, and the management corrections touch neitherconvert.binnor the dynamic convert host path, so it carries over. The agent's full-suite stestr run showed 5 failures that all passed on--concurrency 1replay (host-contention flakes in untouched vhd/vmdk/qcow2 paths), as earlier phases saw. - Decision 6 settled. The dynamic path's silent in-memory-BAT ceiling is reachable from the CLI; recorded in the master plan's Future work, not fixed.
- 8b (
69de3893).TestConvertVhdxFixedLargeand the convert-host EFBIG mutation case. One departure, the operator's call: the plan's 2 TiB case mirrored phase 7's create timing (0.74 s), but convert reads every source block, so a 2 TiB fixed convert is O(source size) and takes about fifteen minutes. The 2 TiB convert was verified once (its structure equalled qemu-img's and both tools checked it clean); the permanent gate test uses 256 GiB instead, which still streams a many-slot BAT and places a data block past 128 GiB and runs in 113 s (measured), keeping the heavy case out of the merge-queue gate as the merge-queue critical-path work did for VHD conversion tests. - 8b verification (management session, built at
86ffcebd). The 256 GiB case passes in 113 s; the partial-last-block, 16 TiB-ceiling (ran on ext4) and RLIMIT_FSIZE cases pass.pre-commit run --all-filespasses,tools/mutate-differencing.sh --check-patternsreports 150 land, 0 drifted, andtools/ci/check-test-partition.shis clean. The 2 TiB convert measured about fifteen minutes standalone.
Risks and mitigations¶
- A fixed convert image that is structurally right and still has the
wrong data. The structural comparator sees the BAT and regions, not
payload.
qemu-img compareagainst qemu's own fixed convert (decision 5) covers the data for the small cases;instar checkand aconvert -O rawround-trip cover the large ones. The management session spot-checks one round-trip to raw equals the source. - The dynamic convert path changes by accident. The
cmpagainst ad88c6e37build in 8a's brief, repeated by the management session on one size. - The host and guest disagree about the fixed layout. Both call
vhdx::layoutwith the sameout_vsizeandblock_size. The guest's capacity check (decision 3) catches a host that sized the device too small; the structural comparison catches a file of the wrong length. The management session confirms the host passes the guest the sameblock_sizeit sized the device with. convert.bingrows past the 768 KiB cap.make check-binary-sizesafter 8a; 8a reports the size before and after. The filler is already compiled in the vhdx crate convert links, so the addition is the one new guest function plus the branch.- The 16 TiB test skips everywhere CI runs. Acceptable (a
host-filesystem fact), but the
RLIMIT_FSIZEvariant exercises theEFBIGpath on any filesystem, and the skip reason names the probe. - Host contention looks like a regression. Replay any failure alone
(
--concurrency 1) before attributing it.
Definition of done¶
Each item is a command, run from the worktree root with the worktree's build, and its expected result.
"$INSTAR_BINARY_PATH" convert -O vhdx --subformat fixed src.qcow2 out.vhdxon a qcow2 with a mix of zero and non-zero blocks produces an image whosehelpers.vhdx_structuresummary has an emptystructural_diffagainstqemu-img convert -O vhdx -o subformat=fixed,block_state_zero=off src.qcow2 ref.vhdx, andqemu-img compare out.vhdx ref.vhdxreports them identical."$INSTAR_BINARY_PATH" check out.vhdxandqemu-img check out.vhdxboth exit 0."$INSTAR_BINARY_PATH" convert -O qcow2 --subformat fixed a bis refused (fixed is vhdx-only), and--subformat bogusis refused.git grep -n 'FLAG_VHDX_FIXED' -- srcprints at least three lines (shared constant, host mirror, guest read).make test-rust,make check-binary-sizesandmake fuzz-buildpass; the execution notes recordconvert.bin's size before and after.- The stestr run of
test_convert,test_vhdx_structure,test_vhdx_fixed_fixtures,test_check_formatsandtest_comparereports "Ran: N" with 0 failures; every skip is listed by reason, including whether the 16 TiB test ran. cmpofinstar convert -O vhdx out.vhdx(dynamic, no--subformat) of a fixed input from this build and from a build ofd88c6e37reports no difference.bash tools/mutate-differencing.shwith each new case name reports PASS (or the documented survivor outcome), and--check-patternspasses.tools/ci/check-test-partition.shexits 0.git diff origin/develop...HEAD -- src tests docs/*.md scripts tools CHANGELOG.md | grep -nE '^\+.*(PLAN-|[Pp]hase [0-9]|step 8[a-c])'prints nothing.git add -A && pre-commit run --all-filespasses.- The dynamic silent-BAT reachability finding (decision 6) is recorded in the master plan's Future work.
Back brief¶
Before executing any step, back brief the operator on your understanding of this phase and how the intended work aligns with it. Put decision 2 (the host extends the file at open, mirroring phase 7) to the operator before 8a starts, since it resolves a point the master plan leaves unspecified for convert. Confirm opus for 8a and 8b, and whether this session may push and open the pull request once the definition of done passes.