Global milestones — updated only on phase transitions and major events.
Per-session details are in progress/.
- Phase: Phase 4+ complete; Track C1 complete; Track C2 complete;
proven-bounds campaign complete (0 runtime
]!acrossZip/Native/andZip/); Track E (security audit) in progress — P0 + P1 + P2 closed, all of P3 closed (P3.1 / P3.2 / P3.4 in prior batches; P3.3 fuzz harness landed as #1602 in this batch), P4 closed, P5.1 proof-friendly helpers landed as #1608 in this batch (P5.2 filed mid-batch as #1628; P5.3 tracked as #1614 and in flight as PR #1626 at batch close).SECURITY_INVENTORY.mdRecommended policy block: Rec. 1 / 3 / 5 executed in this batch (#1623 / #1618 / #1617); Rec. 2 structurally set up by #1610 (parameter added; default still0, issue #1625 tracks the flip); Rec. 4 tracked as #1621 - Toolchain: leanprover/lean4:v4.29.1
- Sorries: 0
- Sessions: ~660 completed (Feb 19 – Apr 22)
- Source files in-repo:
Zip/Spec/42,Zip/Native/7,Zip/(FFI/archive/tar/gzip/basic/checksum) 6,ZipTest/24 - Merged PRs: ~653 (approximate; authoritative count via
gh pr list) - Spec lines: 20,606 across 42 spec files (DEFLATE-only, post-split)
- Bare simp: 0 standalone bare
simpremaining across all spec files - Bare simp_all: 0 across all spec files (campaign complete)
- Zstd: Moved to https://github.com/kim-em/lean-zstd (#1487, 2026-03-27)
- Shared utilities: Moved to https://github.com/kim-em/lean-zip-common
(#1487, 2026-03-27); pulled in via
require zipCommon from git - Proven-bounds: 0 runtime
]!acrossZip/Native/andZip/; only 2 comment-only occurrences remain inZip/Native/Deflate.lean - Track E audit checklist:
plans/track-e-current-audit-checklist.mdis the source of truth for "what is done vs. what remains"; the human-readable trust-boundary catalogue isSECURITY_INVENTORY.md - Track E Recommended policy block: fully Executed (items 1–6, post-#1710 on 2026-04-22); Missing work reads "Residual gaps: none currently open at this layer"
- Checksum characterizing-property ladders: Adler-32 closed end-to-end (last rung
_combinein #1698); CRC32 closed at the concrete-shape terminus_pair(#1701);_replicate*/_combinefor CRC32 require GF(2)[x] algebra (out of scope) - Anchor-refresh maintenance retired (post-#2345): ~176 prior
commits of line-anchor refresh work superseded; close-out wave
in PR #2364 closed 15 obsolete PRs and 15 obsolete issues.
inventory-reconciliationskill now refuses such work at the planner and worker layer. - Track E CD/EOCD + CD/LH boundary-check coverage (post-#1868): 6 / 9 archive-level EOCD consistency dimensions closed (
totalEntries, disk-number,numEntriesThisDisk, ZIP64/standard-EOCD override sentinel, per-entry CDdiskNumberStart, ZIP64 EOCD64 record-size); 7 / 8 per-entry CD/LH consistency dimensions closed (method, flags, version, compressedSize, uncompressedSize, crc32, lastModTime/Date — name-bytes remaining, in-flight PR #1725); ZIP64 extra-field layout-smuggling class closed at the CD/LH boundary (inner-0x0001dataSizeexactness #1785 + outer sub-field structural walk #1788 + duplicate-block rejection #1793); intra-CD stored-method (method=0) size-invariant closed (#1773); CD-parse per-entry field validations closed in the post-#1803 wave (versionNeededToExtract > 45upper bound #1807,localOffset + 30 ≤ cdOffsetarchive-layout micro-invariant #1813,internalFileAttributesreserved bits #1819, GPF bit-5 patched-data #1824, name NUL-byte #1831); CD-parse filename-validation trio completed in the post-#1843 wave (cd-nul-in-name#1831,cd-path-unsafe#1840,cd-empty-name#1848); CD-parse mathematical-invariant family second column closed (uncompSize == 0 → crc == 0#1857, sibling of stored-method size invariant #1773) and third column closed (compSize == 0 → uncompSize == 0#1886, method-agnostic — closes the method=8 gap relative to #1773 / #1857); archive-level EOCD64versionMadeByspec-version upper bound closed (#1826) and companionversionNeededToExtract ≤ 63upper bound closed (#1852), together with the earlierversionNeededToExtract ≥ 45lower bound (issue #1758 / in-flight PR #1764) closing the EOCD64 version-field two-sided-bound dimensions (per-entry CDversionMadeBy > 63remains in-flight PR #1820); ZIP64-trailer archive-layout invariants trio closed (per-entrylocalOffset + 30 ≤ cdOffset#1813, ZIP64 recordeocd64Offset + 56 ≤ locatorPos#1856, archivecdOffset + cdSize ≤ eocdPosin-flight PR #1809); cross-format NUL-byte closure in Tar (UStar basename/linkname/prefix#1880 + GNU long-name / long-link #1865 + PAX record key/value #1866) plus ZIP CD-layer (#1831) spans all four user-supplied string-field layers, every one now carrying an explicit code guard (UStar layer was previously structurally NUL-safe by construction; #1880 added an explicit code guard so the closure is uniform); per-slot ZIP64-override family (opened post-#1718 by #1745 with thecdOffsetslot) closed at 5 of 6 slots in the post-#1904 wave (cdSize#1905,totalEntries#1908,diskWhereCDStarts#1911,numEntriesThisDisk#1922; onlynumberOfThisDiskoutstanding via in-flight PR #1909); local-offset → local-header validation chain (per-entry CD-parse) closed as a complete three-level trio in the post-#1904 wave (level-1cd-entry-localoffset-past-cdstart#1813, level-2cd-entry-past-cdend#1921, level-3cd-bad-lh-signature#1903); PAX-record decode surface closed at both parser-differential dimensions (NUL-byte rejection #1866 + duplicate-key rejection #1899); 47 fixtures intestdata/zip/malformed/(was 41 at #1894, 39 at #1869, 34 at #1843, 28 at #1803, 22 at #1770, 12 at #1721); 18 fixtures intestdata/tar/malformed/(was 17 at #1894, 16 at #1869, 14 at #1843)
Native CRC32 and Adler32 with equivalence proofs against FFI implementations.
Proved in 2 sessions. Key technique: bv_decide for bitvector reasoning.
Pure Lean DEFLATE inflate implementation (RFC 1951) with gzip/zlib wrappers. Conformance tests pass against FFI zlib. Integrated as alternative backend for ZIP and tar.gz extraction. Completed in 4 sessions.
Correctness proofs for the DEFLATE decompressor, completed over 25 sessions (Feb 19–23). Zero sorries. The proof chain covers:
- BitReader ↔ bytesToBits correspondence (BitstreamCorrect)
- Canonical Huffman tree ↔ spec table correspondence (HuffmanCorrect)
- Block-level decode correctness for all 3 block types (DecodeCorrect, DecodeComplete)
- Dynamic Huffman tree decode correctness (DynamicTreesCorrect)
- Stream-level inflate theorem (InflateCorrect)
fromLengthssuccess impliesValidLengths(DynamicTreesCorrect)
Characterizing properties (mathematical content independent of implementation):
- Huffman codes are prefix-free and canonical (Huffman.Spec)
- Kraft inequality bounds symbol counts (HuffmanKraft)
- Prefix-free codes decode deterministically (
decode_prefix_free) - Bitstream operations correspond to LSB-first bit lists
- LZ77 overlap semantics:
copyLoop_eq_ofFn - RFC 1951 tables verified by
decide
Algorithmic correspondence (native implementation agrees with spec decoder):
inflate_correct: native inflateRaw → spec decode succeeds with same resultinflate_complete: spec decode succeeds → native inflateRaw succeeds with same result- Both directions proved for stored, fixed Huffman, and dynamic Huffman blocks
Native compression + roundtrip verification. ~80 sessions. Zero sorries for the core DEFLATE roundtrip.
Compressor infrastructure:
- Native Level 0 (stored), Level 1 (fixed Huffman), and Level 5 (dynamic Huffman) compressors, all conformance tests passing
- Native gzip/zlib compression wrappers (GzipEncode, ZlibEncode)
- BitWriter with formal correctness proofs (writeBits, writeHuffCode)
LZ77 layer (BB1):
- Spec-level LZ77 greedy matcher with fundamental roundtrip theorem
(
resolveLZ77_matchLZ77) - Spec-level lazy LZ77 matcher with Level 2 roundtrip
(
deflateLevel2_spec_roundtrip) - Native LZ77 correctness:
lz77Greedy_valid,lz77Greedy_encodable,lz77Greedy_size_le - Native lazy LZ77 correctness:
lz77Lazy_valid,lz77Lazy_encodable,lz77Lazy_resolvable
Huffman layer (BB2):
- Huffman symbol encoding functions with
encodeSymbols_decodeSymbols - Canonical codes correctness:
canonicalCodes_correct_pos - Huffman code length computation with Kraft equality for binary trees
Bitstream layer (BB3):
- BitWriter correctness (BitstreamWriteCorrect)
readBits_complete(BitReader completeness, spec → native direction)readUInt16LE_complete,readBytes_complete(BitstreamComplete)
Block framing layer (BB4):
- Stored-block encode/decode roundtrip (
encodeStored_decode) - Dynamic tree header roundtrip:
encodeDynamicTrees_decodeDynamicTables writeDynamicHeader_spec— BitWriter chain for dynamic header- Native
emitTokens↔ specencodeSymbolscorrespondence:emitTokens_spec,emitTokensWithCodes_spec
Per-level roundtrips:
inflate_deflateStoredPure— Level 0 (stored blocks)inflate_deflateFixed— Level 1 (fixed Huffman)inflate_deflateLazy— Levels 2–4 (lazy LZ77)inflate_deflateDynamic— Level 5+ (dynamic Huffman)
Capstone theorem (DeflateRoundtripProduction.lean):
theorem inflate_deflateRaw (data : ByteArray) (level : UInt8)
(hsize : data.size < 1024 * 1024 * 1024) :
Inflate.inflate (deflateRaw data level) = .ok dataCovers all compression levels (stored, fixed, lazy, dynamic). The 1 GiB
size bound is now the native inflate's default maxOutputSize (a runtime
zip-bomb guard), not a fuel/termination limitation — all recursive functions
use well-founded recursion (Track C2 complete).
Proof quality reviews (80+ sessions): systematic code review across
spec files, reducing proof size, extracting reusable lemmas to ZipForStd,
splitting large files for maintainability, and converting bare simp to
simp only. Reviews to date: Deflate (#369), DecodeComplete (#374, #442, #517),
BitReaderInvariant (#382), HuffmanCorrect + HuffmanCorrectLoop (#385),
InflateCorrect (#387), InflateRawSuffix (#391, #484), DeflateFixedCorrect (#404),
LZ77 (#408), InflateLoopBounds (#413), DeflateDynamicFreqs (#414),
DynamicTreesComplete (#440), DynamicTreesCorrect (#441),
DeflateEncode (#448, #457), BitstreamCorrect (#459),
HuffmanEncode (#467), EmitTokensCorrect (#470), DeflateEncodeProps (#485),
BitWriterCorrect (#488, #505), DeflateStoredCorrect (#498),
BitstreamWriteCorrect (#500), DeflateDynamicCorrect (#507),
InflateComplete (#513), DeflateDynamicEmit (#514),
HuffmanTheorems (#518), BinaryCorrect + LZ77Lazy + DeflateRoundtrip (#528),
DeflateSuffix (#531), ZipForStd/ (#539), Adler32 + DeflateDynamicCorrect +
DeflateStoredCorrect (#543), 5 small-count files (#545),
GzipCorrect + HuffmanKraft + ZlibCorrect (#547).
Bare simp status: 0 standalone bare simps remaining across all 44 spec files (100% clean). The campaign reduced standalone bare simps from ~129 to 0 (100% reduction) over ~30 review PRs. ZipForStd/ and Native/ are also fully clean. Campaign completed Mar 4, 2026.
Extends the core DEFLATE roundtrip to full gzip (RFC 1952) and zlib (RFC 1950) framing, proving that the encode/decode wrappers are inverses.
Capstone theorems (zero sorries):
-- GzipCorrect.lean
theorem gzip_decompressSingle_compress (data : ByteArray) (level : UInt8)
(hsize : data.size < 1024 * 1024 * 1024) :
GzipDecode.decompressSingle (GzipEncode.compress data level) = .ok data
-- ZlibCorrect.lean
theorem zlib_decompressSingle_compress (data : ByteArray) (level : UInt8)
(hsize : data.size < 1024 * 1024 * 1024) :
ZlibDecode.decompressSingle (ZlibEncode.compress data level) = .ok dataWhat's proved:
- Pure proof-friendly decoders:
GzipDecode.decompressSingle,ZlibDecode.decompressSingle(no loops/mut) - Format lemmas: gzip header bytes, zlib CMF/FLG/CINFO/FDICT fields
compress_size(total output size = header + deflated + trailer)- Binary LE/BE read/write roundtrip proofs (BinaryCorrect.lean)
inflateRaw_complete: if spec decode succeeds at arbitrary offset, native inflateRaw succeeds with same result- Suffix invariance:
decode_go_suffix(spec decode ignores trailing bits) - BitReader invariant infrastructure: 14
_invlemmas trackingdata,hpos, andpos ≤ data.sizethrough all BitReader operations (all proved — split across BitReaderInvariant.lean + InflateLoopBounds.lean) inflateLoop_endPos_le: endPos ≤ data.size after inflate loopinflateRaw_endPos_eq: exact endPos calculation for trailer parsing- Encoder padding decomposition (
deflateRaw_pad) andremaining.length < 8viadecode.goR(decode with remaining) variant - Suffix invariance chain:
decodeCLSymbols_append,decodeDynamicTrees_append,decodeHuffman_go_append - CRC32 and ISIZE trailer byte matching (gzip)
- Adler32 trailer byte matching (zlib)
Architecture: The original monolithic GzipCorrect.lean (1949 lines) was
split into 4 focused modules: BitReaderInvariant.lean (522 lines),
InflateLoopBounds.lean (614 lines), InflateRawSuffix.lean (501 lines),
and GzipCorrect.lean (286 lines).
Eliminated the hard-coded size bound from all roundtrip theorems. The
capstone theorem is now fully parametric in maxOutputSize:
theorem inflate_deflateRaw (data : ByteArray) (level : UInt8)
(maxOutputSize : Nat) (hsize : data.size < maxOutputSize) :
Zip.Native.Inflate.inflate (deflateRaw data level) maxOutputSize = .ok dataThis was achieved progressively:
- #305: raised fuel limits from 5MB to 500MB–1GiB
- #654: generalized stored block roundtrip to parametric
maxOutputSize - #656: generalized levels 1–4 and dynamic roundtrips, plus the capstone
The maxOutputSize parameter is now a caller-chosen zip-bomb guard, not
a proof limitation. Combined with Track C2 (fuel elimination), the DEFLATE
roundtrip theorem has no artificial restrictions.
Replaced all fuel-based recursion with well-founded recursion, eliminating
the data size bound as a proof artifact. The 1 GiB bound in the capstone
theorem is now solely the native inflate's maxOutputSize zip-bomb guard.
All 6 fuel-using functions converted to WF:
- Spec
decodeCLSymbols→ WF (#328):termination_by totalCodes - acc.length - Native
decodeCLSymbols→ WF (#332): same termination measure - Spec
decodeSymbols→ WF (#337):termination_byon remaining bits - Native
decodeHuffman.go→ WF (#341):termination_by dataSize * 8 - br.bitPos - Spec
decode.go/decode.goR→ WF (#344): required deleting DeflateFuelIndep.lean (fuel independence became moot once spec functions are WF) and updating 11 downstream proof files - Native
inflateLoop→ WF (#397):termination_by dataSize * 8 - br.bitPos
Key proofs repaired during and after WF conversions:
inflateLoop_correct(#358): native→spec block loop correspondencedecodeDynamicTrees_complete(#361): composed from sub-completeness theoremsinflateLoop_complete(#373): spec→native block loop, strong induction- Proof repairs across 6 PRs (#345, #350, #351, #356, #357, #362)
Sorry count trajectory: 0 → ~15 (during conversions) → 0 (all repaired).
New skill: lean-wf-recursion (#349) capturing WF function unfolding rules,
f.induct functional induction patterns, and fuel-to-WF migration checklist.
Benchmark infrastructure comparing native Lean compression/decompression vs FFI (zlib) across all levels (0–9) with various data patterns (constant, cyclic, random, text) at sizes 1KB–1MB.
Large-input fixes:
lz77GreedyIter— tail-recursive LZ77 greedy matcher with proved equivalence (lz77GreedyIter_eq_lz77Greedy), replacing the stack- overflowinglz77Greedy_mainLoopfor level 5+ (#372)lz77LazyIter— tail-recursive LZ77 lazy matcher with proved equivalence (lz77LazyIter_eq_lz77Lazy), enabling levels 2–4 at large input sizes (#380)- All compression levels now work at 256KB+ inputs
Benchmark infrastructure (NativeScale.lean, NativeCompressBench.lean):
- Decompression benchmarks added (#379): inflate, gzip decompress, zlib decompress — native vs FFI across all patterns and sizes
- Text corpus pattern added (#390): realistic text data alongside constant, cyclic, and random patterns
- Compression size cap removed (#390): all levels run at all sizes (was previously split into small/large tiers with restricted levels)
- All-level compression ratios (#399): native vs FFI output sizes for levels 0–9 across all 4 data patterns
- MB/s throughput calculations (#399) added to all benchmark sections
Native Lean Zstd implementation, following the same methodology as DEFLATE (B-track). The core decompression infrastructure is now largely in place; the remaining work is wiring these components together for compressed block decompression and adding multi-frame support.
Frame and block layer:
- Frame header parser (#398): magic number, frame header descriptor bit fields,
optional window descriptor, dictionary ID, frame content size — per
RFC 8878 §3.1.1.
ZstdFrameHeaderstructure withparseFrameHeader. - Block header parsing and raw/RLE decompression (#405): 3-byte block
headers (Last_Block, Block_Type, Block_Size), verbatim copy for raw blocks,
single-byte repeat for RLE blocks,
decompressFrameloop over blocks. - XXH64 checksum verification (#432): pure Lean xxHash64 implementation wired into frame decompressor for content checksum validation.
- Compressed block header parsing (#439):
parseLiteralsSection(4 literal block types × 3 size formats) andparseSequencesHeader(sequence count- compression mode bytes for literals/offsets/match lengths).
FSE (Finite State Entropy) infrastructure (Zip/Native/Fse.lean, ~359 lines):
- Distribution decoder (#429):
decodeFseDistributionreads compact probability distributions from the bitstream, producing normalized symbol counts. - Table construction (#429):
buildFseTablebuilds the FSE decoding table (1 << accuracyLog cells) using RFC 8878 §4.1.1 position-stepping algorithm. - Backward bitstream reader (#452):
BackwardBitReaderfor Zstd's MSB-first backward bitstream format (RFC 8878 §4.1), withinit,readBits,isFinished. - Symbol decoding (#452):
decodeFseSymbolsdecodes sequences of symbols from FSE state machine lookups. decodeFseSymbolsAll(#479): variant that loops untilBackwardBitReader.isFinished(for variable-length outputs like Huffman weight sequences).
Sequence execution (ZstdFrame.lean):
- Sequence execution engine (#447):
ZstdSequencestructure,resolveOffsetwith repeat offset codes 1–3 (RFC 8878 §3.1.2.3),copyMatchwith overlap semantics,executeSequencesfor reconstructing output from literal/match interleaving.
Huffman tree descriptors (#458, #479):
- Direct representation parsing (#458):
parseHuffmanWeightsDirectreads raw 4-bit weight nibbles,weightsToMaxBitscomputes max bit depth from weight distribution,buildZstdHuffmanTableconstructs lookup table from weights. - FSE-compressed representation (#479):
parseHuffmanWeightsFsedecodes weight sequences via FSE tables, the common case in real-world Zstd files at default compression levels. parseHuffmanTreeDescriptordispatches between direct and FSE-compressed weight representations.
Sequence infrastructure (#466):
- Extra bits tables (
litLenExtraBits,matchLenExtraBits,litLenBaseValues,matchLenBaseValues) implementing RFC 8878 §3.1.1.3.2.1 tables. - Compression mode parsing:
parseCompressionModedecodes 2-bit mode fields (predefined, RLE, FSE-compressed, repeat) for sequence header entries.
Multi-frame support (#490):
decompressMultiFrame: loops over concatenated frames (RFC 8878 §3.1), accumulating decompressed output.decompressSkippableFrame: identifies and skips skippable frames (magic 0x184D2A50–0x184D2A5F).decompressZstd: top-level entry point handling both standard and skippable frames.
Tests: 25+ tests in ZipTest/ZstdNative.lean covering header parsing,
RLE/raw blocks, FSE distribution decoding, table construction, backward
bitstream reading, sequence execution, Huffman weight parsing, compression
mode parsing, multi-frame support, and checksum verification.
Huffman-compressed literals (#508, merged):
decodeHuffmanSymbol,decodeHuffmanStream,decodeFourHuffmanStreamsfor flat table lookup withBackwardBitReader- Handles litType 2 (Huffman-compressed) in
parseLiteralsSectionper RFC 8878 §3.1.1.3.1
FSE table resolution (#548, merged):
buildRleFseTable: constructs single-symbol FSE table for RLE moderesolveSingleFseTable: dispatches on compression mode (predefined, RLE, FSE-compressed, repeat) to produce an FSE decoding tableresolveSequenceFseTables: resolves all three sequence tables (literal length, offset, match length) from compression modes- This was the critical connector between compression mode parsing and sequence decoding — completing issue #473
Treeless literals (#566, merged):
- litType 3 (treeless) support in
parseLiteralsSection: reuses the Huffman tree from a previous block viaprevHuffTreeparameter - Huffman tree state threaded through the
decompressBlocksloop
Cross-block state (#572, merged):
executeSequencesnow acceptswindowPrefix(prior blocks' output) andoffsetHistory(repeat offset state across blocks)- Match offsets can reach into previous blocks' output
- Return type extended to propagate offset history for next block
Sequences header consolidation (#556, merged):
- Unified
parseSequencesHeadervariants, removing code duplication - Wired compression modes directly into
decompressBlocks
Dictionary frame rejection (#565, merged):
decompressFramenow returns an error ifdictionaryId ≠ 0- 3 validation edge case tests (dictionary rejection, checksum corruption, truncated frame)
Malformed data tests (#567, merged):
- 12 new test cases (158 lines) covering: reserved frame header bits, content size mismatch, max window size, truncated headers, reserved block types, raw block underflow, RLE boundaries, Huffman weight validation, skippable frame overflow, trailing data detection
Zstd specification infrastructure:
Zip/Spec/Fse.lean(#584, merged, 133 lines): FSE distribution validity predicates (ValidAccuracyLog,ValidDistribution,ValidFseTable) withDecidableinstances. Predefined distribution validity proved bydecide. Sorry count reduced from 3 to 1 by provingdecodeFseDistribution_accuracyLog_geand_le(#603).decodeFseDistribution_sum_correctproved via loop invariant (#619).buildFseTable_accuracyLog_eqproved (#646); 1 sorry remains (buildFseTable_cells_size, requiresforInloop invariant).Zip/Spec/XxHash.lean(#618, merged, ~180 lines): XXH64 specification predicates — prime value validation, initial state, round function properties.xxHash64_emptyproved (#642). 4 sorry remaining (3 UInt64 test vectors too expensive for kernel evaluation, 1 docstring mention).Zip/Spec/ZstdHuffman.lean(#606, merged, ~170 lines): Huffman weight validity predicates per RFC 8878 §4.2.1 —ValidWeights,ValidMaxBits,ValidHuffmanTable.isPow2_iffandweightSum_pos_of_exists_nonzeroproved (#628).buildZstdHuffmanTable_maxBits_posproved via WF refactoring ofwhileloop tofindMaxBitsWF(#641). 2 sorry remaining (buildZstdHuffmanTable_tableSizefill loop invariant, one other).Zip/Spec/Zstd.lean(#587, merged): frame specification predicates.parseBlockHeader_blockSize_ltproved viabv_decide(#647) — corrected from false_lestatement to true< 2^21bound. 0 sorry remaining.Zip/Spec/ZstdSequence.lean(#632, merged): sequence validity predicates and block size validation.executeSequences_output_lengthproved (#653) via refactoringexecuteSequencesfromforInto explicit recursion. 0 sorry remaining.
Code modularity:
- ZstdFrame.lean split (#599): monolithic 1059-line file split into
ZstdHuffman.lean(400 lines),ZstdSequence.lean(392 lines),ZstdFrame.lean(302 lines). Error prefixes normalized. This resolved the merge conflict bottleneck by reducing concurrent modification surface. - Block size and window size validation (#613):
decompressBlocksvalidates block size ≤ 128KB,executeSequencesvalidates match offsets against window size. 4 validation tests added.
Code quality reviews:
- ZstdFrame.lean audit (#583): 1059 lines reviewed. Splitting plan identified and executed via #599.
- Fse.lean + XxHash.lean audit (#579): 481 lines reviewed. No critical issues found.
- Stale PR triage (#611): closed 4 conflicted PRs (#561, #577, #602, #596) and 2 stale issues (#568, #558). Unblocked #540 and #552 for fresh work.
- Label cleanup (#612): removed stale
has-prlabels, reviewed and merged PR #606 (Huffman spec). - Final bare simp sweep (#607): completed with DeflateEncodeDynamicProps residual fixed.
- Zstd spec PR review (#624): reviewed #618 (XxHash) and #619 (FSE sum).
Noted one bare
simpin #619 and CLAUDE.md modification in #618.
Self-improvement:
- Merge conflict cascade analysis (#597): documented recovery-of-recovery
anti-pattern and prevention strategies in
agent-pr-recoveryskill. - Zstd spec infrastructure retrospective (#623): created
lean-zstd-spec-patternskill documenting file structure and naming conventions. Updatedlean-monad-proofswitheq_of_beqpattern andsplit vs by_casesdecision guide.
Compressed block integration (#651, milestone):
- Wired full compressed block pipeline end-to-end in
decompressBlocks:resolveSequenceFseTables→ backward bitstream →decodeSequences→executeSequences - Added
PrevFseTablesstruct for FSE Repeat mode across blocks - All three Zstd block types (raw, RLE, compressed) now decompress through a unified pipeline
- This was the critical integration step (issue #552) that had been blocked by merge conflict cascades for multiple summary periods
Zstd spec quality audit (#650):
- Eliminated 12 bare
simpoccurrences across ZstdHuffman.lean and ZstdSequence.lean spec files - Removed
xxHash64_deterministic(tautological — was justrfl) - Added missing docstrings to specification theorems
Test infrastructure expansion:
ZstdNative.leantest monolith split into 3 focused files (#630):ZstdFrameNative.lean,ZstdFseNative.lean,ZstdHuffmanNative.lean
WF refactoring (continued, #667, #671):
decompressBlocksWF(#667): replaced opaqueforInloop with well-founded recursion on remaining data, provedparseBlockHeader_pos_eqbuildZstdHuffmanTablefill loops (#671): extractedfillHuffmanTableInnerWFandfillHuffmanTableWFfrom opaqueforloops, provedfillHuffmanTableInnerWF_preserves_sizeandfillHuffmanTableWF_preserves_size
Spec proofs (14-PR batch, Mar 6):
- #664:
decompressFrame_contentSize_eqanddecompressFrame_checksum_valid— characterizing properties relating frame output to header metadata - #672:
copyBytes_getElem_ltandcopyMatch_getElem_lt— existing buffer content preserved during sequence execution - #677:
decompressRawBlock_content,decompressRawBlock_pos_eq,decompressRLEBlock_content,decompressRLEBlock_pos_eq— raw and RLE block content preservation and position advance - #681:
resolveOffset_repeat1_val,resolveOffset_repeat2_val,resolveOffset_repeat3_val— exact return values for repeat offset codes - #685:
litLenExtraBits_size,matchLenExtraBits_size,decodeLitLenValue_small,decodeMatchLenValue_small,decodeOffsetValue_positive— RFC table sizes and decoded value bounds
Conformance testing (#680, #686):
- End-to-end test matrix: 48 combinations of FFI compress → native decompress across 4 compression levels × 4 data patterns × 3 sizes
- Tests in
ZipTest/ZstdConformance.lean - 36/48 passing (12 text-pattern Huffman failures, known bug tracked in #703)
Spec proofs (12-PR batch, Mar 6):
This batch deepened the sequence execution spec toolkit in ZstdSequence.lean (now 47 theorems/lemmas, 740 lines) across three areas:
Sequence execution framework:
- #700:
executeSequences_empty(base case),executeSequences_loop_output_size_ge(output size monotonicity) - #714:
executeSequences_loop_cons(single-step unfolding),executeSequences_loop_cons_output_size(output size composition)
Content theorems and value bounds:
- #690:
copyBytes_getElem_geandcopyMatch_getElem_ge— content of newly written bytes during sequence execution - #704:
copyMatch_loop_getElem_geandcopyMatch_getElem_ge— general content theorem for copyMatch including overlapping regions (subsumes the non-overlapping case) - #705:
decodeMatchLenValue_ge_threeanddecodeOffsetValue_ge_two— RFC 8878 value decoding bounds viadecide_cbv
resolveOffset completeness:
- #687:
resolveOffset_shifted1_val,resolveOffset_shifted2_val,resolveOffset_shifted3_val— exact values and history rotation for shifted repeat offsets (litLen = 0 case) - #695:
resolveOffset_history_valid_largeandresolveOffset_history_valid_repeat— ValidOffsetHistory preservation - #710:
resolveOffset_positive_shifted12(shifted repeat codes 1–2 positivity),resolveOffset_positive_all(unified positivity covering all cases)
Infrastructure:
- #694, #699: stale PR cleanup (closed #686 duplicate, #689) + composability audit identifying 4 gaps in the executeSequences proof path
- #711: skills update — added WF helper functions, table correctness, exhaustive case analysis, loop invariant sections to lean-zstd-spec-pattern; added split/letFun chains, inline show proofs to lean-monad-proofs
Position advancement spec campaign (16-PR batch, Mar 6):
This batch focused on proving that Zstd parsing functions advance their position correctly — a prerequisite for composing position bounds into end-to-end frame position proofs.
Position specs proved:
- #723:
decompressBlocksWF_pos_gt(output monotonicity and position advancement) - #724:
parseSequencesHeader_pos_gt,parseSequencesHeader_pos_bounded(advances by 1–4 bytes),parseSequencesHeader_byte0_zero(zero-case characterization) - #730:
skipSkippableFrame_pos_eq(exact position formula),skipSkippableFrame_pos_gt - #738:
buildRleFseTablestructural specs,resolveSingleFseTable_predefined_pos - #739:
parseLiteralsSectionraw/RLE position advancement and huffTable specs - #740:
resolveSingleFseTable_rle_pos(RLE advances by 1),resolveSingleFseTable_repeat_pos(repeat mode position unchanged) - #753:
parseFrameHeader_pos_gt(strict advancement),parseFrameHeader_pos_ge(minimum bound)
Structural invariants and sorry reduction:
- #707:
weightsToMaxBits_validproved (corrected false bound from1 <<< (m - 1)to1 <<< m), removing 1 sorry from ZstdHuffman.lean - #718:
buildFseTable_size_eq(table size = 1 <<< accuracyLog),buildFseTable_accuracyLog_eq(accuracy log passthrough) - #733: BackwardBitReader base-case specs —
isFinishedcharacterization,readBitszero returns current state, error characterization - #748:
buildPredefinedFseTables_accuracyLogsproved;buildPredefinedFseTables_successblocked by opaquewhileloop (adds 1 sorry to Fse.lean)
WF recursion:
- #744:
decompressZstdrefactored from opaquewhileloop to well-founded recursion (decompressZstdWFwithtermination_by data.size - pos). Runtime guards ensure non-advancing frames throw errors. This was the last opaquewhileloop in the top-level Zstd pipeline.
Quality reviews:
- #725: ZstdSequence.lean — combined duplicate loop induction proofs into single pass
- #749: Fse.lean — replaced all 22 bare simp/simp_all with explicit
simp only - #754: ZstdSequence.lean — eliminated 7 remaining bare simp calls
Housekeeping:
- #729: closed stale PR #721, closed issue #701, unclaimed #703 for reassignment
Conformance testing:
- 48/48 test matrix passing (FFI compress → native decompress, 4 levels × 4 patterns × 3 sizes)
11-PR batch (Mar 6–7): sorry reduction, position specs, quality reviews:
Sorry reduction:
- #776:
buildPredefinedFseTables_successproved — removed the last Fse.lean sorry. Used structural success proof (each loop body returns .ok) rather than WF refactoring. AddedbuildFseTable_ok_of_validandforIn_pure_okhelpers. Sorry count: 4 → 3.
Position advancement specs:
- #760:
BitReader.readBitsexact bitPos advancement (readBits_bitPos_eq,readBits_pos_eq). Previously the last uncovered function in the position campaign. - #763:
decompressFrameposition advancement (decompressFrame_pos_gt) — composed fromparseFrameHeader_pos_gtanddecompressBlocksWF_pos_gt. - #772:
resolveSingleFseTableFSE-compressed mode position advancement (resolveSingleFseTable_fseCompressed_pos_gt). Composed fromdecodeFseDistribution_bitPos_geandbuildFseTablepassthrough. - #779:
parseLiteralsSectioncompressed path position specs — unifiedparseLiteralsSection_pos_gtcovering all literal types (0–3). - #781:
resolveSequenceFseTablesposition composition — composed threeresolveSingleFseTablecalls provingresolveSequenceFseTables_pos_ge.
Characterizing properties:
- #768:
windowSizeFromDescriptorpositivity (windowSizeFromDescriptor_pos) and minimum bound (windowSizeFromDescriptor_ge_1024). Proved via exhaustive evaluation over all 256 UInt8 inputs.
WF refactoring:
- #765:
buildFseTablewhile loop refactored to WF recursion (buildFseTableStepWF). ProvesbuildFseTableStepWF_pos_ltfor position advancement andbuildFseTableStepWF_cells_sizefor size preservation.
Quality reviews (bare simp_all elimination):
- #764: ZstdHuffman.lean — replaced 3 bare
simp_all, extracted duplicate monadic bind peeling intobuildZstdHuffmanTable_ok_elimhelper (-36 lines). - #778: BitstreamCorrect.lean — replaced 4 bare
simp_allwith targeted tactics (dsimp,eq_of_beq, explicitsplitcases). - #780: BitReaderInvariant.lean — replaced 3 bare
simp_allwith explicitsimp_all only [...]lemma lists.
11-PR batch (Mar 7): value bounds, top-level specs, quality reviews:
Track E position/value specs (5 PRs):
- #792:
readBits_value_lt_pow2— BackwardBitReader.readBits produces value < 2^n - #795:
buildFseTable_numBits_le— cells numBits ≤ accuracyLog (ValidFseTable condition c). Usedset!_preserves_forallhelper for dependent type reasoning. - #797: New
Zip/Spec/ZstdFrame.lean— first specs fordecompressZstdWF:decompressZstdWF_base,_single_standard_frame(base case and single-frame equivalence) - #803:
readBits_totalBitsRemaining_sub,_lt— exact bit consumption tracking for BackwardBitReader.readBits. Key discovery:↓reduceItedoesn't reduce concrete numeral comparisons. - #808:
decompressZstdWF_single_skippable_frame,_skip_then_standard— skippable frame composition specs for the top-level decompressor
Quality reviews (5 PRs):
- #787: Zstd.lean spec audit — deduplicated
parseFrameHeaderposition proofs, documented grind usage (-15 lines) - #796: Closed stale PR #789, compressed
decompressBlocksWFproof boilerplate (-37 lines) - #802: Zstd.lean quality pass — documented grind, converted bare simp
- #807: XxHash.lean quality pass — converted 4 bare simp to simp only
- #811: DeflateEncodeDynamic.lean quality pass — replaced 5 simp_all with targeted
tactics (termination proofs:
simp only+omega; Bool→Prop:beq_eq_false_iff_ne)
Skill updates (1 PR):
- #798: Updated lean-zstd-spec-pattern, lean-monad-proofs, lean-zstd-patterns with position-spec campaign patterns and BackwardBitReader proof techniques
BackwardBitReader lifecycle coverage now complete:
- init → totalBitsRemaining bound and startPos
- readBits → value bound (val < 2^n), exact bit consumption, bitPos advancement
- isFinished → characterization
Remaining:
- Prove remaining sorry stubs: 3 in XxHash (UInt64 test vectors too expensive for kernel evaluation — intractable without native_decide)
- Compose position specs into end-to-end frame position theorem
- Spec-level decoder with correctness proofs (algorithmic correspondence between native and spec decoder, following the DEFLATE B3 pattern)
- Compressor + roundtrip proof
12-PR batch (Mar 7): le_size campaign + characterizing properties + quality reviews:
This batch established position-within-bounds invariants (le_size theorems)
across 6 Zstd parsing functions and extended characterizing properties for
compressed headers and sequence parsing.
Track E le_size campaign (6 PRs):
- #825:
skipSkippableFrame_le_size,decompressRawBlock_le_size,decompressRLEBlock_le_size— frame-level position bounds - #835:
parseBlockHeader_le_size,parseFrameHeader_le_size— header parsers stay within data bounds - #834:
parseSequencesHeader_le_size,parseSequencesHeader_medium_encoding— sequence header position bound and medium encoding characterization
Characterizing properties (3 PRs):
- #833:
decodeHuffmanSymbolspecs — bit consumption monotonicity (decodeHuffmanSymbol_pos_mono) and bounds - #829:
parseCompressedLiteralsHeaderspecs —headerSize,fourStreams,regen_boundcharacterizing compressed literal header fields - #826:
parseSequencesHeader_numSeq_small— small encoding extraction (1-byte case: numSeq = byte0 when 0 < byte0 < 128)
Top-level decompressor specs (1 PR):
- #839:
decompressZstdWF_output_size_ge(output monotonicity via functional induction) anddecompressZstd_single_frame(API-level single-frame theorem)
Quality reviews (2 PRs):
- #832: DeflateSuffix.lean — converted 3 bare
simp_all - #822: Fse.lean — fixed bare simp, documented grind, combined rw calls
The le_size campaign now covers the "easy" functions (those with simple bounds
checks): skipSkippableFrame, decompressRawBlock, decompressRLEBlock,
parseBlockHeader, parseFrameHeader, parseSequencesHeader. Remaining
le_size gaps require deeper invariants: parseLiteralsSection (variable-length
compressed headers), BitReader operations, FSE distribution chain,
decompressBlocks loop, and frame-level composition.
Summary: The Zstd spec infrastructure now spans 6 files with 195 theorems/lemmas: ZstdSequence (57), Fse (50), ZstdHuffman (43), Zstd (27), XxHash (12), ZstdFrame (6). Total spec line count: 4,448 lines.
11-PR batch (Mar 7–8): le_size capstone + content preservation + FSE validity + quality reviews:
This batch completed the le_size campaign to the frame level, started content preservation proofs for sequence execution, proved FSE table validity composition, and continued quality reviews across DEFLATE spec files.
Track E le_size campaign completion (3 PRs):
- #848:
parseHuffmanTreeDescriptor_le_size— tree descriptor position bounds - #856:
decompressBlocksWF_le_size— block loop position within data bounds (composed fromparseBlockHeader_le_size+ per-block-type bounds) - #861:
decompressFrame_le_size— frame-level capstone. WhendecompressFramesucceeds, returned position ≤ data.size. Composed fromparseFrameHeader_le_sizeanddecompressBlocksWF_le_size. This completes the le_size chain:readBit→readBits→parseLiteralsSection→parseHuffmanTreeDescriptor→decompressBlocksWF→decompressFrame.
Track E content preservation (1 PR):
- #865:
executeSequences_loop_getElem_lt— sequence execution loop preserves previously written output bytes. First content preservation theorem for the Zstd sequence execution pipeline.
Track E BackwardBitReader invariants (1 PR):
- #858:
readBits_data_eq,readBits_startPos_eq,init_data_eq,init_startPos_eq— field preservation through BackwardBitReader operations.
Track E FSE table validity (1 PR):
- #872:
buildFseTable_symbol_lt(symbol indices within bounds through all 4 construction loops) andbuildFseTable_valid(composedValidFseTablepredicate). UsedforIn_range_preserveshelper for loop invariants. AddedtoUInt16_toNat_lt_of_ltfor UInt16 bounds reasoning.
Quality reviews (4 PRs):
- #853: ZstdHuffman.lean spec audit — eliminated bare
simp, compressed proofs, improved consistency (-121 lines net) - #857: DeflateEncodeDynamic.lean — converted all 7 bare
simptosimp onlywith explicit lemma lists - #862: DeflateEncodeDynamic.lean quality pass — aggregated skill updates and progress documentation alongside bare simp conversions
- #869: DeflateDynamicEmit.lean — removed 1 unused linter pragma and 2
unnecessary
maxRecDepth 2048pragmas (default settings sufficient)
Maintenance (1 PR):
- #866: Rebased PR #857 to resolve merge conflicts in progress files
11-PR batch (Mar 8): Huffman validity, content preservation, offset history, bare simp completion, multi-frame composition:
This batch advanced Track E spec coverage across four dimensions and completed the bare simp campaign project-wide.
Track E table validity (1 PR):
- #884:
buildZstdHuffmanTable_numBits_le(cells numBits within max bit depth) andbuildZstdHuffmanTable_valid(composedValidHuffmanTablepredicate). 3 helper lemmas for fill loop invariants.
Track E content/field preservation (2 PRs):
- #887:
decompressZstdWF_prefix(every byte in the initial accumulator is preserved at the same index in the result — append-only property via WF induction) anddecompressZstd_empty(empty input → empty output). - #894:
decodeHuffmanSymbol_data_eqanddecodeHuffmanSymbol_startPos_eq— BackwardBitReaderdataandstartPosfields are unchanged through Huffman symbol decoding.
Track E offset history validity (1 PR):
- #880:
resolveOffset_history_valid_of_fst_ne_zeroandexecuteSequences_loop_history_valid—ValidOffsetHistorythreaded through the full sequence execution loop. This connectsresolveOffset's per-step history validity to loop-level invariants.
Track E multi-frame composition (1 PR):
- #901:
decompressZstdWF_standard_then_standard(two consecutive standard frames produce concatenated output at WF level) anddecompressZstd_two_frames(same at API level). First compositional spec for multi-frame Zstd streams.
Bare simp campaign completion (5 PRs):
- #879: DeflateDynamicHeader.lean — bare simp + linter pragma cleanup
- #885: DeflateEncodeDynamicProps.lean first half (lines 17–310)
- #886: DeflateEncodeDynamicProps.lean second half (lines 311–695)
- #892: Huffman.lean — completes the non-intentional bare simp campaign
- #900: Fse.lean, HuffmanKraft.lean, BitstreamComplete.lean — final sweep
With #900, zero standalone bare simp calls remain anywhere in the
codebase (Zip/, ZipForStd/, Native/). Only the 15 documented intentional
instances in DeflateSuffix.lean persist. 4 bare simp_all calls remain
in DeflateEncode, DeflateStoredCorrect, EmitTokensCorrect, and
InflateCorrect (see correction in Mar 8 batch notes).
Skill updates (1 PR):
- #891: Created
lean-content-preservationskill from 16-PR batch patterns. Updatedlean-zstd-patterns,lean-zstd-spec-pattern,lean-content-preservationwith recurring proof strategies.
12-PR batch (Mar 8): FSE validity chain capstone, WF decoder refactors, quality reviews:
This batch completed the FSE table validity chain from predefined tables
through resolveSequenceFseTables, added WF variants of two decoder functions
with output size theorems, proved API-level specs for sequence history and
skippable frames, and continued quality reviews across core spec files.
FSE table validity chain completion (3 PRs):
- #911:
buildPredefinedFseTables_valid— all three predefined FSE tables satisfyValidFseTable(litLen at accuracyLog 6, matchLen at 6, offset at 5) - #919:
resolveSingleFseTable_le_size— per-mode position bound preservation (predefined/RLE/repeat modes all return pos' ≤ data.size) - #924:
resolveSingleFseTable_fseCompressed_valid(FSE-compressed mode producesValidFseTable),resolveSingleFseTable_valid_ex(unified across all 4 modes), andresolveSequenceFseTables_valid(all three sequence tables valid when previous tables valid). This is the FSE validity chain capstone:buildFseTable_valid→resolveSingleFseTable_*_valid→resolveSequenceFseTables_valid.
WF decoder refactors with output size theorems (2 PRs):
- #929:
decodeHuffmanStreamWF— structurally recursive variant ofdecodeHuffmanStreamwith two composition theorems:decodeHuffmanStreamWF_size(output = acc.size + count) anddecodeHuffmanStreamWF_totalBitsRemaining_le(bit budget monotonicity) - #930:
decodeFseSymbolsWF— structurally recursive variant ofdecodeFseSymbolswithdecodeFseSymbolsWF_size(output has exactlycountelements). Both WF variants replace opaqueforloops that could not be unfolded in proofs.
API-level specs (2 PRs):
- #907:
executeSequences_history_valid(ValidOffsetHistory preserved through sequence execution) andexecuteSequences_history_size(output history maintains 3 entries). These lift loop-level invariants to the API boundary. - #912:
decompressZstd_single_skippable(single skippable frame → empty output) anddecompressZstd_skip_then_standard(skip + standard → only standard content). API-level specializations of WF-level frame theorems.
Quality reviews (4 PRs):
- #908: ZstdHuffman.lean — extracted
readBits_elimpattern, removed dead code - #915: Zstd.lean —
unfold_excepttactic macro replacing 20 identical monadic unfolding calls, grind audit (6 calls verified appropriate), removed 4 unusedtermination_by/decreasing_byclauses (-21 lines) - #918: Fse.lean — proof optimization (-87 lines, 5.5%), 2 bare simp_all eliminated
- #926: Deflate.lean — 3 bare simp_all to targeted tactics, proof optimization
(-67 lines, 6.3%), dead code removal (
le_bytes_eq_encodeLEU16, 30 lines)
Skill updates (1 PR):
- #925: Meditate: updated
lean-zstd-spec-patternwith table validity chain and frame composition patterns; updatedproof-review-checklistwith compression and helper extraction patterns
13-PR batch (Mar 8): WF maturation, content characterization, simp_all review throughput:
This batch advanced Track E along two fronts — WF refactoring enabling output size theorems, and content characterization for raw/RLE literals and blocks — while continuing the simp_all elimination campaign across DEFLATE spec files.
Track E feature PRs (5):
- #935:
decodeFseLoop_probs_ge_neg1— FSE loop probability bound invariant. WhendecodeFseLoopsucceeds and all input probs have.toInt ≥ -1, all output probs also have.toInt ≥ -1. 8 supporting lemmas coveringArray.pushpreservation,pushZerosbounds,readProbValueoutput bounds. - #940:
parseHuffmanTreeDescriptor_valid(composedValidHuffmanTablefrom descriptor parsing) andparseHuffmanTreeDescriptor_maxBits_pos(maxBits ≥ 1). Validity composition connecting tree descriptor parsing to table construction. - #943:
decodeSequencesWF— structurally recursive variant ofdecodeSequenceswithdecodeSequencesWF_size(output has exactlynumSeqelements when numSeq > 0). Replaces opaqueforloop. - #951:
decodeFourHuffmanStreamsWF— structurally recursive variant ofdecodeFourHuffmanStreamswithdecodeFourHuffmanStreamsWF_size(output buffer size = regenSize). Combines four individualdecodeHuffmanStreamWF_size. - #952:
parseLiteralsSection_raw_eq_extract(raw literals are an exact contiguous slice of input data) andparseLiteralsSection_rle_all_eq(RLE literals are all-equal). First content characterizations for literal sections.
Track E content — block level (1 PR):
- #955:
decompressBlocksWF_single_rawanddecompressBlocksWF_single_rle— single-block output characterization. When the block loop processes a single last raw/RLE block, the result isoutput ++ blockat the correct position.
Review PRs — simp_all campaign (7):
- #936: HuffmanCorrectLoop.lean — simp_all conversion + proof optimization
- #939: HuffmanCorrect.lean — simp_all audit + proof optimization
- #944: HuffmanEncodeCorrect.lean — simp_all + bare simp elimination
- #947: DecodeCorrect.lean — simp_all conversion + proof optimization
- #950: DecodeComplete.lean — simp_all conversion + proof optimization
- #956: BitstreamWriteCorrect.lean — simp_all conversion + proof optimization
- #961: DeflateFixedTables.lean — simp_all conversion + proof optimization
Summary: The Zstd spec infrastructure now spans 6 files with 272 theorems/lemmas: ZstdSequence (77), Fse (73), ZstdHuffman (65), Zstd (33), XxHash (12), ZstdFrame (12). Total spec line count: 6,267 lines.
Bare simp_all correction: The previous batch claimed "0 bare simp_all
remaining" but 4 instances survived in DeflateEncode.lean,
DeflateStoredCorrect.lean, EmitTokensCorrect.lean, and InflateCorrect.lean.
These are pre-existing residuals, not regressions from this batch. The simp_all
campaign has made substantial progress (12 simp_all only conversions across
spec files) but is not yet complete.
WF refactoring status: The WF refactoring pipeline now covers 5 Zstd
decoder functions: decodeFseSymbolsWF, decodeHuffmanStreamWF,
decodeFourHuffmanStreamsWF, decodeSequencesWF, decompressBlocksWF. Each
WF variant replaces an opaque for/while loop that could not be unfolded
in proofs, enabling output size theorems and loop invariant reasoning.
Content characterization emergence: This batch introduced the first
literal section content specs (raw extract, RLE all-equal) and composed them
to the block loop level (single-block raw/RLE output = output ++ block).
Combined with the existing executeSequences_loop_getElem_lt (sequence
execution preserves previously written bytes), this represents the beginning
of end-to-end content characterization for Zstd frames — though significant
gaps remain between the literal/sequence level and full frame output.
10-PR batch (Mar 8): content pipeline maturation + simp_all campaign completion:
This batch advanced the Track E content pipeline from single-block characterization to multi-block composition and frame-level output, while completing the simp_all campaign down to 2 remaining instances.
Track E content pipeline — block level (3 PRs):
- #962:
decompressBlocksWF_raw_stepanddecompressBlocksWF_rle_step— non-last block continuation theorems. When the block loop processes a non-last raw/RLE block followed by more blocks, the intermediate output isoutput ++ blockat the correct position. - #988:
decompressBlocksWF_two_rawanddecompressBlocksWF_two_rle— two-block composition for same-type blocks. Output =output ++ block1 ++ block2. - #1000:
decompressBlocksWF_two_compressed_literals— two-block composition for compressed-literals blocks (non-last compressed + last compressed).
Track E content pipeline — mixed types (1 PR):
- #997:
decompressBlocksWF_raw_then_rleanddecompressBlocksWF_rle_then_raw— mixed-type two-block composition. Raw followed by RLE and vice versa.
Track E content pipeline — frame level (1 PR):
- #974:
decompressFrame_raw_contentanddecompressFrame_rle_content— single-block frame output characterization. When a frame contains a single raw/RLE block, the frame-level output matches the block content.
Merge conflict recovery (2 PRs):
- #989: rebased compressed sequences theorems (#977) onto master after merge conflicts from concurrent PRs
- #982: rebased compressed literals-only theorems (#970) onto master
simp_all campaign (3 PRs):
- #976: HuffmanTheorems + EmitTokensCorrect + DeflateStoredCorrect — converted
all bare
simp_allin these files to explicit alternatives. Reduces bare simp_all count from 4 to 2. - #996: Fse.lean + HuffmanEncode.lean + HuffmanCorrect.lean — completes the simp_all campaign across these files (0 remaining in each).
- #995: ZstdSequence.lean — simp_all conversion + proof optimization.
Quality reviews (2 PRs):
- #981: GzipCorrect.lean + ZlibCorrect.lean — bare simp cleanup + proof optimization
- #971: BitReaderInvariant.lean — simp_all conversion + proof optimization
Content pipeline status: The Track E content characterization now covers:
| Block type | Single | Step (non-last) | Two-block (same) | Two-block (mixed) | Frame (single) |
|---|---|---|---|---|---|
| Raw | done | done | done | raw+RLE done | done |
| RLE | done | done | done | RLE+raw done | done |
| Compressed (literals-only) | done | done | done | — | — |
| Compressed (with sequences) | — | — | — | — | — |
simp_all campaign status: 2 bare simp_all remaining (DeflateEncode.lean,
InflateCorrect.lean). Both are covered by existing review issue #968. Down from
~50+ at campaign start to 2.
Summary: The Zstd spec infrastructure now spans 6 files with 333 theorems/lemmas: ZstdSequence (84), Fse (80), ZstdHuffman (73), Zstd (59), XxHash (25), ZstdFrame (12). Total spec line count: 6,719 lines.
15-PR batch (Mar 8–9): compressed composition matrix, API-level content, Zstd benchmarking:
This batch filled in the block-level compressed composition matrix, extended frame-level content to compressed block types, lifted content theorems to the API level, added Zstd decompression benchmarking, and continued quality reviews.
Track E block-level compositions (4 PRs):
- #1013:
decompressBlocksWF_two_compressed_sequences_blocks(homogeneous compSeq pair) anddecompressBlocksWF_compressed_seq_then_raw(compSeq+raw) - #1019:
decompressBlocksWF_compressed_literals_then_rawanddecompressBlocksWF_compressed_literals_then_rle(compLit+raw, compLit+RLE) - #1033:
decompressBlocksWF_raw_then_compressed_sequences,decompressBlocksWF_rle_then_compressed_sequences,decompressBlocksWF_compressed_seq_then_rle(raw/RLE+compSeq, compSeq+RLE) - #1034:
decompressBlocksWF_compressed_seq_then_compressed_litanddecompressBlocksWF_compressed_lit_then_compressed_seq(cross-compressed pairs)
Track E frame-level content (3 PRs):
- #1020:
decompressFrame_raw_then_rle_contentanddecompressFrame_rle_then_raw_content(mixed raw/RLE two-block frames) - #1027:
decompressFrame_single_compressed_literals_contentanddecompressFrame_single_compressed_sequences_content(single compressed blocks) - #1051:
decompressFrame_compressed_lit_then_raw_contentanddecompressFrame_compressed_lit_then_rle_content(compressed-first + raw/RLE)
Track E API-level content (2 PRs):
- #1029:
decompressZstd_two_raw_blocks_contentanddecompressZstd_two_rle_blocks_content(two-block raw/RLE at API level) - #1039:
decompressZstd_single_compressed_literalsanddecompressZstd_single_compressed_sequences(single compressed-block at API level)
Track D (1 PR):
- #1038: Zstd decompression benchmarking suite in
ZipTest/ZstdBench.lean— native vs FFI across 4 compression levels × 4 data patterns × 3 sizes
Quality reviews (3 PRs):
- #1032: DynamicTreesComplete + DynamicTreesCorrect cleanup
- #1040: DecodeCorrect.lean + GzipCorrect.lean bare simp cleanup
- #1046: ZstdFrame.lean — 1 bare simp eliminated, helper lemma extraction
Merge conflict recovery (1 PR):
- #1023: rebased PR #1009 onto master, closed stale #1006
Self-improvement (1 PR):
- #1024: Meditate — composition matrix scaling patterns, review campaign completion analysis, conflict resolution patterns
Block-level composition matrix status (14/16):
| Raw | RLE | CompLit | CompSeq | |
|---|---|---|---|---|
| Raw + | done | done | — | done |
| RLE + | done | done | — | done |
| CompLit + | done | done | done | done |
| CompSeq + | done | done | done | done |
2 remaining: raw+compLit and RLE+compLit (covered by open PR #1011).
Content pipeline status (updated):
| Block type | Single block | Step (non-last) | Two-block (same) | Two-block (mixed) | Frame (single) | Frame (two-block) | API (single) |
|---|---|---|---|---|---|---|---|
| Raw | done | done | done | raw+RLE | done | two_raw, raw+RLE | two_raw |
| RLE | done | done | done | RLE+raw | done | two_rle, RLE+raw | two_rle |
| CompLit | done | done | done | compLit+raw, +RLE | done | compLit+raw, +RLE | done |
| CompSeq | done | done | done | compSeq+raw, +RLE, +compLit, compLit+compSeq | done | — | done |
simp_all campaign status: 0 bare simp_all remaining (DeflateEncode and
InflateCorrect instances now have explicit lemma arguments: simp_all [beq_iff_eq]
and simp_all [← UInt32.toNat_inj] respectively). Campaign complete.
Summary: The Zstd spec infrastructure now spans 6 files with 361 declarations: ZstdSequence (84), Fse (81), ZstdHuffman (73), Zstd (78), XxHash (26), ZstdFrame (19). Total spec line count: 8,172 lines.
12-PR batch (Mar 9): two-block content theorem completion wave + simp_all elimination + quality reviews:
This batch completed the block-level composition matrix, extended frame-level content coverage across most two-block combinations, lifted several pairings to API level, completed the simp_all elimination campaign, and continued proof quality reviews.
Track E block-level completion (1 PR):
- #1068:
decompressBlocksWF_raw_then_compressed_literalsanddecompressBlocksWF_rle_then_compressed_literals— completing the 16/16 block-level composition matrix.
Track E frame-level content (4 PRs):
- #1055:
decompressFrame_compressed_seq_then_raw_contentanddecompressFrame_compressed_seq_then_rle_content(compSeq-first + raw/RLE) - #1060:
decompressFrame_compressed_seq_then_compressed_lit_contentanddecompressFrame_compressed_lit_then_compressed_seq_content(cross-compressed) - #1073:
decompressFrame_two_compressed_literals_blocks_contentanddecompressFrame_two_compressed_sequences_blocks_content(homogeneous compressed)
Track E API-level content (3 PRs):
- #1052:
decompressZstd_raw_then_rle_contentanddecompressZstd_rle_then_raw_content(mixed raw/RLE two-block frames at API level) - #1060:
decompressZstd_single_raw_block_contentanddecompressZstd_single_rle_block_content(single raw/RLE at API level) - #1061:
decompressZstd_compressed_lit_then_raw_contentanddecompressZstd_compressed_lit_then_rle_content(compLit+raw/RLE at API level) - #1074:
decompressZstd_compressed_seq_then_compressed_lit_contentanddecompressZstd_compressed_lit_then_compressed_seq_content(cross-compressed at API level)
Track E FSE spec (1 PR):
- #1067:
readProbValue_pos_le_sizeanddecodeFseDistribution_pos_le_size— position-within-bounds chain for FSE distribution decoding
simp_all campaign final completion (1 PR):
- #1069: Converted the last 4
simp_allcalls across Deflate.lean, DeflateEncode.lean, and InflateCorrect.lean tosimp onlywith explicit lemma lists. 0simp_allnow remain across all spec files.
Quality reviews (2 PRs):
- #1056: DeflateStoredCorrect + DeflateFixedCorrect — proof optimization
- #1062: DeflateDynamicCorrect + DeflateDynamicFreqs — proof optimization
Self-improvement (1 PR):
- #1066: Meditate — content pipeline template, hot file tracking, extraction heuristics
Block-level composition matrix status (16/16 — COMPLETE):
| Raw | RLE | CompLit | CompSeq | |
|---|---|---|---|---|
| Raw + | done | done | done | done |
| RLE + | done | done | done | done |
| CompLit + | done | done | done | done |
| CompSeq + | done | done | done | done |
Two-block content theorem coverage matrix (block / frame / API):
| First \ Second | Raw | RLE | CompLit | CompSeq |
|---|---|---|---|---|
| Raw | B/F/A | B/F/A | B/—/— | B/—/— |
| RLE | B/F/A | B/F/A | B/—/— | B/—/— |
| CompLit | B/F/A | B/F/A | B/F/— | B/F/A |
| CompSeq | B/F/— | B/F/— | B/F/A | B/F/— |
B = block-level, F = frame-level, A = API-level. All 4 single-block types are fully covered at all three levels.
Block-level: 16/16 complete. Frame-level: 12/16 covered (missing 4: raw+compLit, raw+compSeq, rle+compLit, rle+compSeq — all raw/RLE-first + compressed-second pairings). API-level: 8/16 covered (missing 8: the 4 frame-level gaps plus compLit+compLit, compSeq+raw, compSeq+rle, compSeq+compSeq).
The lifting pattern: block-level → frame-level → API-level. Each level
composes the lower level's theorem with frame/API wrapping. Frame-level
theorems add parseFrameHeader + checksum handling. API-level theorems add
decompressZstdWF single-frame unwinding. The raw/RLE-first + compressed
gap at frame level is the bottleneck — these require lifting the block-level
compositions that include compressed-second blocks through decompressFrame.
simp_all campaign status: 0 simp_all remaining anywhere in Zip/Spec/.
Campaign fully complete: all instances converted to simp only with explicit
lemma lists or targeted alternatives. This is a significant proof quality
milestone — every simplifier call in the spec layer now declares its
dependencies explicitly.
Summary: The Zstd spec infrastructure now spans 6 files with 543 declarations: Fse (151), ZstdSequence (147), Zstd (102), ZstdHuffman (84), XxHash (32), ZstdFrame (27). Total spec line count: 9,503 lines.
10-PR batch (Mar 9–10): parsing completeness direction + API-level content completion + quality reviews:
This batch introduced a new proof direction — parsing completeness ("if input is well-formed, parsing succeeds") — and completed the two-block API-level content coverage to 14/16 combinations.
Track E two-block API-level content completion (3 PRs):
- #1083:
decompressZstd_raw_then_compressed_lit_contentanddecompressZstd_rle_then_compressed_lit_content(raw/RLE + compLit at API level) - #1087/#1095: Merge conflict fixes for PR #1076 (compSeq+raw/RLE) and PR #1075 (compSeq+raw/RLE at API level), plus ZstdFrame.lean API-level coverage audit
- #1101:
decompressZstd_raw_then_compressed_seq_contentanddecompressZstd_rle_then_compressed_seq_content(raw/RLE + compSeq at API level)
Track E API-level metadata validation (1 PR):
- #1096:
decompressZstd_single_frame_contentSize(when frame header declares contentSize = some n, output has exactly n bytes) anddecompressZstd_single_frame_checksum(when frame has checksum enabled, xxHash64 of output matches stored checksum). These are characterizing properties — they relate frame metadata to decompressed content.
Track E parsing completeness — new proof direction (3 PRs):
- #1097:
parseBlockHeader_succeeds,decompressRawBlock_succeeds,decompressRLEBlock_succeeds— first parsing completeness theorems. Prove that when the input has enough bytes, these functions return.ok. Usedbv_decidefor bitwise AND reasoning in block header catch-all case. - #1108:
parseFrameHeader_succeeds— proves frame header parsing succeeds when data has the correct magic number and enough bytes (viaframeHeaderMinSizehelper). RequiredmaxRecDepth 4096andmaxHeartbeats 800000for deeply nested monadic expressions. - #1114:
parseCompressedLiteralsHeader_succeedsandparseLiteralsSection_succeeds_treeless— compressed literals header and treeless literals section completeness.
Quality reviews (2 PRs):
- #1109: ZstdHuffman.lean audit — extracted
parseHuffmanTreeDescriptor_ok_elimshared eliminator combining three separate case analyses (-62 lines, -4.3%). - #1112: Fse.lean audit — extracted
decodeFseDistribution_ok_decomposehelper from 5 theorems sharing identical unfolding boilerplate (-47 lines, -2.4%).
Merge conflict resolution (1 PR):
- #1087: Resolved merge conflicts for raw/RLE+compSeq frame-level content
Parsing completeness — concept and coverage:
This is the converse of existing correctness theorems. Where correctness says
"if parsing returns .ok, the result satisfies properties P", completeness
says "if input satisfies conditions C, parsing returns .ok". Together they
give "well-formed input ↔ successful parsing". This is a prerequisite for
composing "well-formed Zstd frame → decompression succeeds" end-to-end.
Current parsing completeness coverage:
parseBlockHeader_succeeds(pos + 3 ≤ data.size)decompressRawBlock_succeeds(pos + blockSize ≤ data.size)decompressRLEBlock_succeeds(pos + 1 ≤ data.size)parseFrameHeader_succeeds(magic number + enough bytes per header format)parseCompressedLiteralsHeader_succeeds(pos + headerSize ≤ data.size)parseLiteralsSection_succeeds_treeless(treeless path, sufficient bytes)
Remaining gaps: parseLiteralsSection Huffman-compressed path,
parseSequencesHeader, resolveSingleFseTable (FSE-compressed mode),
resolveSequenceFseTables composition, decompressBlocks loop, full frame.
Two-block content theorem coverage matrix (block / frame / API):
| First \ Second | Raw | RLE | CompLit | CompSeq |
|---|---|---|---|---|
| Raw | B/F/A | B/F/A | B/F/A | B/F/A |
| RLE | B/F/A | B/F/A | B/F/A | B/F/A |
| CompLit | B/F/A | B/F/A | B/F/— | B/F/A |
| CompSeq | B/F/A | B/F/A | B/F/A | B/F/— |
B = block-level, F = frame-level, A = API-level. All 4 single-block types are fully covered at all three levels.
Block-level: 16/16 complete. Frame-level: 16/16 complete. API-level: 14/16 covered (missing 2: compLit+compLit and compSeq+compSeq — homogeneous compressed pairs).
Summary: The Zstd spec infrastructure now spans 6 files with 563 declarations: Fse (152), ZstdSequence (147), Zstd (111), ZstdHuffman (86), XxHash (32), ZstdFrame (35). Total spec line count: 10,377 lines.
15-PR batch (Mar 10–11): completeness chain deepening + API-level content completion + review campaign:
This batch deepened the parsing completeness chain to cover nearly every sub-function in the Zstd decompression pipeline, completed the two-block API-level content matrix to 16/16, and advanced the code quality review campaign to 4 of 5 Zstd spec files.
Track E parsing completeness (7 PRs):
- #1121:
parseLiteralsSection_succeeds_compressed— completeness for Huffman-compressed literals (litType=2), the most complex literal path - #1123:
resolveSequenceFseTables_succeeds— composed completeness theorem threading threeresolveSingleFseTablecalls - #1130:
parseHuffmanTreeDescriptor_succeeds_fseandparseHuffmanWeightsFse_succeeds— FSE-path Huffman tree descriptor completeness - #1131:
BackwardBitReader_init_succeeds— backward bitstream initialization completeness - #1134:
resolveSingleFseTable_succeeds_fse(FSE-compressed mode) anddecodeSequencesWF_succeeds_zero(zero-sequence case) - #1135:
decodeHuffmanLiterals_succeeds_singleanddecodeHuffmanLiterals_succeeds_four— Huffman literal decoding completeness for both 1-stream and 4-stream modes - #1138:
parseSequencesHeader_succeedsandparseSequencesHeader_succeeds_zero— universal parsing completeness with byte0 case dispatch
Track E characterization (1 PR):
- #1144:
parseBlockHeader_lastBlock_eq,parseBlockHeader_blockType_eq,parseBlockHeader_blockSize_eq— field characterization theorems proving each field equals specific bit ranges of the 24-bit header word
Track E API-level content completion (1 PR):
- #1151:
decompressZstd_two_compressed_literals_blocks_contentanddecompressZstd_two_compressed_sequences_blocks_content— the final two API-level content theorems, completing the 16/16 matrix
Quality reviews (4 PRs):
- #1139: ZstdSequence.lean — proof body compression and refactoring
- #1145: ZstdSequence.lean — proof quality audit
- #1152: Zstd.lean — proof quality audit with
frame_from_blockstactic macro extraction andparseBlockHeader_data_not_lehelper (−406 lines, −10.3%) - #1153: ZstdFrame.lean — proof quality audit
Housekeeping (2 PRs):
- #1127: Meditate — parsing completeness maturity analysis, created
lean-parsing-completenessskill (255 lines) - #1146: Stale issue cleanup — closed 2 moot issues, removed stale labels
Parsing completeness coverage (updated):
| Function | Status |
|---|---|
parseBlockHeader |
done |
decompressRawBlock |
done |
decompressRLEBlock |
done |
parseFrameHeader |
done |
parseCompressedLiteralsHeader |
done |
parseLiteralsSection (treeless) |
done |
parseLiteralsSection (compressed, litType=2) |
done |
parseHuffmanWeightsDirect |
done |
parseHuffmanWeightsFse |
done |
parseHuffmanTreeDescriptor (direct) |
done |
parseHuffmanTreeDescriptor (FSE) |
done |
BackwardBitReader.init |
done |
decodeHuffmanLiterals (1-stream, 4-stream) |
done |
resolveSingleFseTable (predefined/RLE/repeat/FSE) |
done |
resolveSequenceFseTables (composed) |
done |
parseSequencesHeader |
done |
decodeSequencesWF (zero-case) |
done |
parseLiteralsSection (raw/RLE) |
gap |
decompressBlocksWF (composed) |
gap |
executeSequences |
gap |
| Frame-level composed | gap |
The completeness chain now covers 17 functions/paths. Remaining gaps:
parseLiteralsSection raw/RLE paths, decompressBlocksWF loop
composition, executeSequences, and full frame-level composition.
Two-block content theorem coverage matrix (block / frame / API) — COMPLETE:
| First \ Second | Raw | RLE | CompLit | CompSeq |
|---|---|---|---|---|
| Raw | B/F/A | B/F/A | B/F/A | B/F/A |
| RLE | B/F/A | B/F/A | B/F/A | B/F/A |
| CompLit | B/F/A | B/F/A | B/F/A | B/F/A |
| CompSeq | B/F/A | B/F/A | B/F/A | B/F/A |
All 16/16 combinations covered at all three levels (block, frame, API). All 4 single-block types also fully covered at all three levels.
Review campaign progress: 4 of 5 Zstd spec files audited:
- ZstdHuffman.lean (#1109, #853) — extracted shared eliminators, −4.3%
- Fse.lean (#1112, #822) — extracted decomposition helpers, −2.4%
- ZstdSequence.lean (#1145, #1139) — proof compression + quality audit
- Zstd.lean (#1152) — tactic macro extraction, −10.3%
- ZstdFrame.lean (#1153) — proof quality audit Remaining: XxHash.lean (previously audited at #807).
Spec declaration count note: Total spec declarations dropped from 1,819 to 1,793 despite net additions — the Zstd.lean review (#1152) removed dead code and deduplicated helper lemmas more aggressively than new theorems were added. The Zstd-specific declaration count rose from 563 to 572 (net +9 new theorems).
Summary: The Zstd spec infrastructure now spans 6 files with 572 declarations: ZstdSequence (168), Fse (148), Zstd (106), ZstdHuffman (89), ZstdFrame (37), XxHash (24). Total spec line count: 10,448 lines.
12-PR batch (Mar 11–12): frame completeness chain + review campaign completion + field characterization:
This batch deepened the parsing completeness chain from individual function completeness to composed frame-level completeness for raw/RLE and compressed blocks, completed the Zstd spec file review campaign (all 6 files audited), and proved executeSequences loop completeness.
Track E parsing completeness — raw/RLE literals (1 PR):
- #1159:
parseLiteralsSection_succeeds_rawandparseLiteralsSection_succeeds_rle— completeness for raw and RLE literal section parsing. Closes the gap identified in the previous batch's completeness coverage table.
Track E composed frame-level completeness (4 PRs):
- #1160:
skipSkippableFrame_succeeds(skippable frame completeness) anddecompressZstd_succeeds_single_skippable(composed skippable-frame completeness at WF level) - #1161:
decompressBlocksWF_succeeds_single_rawanddecompressBlocksWF_succeeds_single_rle— composed block-loop completeness for single raw/RLE blocks (6-step pattern: typeVal derivation → parse result → field characterization → constraints → operation result → composition) - #1168:
decompressBlocksWF_succeeds_single_compressed_zero_seq(numSeq=0) anddecompressBlocksWF_succeeds_single_compressed_sequences(numSeq>0) — composed block-loop completeness for single compressed blocks - #1178:
decompressFrame_succeeds_single_rawanddecompressFrame_succeeds_single_rle— composed frame-level completeness for single raw/RLE blocks (composesparseFrameHeader_succeeds+decompressBlocksWF_succeeds_single_*)
Track E sequence execution completeness (1 PR):
- #1179:
executeSequences_loop_succeedsandexecuteSequences_succeeds— completeness for the sequence execution loop under windowed mode. AddedOffsetsValidForLooppredicate for per-step offset validity andfoldl_litLen_addhelper. 118 lines added to ZstdSequence.lean.
Track E field characterization (1 PR):
- #1164:
parseFrameHeader_frameHeaderDescriptor_eq,parseFrameHeader_windowDescriptor_eq,parseFrameHeader_dictionaryId_eq,parseFrameHeader_frameContentSize_eq— proves each parsed field equals specific byte ranges of the raw header data
Track E API-level content completion (1 PR):
- #1151:
decompressZstd_two_compressed_literals_blocks_contentanddecompressZstd_two_compressed_sequences_blocks_content— the final two API-level content theorems, completing the 16/16 two-block content matrix
Quality reviews (3 PRs):
- #1152: Zstd.lean proof quality audit — extracted
frame_from_blockstactic macro (replacing 19-line frame-unwinding epilogue in ~20 theorems) andparseBlockHeader_data_not_lehelper (−406 lines, −10.3%) - #1153: ZstdFrame.lean proof quality audit
- #1154: Removed dead
_hdicthypotheses from 20 theorem signatures across 2 Zstd spec files
Housekeeping (2 PRs):
- #1158: ZipForStd upstreaming readiness audit — assessed 4 files (52 lemmas) for Lean/Std PR readiness
- #1167: Closed duplicate issue #1163, updated ZstdFrame.lean content matrix docstring from "12/16" to "16/16"
Self-improvement (1 PR):
- #1171: Meditate — updated
lean-parsing-completenessskill with 6-step composed completeness pattern, updatedlean-simp-tacticswith Bool/Prop bridging reference
Parsing completeness coverage (updated):
| Function | Status |
|---|---|
parseBlockHeader |
done |
decompressRawBlock |
done |
decompressRLEBlock |
done |
parseFrameHeader |
done |
parseCompressedLiteralsHeader |
done |
parseLiteralsSection (treeless) |
done |
parseLiteralsSection (compressed, litType=2) |
done |
parseLiteralsSection (raw) |
done |
parseLiteralsSection (RLE) |
done |
parseHuffmanWeightsDirect |
done |
parseHuffmanWeightsFse |
done |
parseHuffmanTreeDescriptor (direct) |
done |
parseHuffmanTreeDescriptor (FSE) |
done |
BackwardBitReader.init |
done |
decodeHuffmanLiterals (1-stream, 4-stream) |
done |
resolveSingleFseTable (predefined/RLE/repeat/FSE) |
done |
resolveSequenceFseTables (composed) |
done |
parseSequencesHeader |
done |
decodeSequencesWF (zero-case) |
done |
skipSkippableFrame |
done |
executeSequences |
done |
decompressBlocksWF (single raw) |
done |
decompressBlocksWF (single RLE) |
done |
decompressBlocksWF (single compressed, zero-seq) |
done |
decompressBlocksWF (single compressed, with-seq) |
done |
decompressFrame (single raw) |
done |
decompressFrame (single RLE) |
done |
decompressBlocksWF (multi-block composed) |
gap |
decompressFrame (compressed blocks) |
gap |
decompressZstd (full end-to-end) |
gap |
The completeness chain now covers 21 functions/paths (up from 17). Composed
completeness reaches the frame level for raw/RLE single-block frames. Remaining
gaps: multi-block composed completeness, compressed-block frame-level
completeness, and end-to-end decompressZstd completeness.
Review campaign status (6/6 Zstd spec files — COMPLETE):
- Fse.lean (#1112, #822, #918) — decomposition helpers, proof optimization
- ZstdHuffman.lean (#1109, #853, #908) — shared eliminators, dead code removal
- ZstdSequence.lean (#1145, #1139, #995) — proof compression + quality audit
- Zstd.lean (#1152) — tactic macro extraction, −10.3%
- ZstdFrame.lean (#1153) — proof quality audit
- XxHash.lean (#807) — quality pass
Summary: The Zstd spec infrastructure now spans 6 files with 634 declarations: ZstdSequence (181), Fse (154), Zstd (130), ZstdHuffman (99), ZstdFrame (38), XxHash (32). Total spec line count: 11,136 lines.
14-PR batch (Mar 12): two-block compressed completeness + write-side review campaign:
This batch advanced Track E composed completeness into compressed block territory (both at block and frame/API levels) while the review campaign audited 8 DEFLATE write-side spec file pairs.
Track E composed completeness — block level (3 PRs):
- #1198:
decompressBlocksWF_succeeds_two_raw_blocksanddecompressBlocksWF_succeeds_two_rle_blocks— homogeneous two-block completeness for raw and RLE blocks - #1205:
decompressBlocksWF_succeeds_raw_then_rle,decompressBlocksWF_succeeds_rle_then_raw— heterogeneous raw/RLE two-block completeness - #1220:
decompressBlocksWF_succeeds_raw_then_compressed_zero_seqanddecompressBlocksWF_succeeds_rle_then_compressed_zero_seq— first two-block completeness involving compressed blocks (raw/RLE + compressed zero-seq)
Track E composed completeness — frame and API levels (2 PRs):
- #1193:
decompressFrame_succeeds_single_compressed_zero_seqanddecompressFrame_succeeds_single_compressed_sequences— frame-level completeness for single compressed blocks (both zero-seq and with-seq) - #1212:
decompressZstd_succeeds_single_compressed_zero_seq_frameanddecompressZstd_succeeds_single_compressed_sequences_frame— end-to-end API-level completeness for single compressed blocks. ComposesparseFrameHeader_succeeds→decompressFrame_succeeds_*→decompressZstd_single_frame.
DEFLATE write-side review campaign (7 PRs):
- #1189: DecodeCorrect + DecodeComplete — bare
simpaconversion, dead hypothesis removal - #1194: HuffmanCorrect — proof audit, no changes needed
- #1195: GzipCorrect + ZlibCorrect + DeflateRoundtrip (695 lines) —
redundant
haveinlining, proof optimization (−18 lines) - #1199: BitstreamCorrect + BitstreamComplete — proof audit
- #1202: DeflateDynamicHeader + DeflateDynamicEmit (706 lines) —
merged consecutive
simp onlypairs, inlined deadhavebindings - #1206: BitstreamWriteCorrect + BitWriterCorrect — proof audit
- #1209: DeflateEncode + DeflateEncodeProps (1021 lines) — merged
consecutive
rwcalls, removed unnecessary case split, dead variable - #1214: DeflateEncodeDynamic + DeflateEncodeDynamicProps (1141 lines) —
replaced verbose
List.reduceReplicatepattern withList.length_replicate - #1215: DynamicTreesCorrect + DynamicTreesComplete (1305 lines) — 2 bare
simpa→simpa only, 1 dead hypothesis removed. Discovered bare simp count from issue was inflated — allsimpcalls already usedsimp only.
Composed completeness coverage (updated):
| Function | Status |
|---|---|
decompressBlocksWF (two raw) |
done |
decompressBlocksWF (two RLE) |
done |
decompressBlocksWF (raw+RLE, RLE+raw) |
done |
decompressBlocksWF (raw/RLE + compressed zero-seq) |
done |
decompressFrame (single compressed zero-seq) |
done |
decompressFrame (single compressed with-seq) |
done |
decompressZstd (single compressed zero-seq frame) |
done |
decompressZstd (single compressed with-seq frame) |
done |
decompressBlocksWF (raw/RLE + compressed with-seq) |
gap |
decompressBlocksWF (compressed + raw/RLE) |
gap |
decompressFrame (two-block compressed) |
gap |
decompressZstd (full end-to-end multi-block) |
gap |
The composed completeness chain now reaches the API level for single compressed blocks (both zero-seq and with-sequences). Two-block completeness covers all raw/RLE combinations and the first mixed raw/RLE + compressed pairings. Remaining gaps: compressed-first two-block completeness, and composing multi-block completeness to frame and API levels.
Review campaign progress: The write-side DEFLATE review campaign
covered 8 file pairs (4,868 lines total). All files are now clean:
0 bare simp, 0 bare simp_all, no dead hypotheses. The campaign
confirmed that most files had already reached review quality from
previous passes — the main value was verification rather than
remediation.
Summary: The Zstd spec infrastructure now spans 6 files with 454 declarations: Zstd (117), ZstdSequence (97), ZstdHuffman (88), Fse (87), ZstdFrame (39), XxHash (26). Total spec line count: 11,879 lines.
17-PR batch (Mar 12): completeness matrix expansion + DEFLATE review deepening:
This batch expanded the two-block composed completeness ("succeeds") matrix across all three levels (block, frame, API), primarily filling in compressed block combinations. Concurrently, 7 review PRs deepened the DEFLATE proof quality campaign into previously unaudited spec files.
Track E composed completeness — block level (4 PRs):
- #1227:
decompressBlocksWF_succeeds_compressed_zero_seq_then_rawanddecompressBlocksWF_succeeds_compressed_zero_seq_then_rle— compressed zero-seq first + raw/RLE second - #1248:
decompressBlocksWF_succeeds_compressed_sequences_then_rawanddecompressBlocksWF_succeeds_compressed_sequences_then_rle— compressed sequences first + raw/RLE second - #1256:
decompressBlocksWF_succeeds_compressed_zero_seq_then_compressed_zero_seqanddecompressBlocksWF_succeeds_compressed_zero_seq_then_compressed_sequences— compressed-to-compressed pairs - #1281:
decompressBlocksWF_succeeds_compressed_sequences_then_compressed_zero_seqanddecompressBlocksWF_succeeds_compressed_sequences_then_compressed_sequences— remaining compressed-to-compressed pairs (conflict fix for #1264)
Track E composed completeness — frame level (3 PRs):
- #1231:
decompressFrame_succeeds_raw_then_compressed_zero_seqanddecompressFrame_succeeds_rle_then_compressed_zero_seq— raw/RLE first- compressed zero-seq second
- #1242:
decompressFrame_succeeds_compressed_zero_seq_then_rawanddecompressFrame_succeeds_compressed_zero_seq_then_rle— compressed zero-seq first + raw/RLE second - #1277:
decompressFrame_succeeds_compressed_sequences_then_rawanddecompressFrame_succeeds_compressed_sequences_then_rle— compressed sequences first + raw/RLE second (conflict fix for #1188/#1257)
Track E composed completeness — API level (3 PRs):
- #1252:
decompressZstd_succeeds_rle_then_rle_frame,decompressZstd_succeeds_rle_then_raw_frame,decompressZstd_succeeds_rle_then_compressed_zero_seq_frame— rle-first frames at API level - #1269:
decompressZstd_succeeds_two_raw_frame,decompressZstd_succeeds_raw_then_rle_frame,decompressZstd_succeeds_raw_then_compressed_zero_seq_frame— raw-first frames at API level (conflict fix for #1253) - #1277:
decompressZstd_succeeds_single_raw_frameanddecompressZstd_succeeds_single_rle_frame— single raw/RLE at API level
Quality reviews — DEFLATE spec files (7 PRs):
- #1230: LZ77.lean + LZ77Lazy.lean — proof quality audit
- #1234: HuffmanKraft.lean + HuffmanTheorems.lean — proof quality audit
- #1243: ZstdHuffman.lean — proof quality audit (refactoring)
- #1244: ZstdFrame.lean — proof quality audit
- #1258: DeflateStoredCorrect.lean + DeflateFixedCorrect.lean — proof quality audit
- #1262: LZ77NativeCorrect.lean + EmitTokensCorrect.lean — proof quality audit
- #1278: DeflateSuffix.lean + BinaryCorrect.lean — proof quality audit
Self-improvement (1 PR):
- #1226: Meditate — 17-PR batch skill updates
Two-block composed completeness ("succeeds") matrix — current state:
Block-level (14/16):
| Raw | RLE | CompZS | CompSeq | |
|---|---|---|---|---|
| Raw + | done | done | done | — |
| RLE + | done | done | done | — |
| CompZS + | done | done | done | done |
| CompSeq + | done | done | done | done |
Missing at block level: raw+comp_sequences and rle+comp_sequences.
Frame-level (10/16 two-block + 4 single):
| Raw | RLE | CompZS | CompSeq | |
|---|---|---|---|---|
| Raw + | done | done | done | — |
| RLE + | done | done | done | — |
| CompZS + | done | done | — | — |
| CompSeq + | done | done | — | — |
Missing at frame level: all 4 compressed-to-compressed pairs plus raw+comp_sequences and rle+comp_sequences.
API-level (6/16 two-block + 5 single):
| Raw | RLE | CompZS | CompSeq | |
|---|---|---|---|---|
| Raw + | done | done | done | — |
| RLE + | done | done | done | — |
| CompZS + | — | — | — | — |
| CompSeq + | — | — | — | — |
The API level covers raw/RLE-first pairings only. Compressed-first and compressed-to-compressed API-level completeness remains to be lifted from the frame level.
Review campaign: This batch extended the DEFLATE proof quality review to 7 additional file pairs, including LZ77 (greedy + lazy), Huffman characterizing properties, and several correctness files. These are cumulative with the previous write-side campaign (#1189–#1215) and the Zstd spec review campaign (all 6 files). The codebase now has extensive review coverage across both DEFLATE and Zstd spec layers.
Summary: The Zstd spec infrastructure now spans 6 files with 501 declarations: Zstd (145), ZstdSequence (104), Fse (95), ZstdHuffman (86), ZstdFrame (48), XxHash (23). Total spec line count: 14,081 lines.
8-PR batch (Mar 12): API-level succeeds near-completion + review campaign deepening:
This batch advanced the API-level succeeds matrix to 19/21 and continued the DEFLATE proof quality review campaign across 4 more spec file pairs.
Track E API-level succeeds (3 PRs):
- #1296: Merge conflict fix for frame-level comp_zero_seq + compressed block theorems, unblocking downstream API-level lifting
- #1306:
decompressZstd_succeeds_compressed_sequences_then_raw_frameanddecompressZstd_succeeds_compressed_sequences_then_rle_frame— compressed sequences first + raw/RLE at API level - #1320:
decompressZstd_succeeds_raw_then_compressed_sequences_frame,decompressZstd_succeeds_rle_then_compressed_sequences_frame,decompressZstd_succeeds_compressed_sequences_then_compressed_zero_seq_frame,decompressZstd_succeeds_two_compressed_sequences_frame— 4 compressed- sequences two-block pairs at API level, derived from content theorems
Quality reviews — DEFLATE spec files (4 PRs):
- #1300: DeflateDynamicCorrect + DeflateDynamicFreqs — proof optimization
- #1304: InflateLoopBounds + InflateRawSuffix — proof quality audit (conflict fix for #1276)
- #1314: DynamicTreesCorrect + DynamicTreesComplete — minimal changes, files already at review quality
- #1316: BitReaderInvariant + BitWriterCorrect — proof quality audit
Infrastructure (1 PR):
- #1307: Meditate — merge conflict frequency analysis, completeness matrix scaling patterns
Housekeeping (3 PRs):
- #1321: Triage stale issue #1318 (work already merged via #1306)
- #1325: Recovered proof-review-checklist patterns from conflicted PR #1312
- #1326: Recovered
bfinal_suffix_dispatchtactic macro from PR #1312
Two-block composed completeness ("succeeds") matrix — current state:
API-level (19/21):
| Raw | RLE | CompZS | CompSeq | |
|---|---|---|---|---|
| Raw + | done | done | done | done |
| RLE + | done | done | done | done |
| CompZS + | done | done | — | — |
| CompSeq + | done | done | done | done |
Missing at API level: czs+czs and czs+cs. Both exist at frame level
(from decompressFrame_succeeds_compressed_zero_seq_then_compressed_*
theorems in Zstd.lean) and need only the standard API-level lift.
Frame-level completeness is 16/16 for two-block combinations plus all 4 single-block types. Block-level completeness is 16/16.
Review campaign progress: The DEFLATE proof quality review campaign now covers all 49 spec files across multiple passes. This batch audited 4 DEFLATE spec file pairs (BitReaderInvariant, BitWriterCorrect, InflateLoopBounds, InflateRawSuffix, DeflateDynamicCorrect, DeflateDynamicFreqs, DynamicTreesCorrect, DynamicTreesComplete). Most files required only minimal changes, confirming convergence toward review-complete quality.
Summary: The Zstd spec infrastructure now spans 6 files with 491 declarations: Zstd (137), ZstdSequence (97), Fse (87), ZstdHuffman (88), ZstdFrame (56), XxHash (26). Total spec line count: 15,091 lines.
5-PR batch (Mar 12): review campaign completion + Track D1 benchmark:
This batch completed the DEFLATE proof quality review campaign to cover all 49 spec files and added the first DEFLATE decompression throughput benchmark.
Quality reviews — final DEFLATE spec files (4 PRs):
- #1327/#1328: HuffmanCorrect + HuffmanCorrectLoop proof quality audit (with merge conflict rebase)
- #1332: Deflate.lean proof quality audit
- #1333: Final spec file batch — Adler32, Crc32, Huffman, DeflateFixedTables.
These were the last 4 unreviewed DEFLATE spec files. With this PR, all
49 spec files in
Zip/Spec/have been audited at least once.
Track D1 (1 PR):
- #1339: DEFLATE decompression throughput benchmark (
ZipTest/DeflateBench.lean). Measures native vs FFI inflate/gzip/zlib decompression across 4 data patterns (constant, cyclic, random, text) and 4 sizes (1KB–1MB). Adds the first Track D infrastructure since the compression benchmark suite (#399).
Review campaign — project-wide completion:
All 49 spec files in Zip/Spec/ have now been audited at least once
across the review campaign (spanning ~30 review PRs from #369 to #1333).
The campaign covered:
- 43 DEFLATE spec files (checksums, bitstream, Huffman, LZ77, inflate, deflate encode/decode, dynamic trees, framing, roundtrip)
- 6 Zstd spec files (Fse, XxHash, ZstdHuffman, ZstdSequence, Zstd, ZstdFrame)
Quality metrics at campaign completion:
- 0 standalone bare
simpacross all spec files - 0 bare
simp_allacross DEFLATE spec files - 5 bare
simp_allremaining in Zstd.lean only (post-campaign additions from Track E completeness work) - Multiple files saw significant proof compression (Zstd.lean −10.3%, ZstdHuffman.lean −4.3%, Fse.lean −2.4%)
The review campaign reached diminishing returns — most files in the final batch required minimal or no changes, confirming convergence toward stable proof quality.
11-PR batch (Mar 12–13): WellFormedBlocks all-type induction + review campaign + benchmarks:
This batch generalized the WellFormedBlocks induction predicate to all block types, expanded block-level two-block succeeds coverage, continued the Zstd proof quality review campaign, and consolidated benchmark infrastructure.
Track E feature PRs (3):
- #1361:
WellFormedSimpleBlocksinduction predicate for raw/RLE N-block sequences - #1372:
WellFormedBlocksgeneralized to all 8 block types (raw, RLE, compressed_literals, compressed_sequences × last/non-last). Milestone: universal induction predicate for arbitrary block sequences. - #1382: Block-level two-block succeeds for raw/RLE + compressed_sequences combinations (closes #1232)
Proof quality reviews (3 PRs):
- #1359: Zstd.lean foundational sections audit — proof compression
- #1380: Zstd.lean step theorems + raw/RLE two-block audit
- #1383: Fse.lean proof quality audit — compressed proof patterns
Proof compression (1 PR):
- #1358: GzipCorrect + ZlibCorrect proof pattern compression
Infrastructure (3 PRs):
- #1356: Recovered skill updates from superseded PR #1351
- #1368: Meditate — two-block completion + review campaign transition
- #1377: Rebased ZstdFrame.lean review PRs (#1366 + #1371), net −149 lines
Track D (1 PR):
- #1357: Benchmark consolidation — merged NativeCompressBench + NativeScale into unified Benchmark.lean, added Zstd multi-iteration support
Bare simp_all in Zstd.lean: 5 → 2. Zstd spec lines: 15,091 → 15,811.
14-PR batch (Mar 13): proven-bounds campaign + Zstd splitting + WellFormedBlocks completion:
This batch launched the proven-bounds campaign (converting [i]! runtime
bounds checks to proven-bounds [i] access), split Spec/Zstd.lean into
three files, and completed the WellFormedBlocks/WellFormedSimpleBlocks
induction predicates at frame and API levels.
Proven-bounds campaign (5 PRs):
- #1414: Deflate.lean — encoding helpers + constant tables (22 patterns)
- #1417: DEFLATE length/distance tables — 4 patterns in
decodeHuffman.gowith@[simp]size lemmas for all 5 constant tables - #1426: ZstdHuffman.lean — 13
data[pos + i]!→data[pos + i]'(by omega)inparseCompressedLiteralsHeader - #1427: Merge conflict fix for ZstdFrame.lean proven-bounds PR #1413
- #1428: ZstdSequence.lean — data byte reads + history array access
Zstd spec splitting (1 PR):
- #1409: Extracted ZstdBase.lean (542 lines, L1: base predicates) and ZstdBlockLoop.lean (493 lines, L2: block loop specs) from the 6011-line Spec/Zstd.lean. Spec/Zstd.lean remains at 6011 lines — further splitting planned.
Track E WellFormedBlocks completion (3 PRs):
- #1393: API-level
WellFormedBlockssucceeds (full predicate) - #1398, #1401: Frame/API-level
WellFormedSimpleBlockssucceeds, including 3-block corollary (raw → RLE → raw)
Quality reviews (3 PRs):
- #1394: ZstdSequence.lean proof quality audit
- #1397: Zstd.lean file organization — splitting plan
- #1402: Zstd.lean proof quality audit (frame characterization + compressed
two-block) — eliminated 2 bare
simp_all
Self-improvement (1 PR):
- #1420: Meditate — created
proven-boundsskill, assessed Zstd splitting readiness
Proven-bounds campaign status: 149 ]! remaining across 10 native files.
Largest concentrations: Deflate.lean (53), ZstdSequence.lean (29), Fse.lean (18),
ZstdHuffman.lean (14), Gzip.lean (12), DeflateDynamic.lean (11). Files fully
converted: Crc32.lean, Adler32.lean, BitWriter.lean.
Remaining:
- Zstd.lean at 6011 lines needs further splitting (plan: #1432)
- Prove remaining sorry stubs: 4 in XxHash (UInt64 test vectors too expensive for kernel evaluation — intractable without native_decide)
- Proven-bounds campaign: 149
]!across 10 native files - Composed completeness: lift czs+czs and czs+cs to API level (19→21),
compose multi-block completeness to end-to-end
decompressZstd - Content preservation campaign: extend to N-block frames and compressed block content (with sequences)
- Spec-level decoder with correctness proofs (algorithmic correspondence between native and spec decoder, following the DEFLATE B3 pattern)
- Compressor + roundtrip proof
31-PR batch (Mar 13 – Apr 18): proven-bounds continuation + lean-zip-common/lean-zstd split + Lean 4.29.1 CI fixes (summarize #1511):
This batch is dominated by two strands: the proven-bounds campaign
(converting [i]! runtime bounds checks to statically-proven access)
and a structural split of the repository into three repos. See
progress/20260418T081059Z_10fac7ec.md for the full PR breakdown.
The structural change (#1487, 2026-03-27): lean-zip was split into
three repositories:
- lean-zip-common — shared utilities (Binary, Handle, BitReader, ZipForStd, io_ffi.c)
- lean-zstd — Zstandard decompression (13 spec files + FSE + XxHash + frame decoder + Huffman + sequences)
lean-zip(this repo) — zlib/DEFLATE/Gzip/ZIP/Tar with all specs and proofs
39 files were moved out; a require zipCommon from git pin replaced
them. Zip/Spec/ dropped from 51 files to 42.
Proven-bounds campaign (18 PRs): pre-split coverage in ZstdHuffman/ZstdSequence/Fse/BitReader/Binary (now in sibling repos) plus DEFLATE-side work in Deflate.lean (#1446 emitTokens, #1500 canonicalCodes/findTableCode/insertLoop), DeflateDynamic.lean (#1450, #1498), Inflate.lean (#1449, #1451), Tar.lean (#1459), and Gzip.lean (#1492). A cross-file consistency audit (#1431) kept conventions aligned.
Spec file splitting (5 PRs, pre-split): Spec/Zstd.lean and ZstdFrame.lean were modularized in-repo just before the #1487 split (#1438 ZstdTwoBlock extraction; #1460/#1466 splitting assessments; #1473 ZstdFrameBase extraction; #1479 three-way Spec/Zstd.lean split). The resulting files migrated to lean-zstd.
Other PRs (4): #1468 Track C1 fuel-dependent Zstd audit, #1491 closing stale Zstd PRs post-split, #1497 Track E ZIP local-header span validation, #1501 InflateCorrect + InflateComplete proof-quality audit, #1502 localized Array.set! monotonicity lemma.
Toolchain / CI (3 PRs): Lean 4.29.1 introduced a Lake "compiled
configuration invalid" regression on lake exe test. #1495 switched CI
to lake test; #1506 pinned zipCommon + added lake reconfigure in
CI; #1499 renamed call sites to upstream-compatible lemma names
(Nat.or_two_pow_eq_add_of_lt, List.getElem_foldl_set_*,
ByteArray.getElem!_push_lt) and pinned zipCommon to cleanup commit
89204d6.
Carry-over: 48 ]! remaining in Native/, all in two files —
Zip/Native/Deflate.lean (42, LZ77 matcher family: tracked in new
issues #1508/#1509/#1510 splitting into three clusters) and
Zip/Native/DeflateDynamic.lean (6, after PR #1513 merged mid-
summarize addressed 2 of the 8 from #1505). Three diag PRs (#1496,
#1503, #1507) remain open as instrumentation for the Lake/Lean 4.29.1
regression.
Quality metrics: 0 sorries; Zip/Spec/ at 42 files, 20,151 LOC
(DEFLATE-only — Zstd's ~16 kLOC of spec work migrated to lean-zstd);
Zip/Native/ at 7 files, 1,603 LOC. Toolchain: v4.29.1.
10-PR batch (Apr 18): proven-bounds campaign completion (summarize #1537):
This batch closes out the DEFLATE-side proven-bounds campaign carried
forward from the batch above. Eight feature PRs converted every
remaining ]! runtime-bounds access in the LZ77 matcher family of
Zip/Native/Deflate.lean (clusters A/B/C1/C2/C3 — spanning the
recursive and iterative variants of lz77Greedy and lz77Lazy plus
their where-local hash3/countMatch/trailing/mainLoop/
updateHashes helpers) and in the two remaining clusters of
Zip/Native/DeflateDynamic.lean (cluster A emitTokensWithCodes;
cluster B tokenFreqs.go). End-point: ]! in
Zip/Native/Deflate.lean 24 → 0 and Zip/Native/DeflateDynamic.lean
6 → 0 as runtime reads — the only remaining ]! in Deflate.lean
(2 occurrences) live inside comments, with no runtime-bounded
accesses left in the compressor.
Cluster pattern that landed. The matcher plumbing grew three
signature-level size invariants threaded from the top-level call down
through each where-local helper:
hht : hashTable.size = hashSize, hhv : hashValid.size = hashSize,
and hhs : 0 < hashSize for the hash-table reads;
h1 : p1 + maxLen ≤ data.size + h2 : p2 + maxLen ≤ data.size for
the countMatch byte-compare loops; and h : pos + 2 < data.size on
each local hash3. The iterative (lz77GreedyIter/lz77LazyIter,
PRs #1517 and #1529) and recursive (lz77Greedy/lz77Lazy,
PRs #1532 and #1524 + #1535) variants each required their own
size-preservation lemma pair —
lz77{Greedy,Lazy}_updateHashes_{fst,snd}_size, four in total —
because updateHashes is private to each where-block and cannot be
shared without a larger refactor. Dead-branch fallbacks for the outer
hht/hhv failure are byte-identical across the iterative and lazy
variants (emit one literal, advance pos + 1); the inner lookahead
guard in mainLoop_lazy takes the lookahead-dropped branch and
commits the already-found match. The emitTokensWithCodes dead
fallback recurses without emitting — ruled out by
nativeFindLengthCode_idx_bound + hlit_size, retained only to keep
the elaborator happy.
Skill-file deltas (#1530). Appended two pitfalls to
.claude/skills/proven-bounds/SKILL.md, bringing the numbered list
from 7 to 9. Pitfall #8 records that unless rebinds the threaded
mut state even when the body does not write to it, so bounds
captured before unless no longer apply to reads after; remedy is to
read all fields up front inside the if h : pos + N ≤ data.size
guard (pattern used in the Gzip decompressor rewrite). Pitfall #9
records that List.pmap (and other @[expose] combinators) cause
rfl-proved specs to unfold the skeleton simultaneously on each side
and blow the kernel recursion limit; remedy is a named-helper wrapper
(freqsToPairs in DeflateDynamic.lean).
Proof-quality review (#1536). Audited the six feature PRs landed
across the clusters. No cross-cluster renames rose to the "wrap it"
bar — hypothesis names (hht, hhv, hhs, hlit, hdist) overload
by scope in ways that are consistently scope-disambiguated.
getElem!_le_set!_incr was the sole repeated spec-cascade idiom that
justified a local helper lemma, and #1526 already introduced it. The
four size-preservation lemmas in Zip/Spec/DeflateFixedCorrect.lean
(lz77{Greedy,Lazy}_updateHashes_{fst,snd}_size) remain independent
rather than being collapsed, because lz77Greedy.updateHashes and
lz77Lazy.updateHashes are private to their respective where-blocks
— unification would require a separate refactor hoisting
updateHashes to top-level. Review landed doc-only (no cleanup
commit), per the issue's explicit opt-out.
Self-improvement (#1516). Post-proven-bounds-campaign meditate
captured the clustering heuristic that split the Deflate matcher work
into sub-issues #1508/#1509/#1510, and recorded the ]! distribution
across Zip/Native/ that scoped this batch.
Quality metrics: 0 sorries across Zip/;
Zip/Native/Deflate.lean and Zip/Native/DeflateDynamic.lean both at
0 runtime ]! reads (2 comment-only occurrences remain in the
former); Zip/Native/ at 7 files, 1,780 LOC (up from 1,603 —
+177 LOC from the threaded size hypotheses and dead-branch
fallbacks); Zip/Spec/ at 42 files, 20,516 LOC (up from 20,151 —
+365 LOC across DeflateFixedCorrect.lean, DeflateDynamicEmit.lean,
DeflateDynamicFreqs.lean, DeflateDynamicCorrect.lean,
LZ77NativeCorrect.lean); toolchain v4.29.1.
Track E carry-over. #1531 and #1533 (ZIP64 oversized-compressedSize / oversized-uncompressedSize malformed- fixture features) remain unclaimed Priority-0 work for the next planner.
11-PR batch (Apr 18 – Apr 21): Track E security-audit cluster (summarize #1563):
This batch is the first concentrated sweep of the Track E security-audit
sub-roadmap. No DEFLATE / Huffman / LZ77 code path changed; no spec file
was touched; grep -rc sorry Zip/ stayed at 0 throughout. The work is
entirely on the trust boundary — parser guards, malformed fixtures,
regression tests, and boundary documentation. The library's
cryptographic and compression kernels are unchanged; what moved is how
much of the attack surface is now explicitly guarded, tested, or
catalogued. The corresponding Track E checklist is
plans/track-e-current-audit-checklist.md;
this summary reads side-by-side with it rather than restating the full
matrix.
Priority 0 — ZIP unchecked-size + local-span audit (closed).
- #1543 — ZIP64 oversized-
compressedSizemalformed fixture (testdata/zip/malformed/oversized-zip64-compressed-size.zip, 134 B; firesassertSpanInFile's"local data span"branch with a claimed exabytecompressedSize). - #1544 — ZIP64 oversized-
uncompressedSizemalformed fixture (testdata/zip/malformed/oversized-zip64-uncompressed-size.zip, 134 B; the exabyte claim is caught by the post-read"size mismatch"guard rather than a span check — method-0 stored payload, sofileData.sizenever matchesentry.uncompressedSize). A later strict ZIP64 local-extra parse (landed in #1554) now rejects this fixture earlier withtruncated ZIP64 local extra field; the fixture's assert was updated to reflect the stricter check. - #1554 — Central-directory vs. local-header consistency audit.
Archive.readEntryDatanow parses the remaining LH fields (flags,method,crc,stdLocalCompSize,stdLocalUncompSize, ZIP64 local-extra block) and hard-errors on any CD/LH mismatch on method / compressedSize / uncompressedSize / crc, except when flag bit 3 (data-descriptor) legitimately leaves LH crc/sizes at zero. Covered bytestdata/zip/malformed/cd-lh-method-mismatch.zipandcd-lh-size-mismatch.zip(both deterministic, built byscripts/build-cd-lh-mismatch.py).parseZip64Extrais reused for the LH case; a follow-up could threadEntry.flagsfor a bit-11 UTF-8 check.
Priority 1 — Tar partial-decoder audit (closed).
- #1545 — Malformed PAX extended-header fixtures
(
testdata/tar/malformed/pax-oversized-length.tar,pax-truncated-record.tar,pax-invalid-utf8-key.tar,pax-invalid-utf8-value.tar,pax-inconsistent-length.tar— each 2048 B; built deterministically byscripts/build-pax-malformed-fixtures.lean). Every malformation was silently skipped by existingparsePaxRecordsguards; no hardening needed, but the regression pins behaviour. - #1546 — Malformed GNU long-name / long-link fixtures
(
testdata/tar/malformed/gnu-longname-truncated.tar,gnu-longname-no-terminator.tar,gnu-longname-invalid-utf8.tar,gnu-longlink-truncated.tar; built byscripts/build-gnu-long-malformed-fixtures.lean). Invalid UTF-8 in long-names falls throughString.fromUTF8?to a Latin-1 fallback, which is now pinned by test rather than left implicit. - #1550 —
String.fromUTF8!callsite audit inZip/Tar.lean. Three panicking raw-byte truncations inbuildPaxEntryandcreatereplaced byTar.truncateUTF8(codepoint-aware); the two remainingfromUTF8!callsites insplitPathnow carry an explanatory comment (split is at an ASCII'/'byte, safe by construction). Regression coverage in new test moduleZipTest/TarPathTruncation.lean. - #1555 — Symlink / hardlink extraction policy documented explicitly.
Per-typeflag policy is now in both the
Tar.extractdocstring andSECURITY_INVENTORY.md's new "Symlink/hardlink extraction policy" subsection:typeRegular/typeDirectorygo throughBinary.isPathSafe;typeSymlinkvalidateslinknamefor absolute / backslash /..components beforecreateSymlink;typeHardlink(now a named constant,0x31) and all other typeflags (devices, FIFO, GNU sparse) silently skip — the payload is consumed but no filesystem entry is created. New fixturestestdata/tar/security/symlink-absolute.tarandhardlink-outside.tarplus the previously orphanedbackslash-slip.tarnow have asserts inZipTest/TarFixtures.lean; built byscripts/build-symlink-hardlink-malformed-fixtures.lean.
Priority 2 — Public decompression limit policy (items 1–3 closed, item 4 open).
- #1556 — Public decompression limit policy inventory. Added a new
"Decompression Limit Inventory" section to
SECURITY_INVENTORY.mdcataloguing every public decompression / extraction API (16 entry points across FFI + native + archive + tar) with Entry point / Parameter / Default / Semantics of 0 / Notes columns. Flagged four known inconsistencies (FFI whole-buffer vs. streaming cap defaults;Archive.readEntryDatasilent0 → 256 MiBupgrade on the native backend; native-decoder 1 GiB vs. 256 MiB default disagreement;maxCentralDirSizefinite butmaxEntrySizeunlimited mixed semantics) and proposed a six-point "Recommended policy" with placeholder numbers (256 MiB FFI whole-buffer, 1 GiB archive per-entry, streaming FFI gains a cap, native-decoder defaults normalised). NoZip/*.leanchanges — inventory + policy proposal only. - #1560 — Bomb-limit regression tests for
Gzip.decompress,RawDeflate.decompress, andZip.Native.GzipDecode.decompress(added toZipTest/Gzip.lean,ZipTest/RawDeflate.lean,ZipTest/NativeGzip.lean). Each test compresses ~6200 B ofmkTestData, then calls the decoder withmaxDecompressedSize := 10(ormaxOutputSize := 10for native) and asserts the error substring ("exceeds limit"for FFI,"exceeds maximum size"for native — the substrings diverge because the native path errors originate inZip/Native/Inflate.lean, not in the C code). - #1561 — Bomb-limit regression tests for
Archive.extract,Archive.extractFile,Tar.extract, andTar.extractTarGzNative(added toZipTest/Archive.leanandZipTest/Tar.lean). Each test builds the bomb in-memory viamkTestData+Tar.create/Gzip.compressrather than committing new fixtures. TheTartest uses(maxEntrySize := 0) (maxOutputSize := 10)and asserts"exceeds maximum size"— the gzip-decode budget fails before per-entry limits are reached. - #1562 — Reconcile audit checklist +
SECURITY_INVENTORY.mdmissing-work lists. Checklist line for each of the six items closed above now carries its closing PR number.SECURITY_INVENTORY.md's ZIP / Tar "Missing work" lists shrunk to genuinely-open items (bounded-read lemmas on the ZIP side, none on the tar side); completed items moved to a new per-subsystem "Recent wins" bullet list referencing the closing PRs. Documentation-only — no code changes.
Test-fixture inventory added in this batch. New fixtures landed in
three families, all built by committed deterministic builders
(scripts/build-*) so they can be regenerated byte-stable:
testdata/zip/malformed/— 4 new:oversized-zip64-compressed-size.zip,oversized-zip64-uncompressed-size.zip,cd-lh-method-mismatch.zip,cd-lh-size-mismatch.zip(count 11 after this batch, up from 7).testdata/tar/malformed/— 9 new: 5pax-*.tar+ 4gnu-longname/longlink-*.tar(count 12, up from 3).testdata/tar/security/— 2 new files (symlink-absolute.tar,hardlink-outside.tar) and the previously-orphanedbackslash-slip.tarnow exercised rather than committed-but-unused (count 6, up from 4; the other three aretar-slip.tar,tar-absolute.tar,symlink-slip.tar).
Scope discipline. Every PR in this batch deliberately stopped at
the doc / fixture / test boundary. Where Zip/*.lean did change —
#1550 (Tar.truncateUTF8 + ZipTest/TarPathTruncation.lean), #1554
(Archive.readEntryData LH consistency) — the changes were new
guards + regression fixtures; nothing was removed or weakened, and
no proof / spec file was touched. SECURITY_INVENTORY.md "Recommended
policy" numbers are deliberately flagged as seed values for follow-up
issues, not decisions. The library's proof corpus (42 spec files,
20,516 LOC, 0 sorries) is byte-identical to the pre-batch tree.
Quality metrics: 0 sorries across Zip/; 0 runtime ]!; Zip/Spec/
at 42 files, 20,516 LOC (unchanged); Zip/Native/ at 7 files, 1,780
LOC (unchanged); Zip/ (FFI / archive / tar / gzip / basic) at 6
files, 1,437 LOC (+ Archive.lean grew from readEntryData LH
parse + guards; Tar.lean grew from truncateUTF8 + docstring);
ZipTest/ at 22 files (+1 from TarPathTruncation.lean); toolchain
v4.29.1.
Track E open work (post-batch). P2 item 4 (docstring + error-message
policy), all of P3 (FFI adversarial validation: sanitizer harness,
truncated / concatenated-gzip / repeated-inflateReset / near-limit
regression tests, fuzz harness), P4 (runtime-boundary docs), and P5
(proof-friendly guard lemmas for bounded reads). Planner has already
queued the next wave: #1558 / #1559 / #1564 / #1565 / #1566 track the
remaining Priority 2–3 items.
15-PR batch (Apr 22): Track E security-audit follow-ups (summarize #1588):
This batch is the second concentrated wave of the Track E security-audit
sub-roadmap. It builds on the 11-PR cluster captured in the prior block
(summarize #1563 / PR #1568) by closing every remaining audit-checklist
P-item except P3.3 (fuzz harness) and P5.* (proof-friendly guard
lemmas). No DEFLATE / Huffman / LZ77 code path changed; no spec file
was touched; grep -rc sorry Zip/ stayed at 0 throughout. All work
sits on the trust boundary — limit-policy docstrings, FFI regression
tests, sanitizer harness, allocation-site audit, and three new
SECURITY_INVENTORY.md sub-sections (upstream-runtime tracking,
minimized reproducer corpus, and local guard inventory). The library's
cryptographic and compression kernels are unchanged; what moved is
how much of the audit checklist is now formally documented and locally
guarded. The corresponding checklist file is
plans/track-e-current-audit-checklist.md
(source of truth for checkbox state).
Priority 2 item 4 — Limit-policy docstrings (closed).
- #1573 — Explicit limit-policy docstrings for FFI whole-buffer +
streaming decompression (
Zip/Basic.lean,Zip/Gzip.lean,Zip/RawDeflate.lean). Documents per-API the0 = no limit/Some n/ default-cap semantics surfaced by #1556's inventory. - #1586 — Public-API limit-policy docstrings for
Archive.extract,Archive.extractFile,Tar.extract,Tar.extractTarGzNative(Zip/Archive.lean,Zip/Tar.lean). - #1594 — P2.4 final wedge: native-decoder limit-policy docstrings
for
Zip/Native/Inflate.lean(maxOutputSize) andZip/Native/Gzip.lean(maxDecompressedSize).
Priority 3 — FFI adversarial validation (P3.1 / P3.2 / P3.4 closed; P3.3 open).
- #1576 — P3.1: dedicated sanitizer script
scripts/sanitize-ffi.shrebuildsc/zlib_ffi.cunder-fsanitize=address,undefined, explicitly links GCC's libasan / libubsan past Lean's bundled clang, and runs the test suite withLD_PRELOADso ASan initialises first. The April 2026 tree is ASan + UBSan clean. - #1571 — P3.2 first tranche: truncated-stream regression tests for
Zlib,Gzip, andRawDeflateFFI entry points (added toZipTest/Zlib.lean,ZipTest/Gzip.lean,ZipTest/RawDeflate.lean). - #1572 — P3.2 second tranche: concatenated-gzip-member + zero-length-chunk regression tests (added to the same FFI test modules).
- #1577 — P3.2 third tranche: repeated-
inflateResetacross concatenated gzip members + exact-fit and n−1 near-limit-output regression tests. - #1580 — P3.4:
c/zlib_ffi.cmalloc/realloc/buffer-growth audit. New "Allocation site audit (c/zlib_ffi.c)" sub-section inSECURITY_INVENTORY.mdenumerates every allocation call-site (kind / overflow guard / failure mode);scripts/check-c-allocations.shflags accidental new sites at PR-review time against the recorded baseline.
Priority 4 — Trusted runtime boundary documentation (P4.1 / P4.2 / P4.3 closed).
- #1585 — P4.3: per-call-site local-guard inventory for
Handle.readandStream.readcallsites inZip/Archive.leanandZip/Tar.lean. New "Local guard inventory forHandle.readandStream.read" sub-section inSECURITY_INVENTORY.mdrecords eachreadExact,readEntryData,skipEntryData, and inline-loop callsite together with the bound it relies on (claimed-vs-actual span,assertSpanInFileenclosure, etc.). - #1589 — P4.1: upstream-runtime tracking sub-block in
SECURITY_INVENTORY.md§ "Lean Runtime". Records the ZIP64 oversized-size / CD-vs-LH attack surface, pins the upstream status as "no upstream link yet — local tracking only" (dated 2026-04-22), and lists the local regression coverage (#1543 / #1544 / #1554 / #1560 / #1561) that guards the surface today. - #1590 — P4.2: "Minimized Reproducer Corpus" section in
SECURITY_INVENTORY.mdtabulating all 29 fixtures undertestdata/zip/malformed/,testdata/tar/malformed/, andtestdata/tar/security/with the guard each exercises, the first-landing PR (or481e562for fixtures inherited from the initiallean-zlib → lean-zipimport), and a{oversized allocation, partial-decoder panic, archive-slip, decompression bomb, other}class tag.
Housekeeping (4 PRs).
- #1569 — Meditate session: patterns and friction in the Track E security-audit phase (drafted skill notes; the materialised SKILL files landed in #1579).
- #1579 — Materialise the skill drafts from #1569 as
.claude/skills/<skill>/SKILL.mdfiles. - #1584 — Doc: progress entry for #1581 rebase fix.
- #1592 — Doc: progress entry for #1590 rebase fix.
Track E open work (post-batch). Two checklist items remain:
- P3.3 — fuzz harness for whole-buffer and streaming inflate entry points. Tracking issue: #1595 (unclaimed at the time of writing).
- P5.1 — proof-friendly helpers for bounded reads and validated
spans in
Zip/Archive.lean/Zip/Tar.lean. Tracking issue: #1596 (unclaimed at the time of writing). - P5.2 / P5.3 — bounded-span lemmas + helper-adoption rollout; no tracking issue yet, downstream of #1596.
A bonus boundary-hardening PR is in flight outside the checklist:
PR #1597 / issue #1593 caps Tar readEntryData reads at the
GNU-long-name and PAX extended-header callsites (open at the time
this summary lands; not counted in the 15-PR batch above).
Scope discipline. Same rule as the prior batch: every PR in this
wave stopped at the doc / fixture / test boundary, plus narrow
boundary-layer edits in Zip/Native/Inflate.lean,
Zip/Native/Gzip.lean, Zip/Basic.lean, Zip/Gzip.lean,
Zip/RawDeflate.lean, Zip/Archive.lean, and Zip/Tar.lean for
the limit-policy docstring PRs (#1573 / #1586 / #1594) — none of
these touched a logic guard or a spec file. The library's proof
corpus (42 spec files, 20,516 LOC, 0 sorries) is byte-identical to
the pre-batch tree.
Quality metrics: 0 sorries across Zip/; 0 runtime ]!;
Zip/Spec/ at 42 files, 20,516 LOC (unchanged); Zip/Native/ at
7 files, 1,813 LOC (was 1,780; +33 LOC from limit-policy
docstrings in Zip/Native/Inflate.lean and Zip/Native/Gzip.lean);
Zip/ (FFI / archive / tar / gzip / basic) at 6 files, 1,511 LOC
(was 1,437; +74 LOC across the four FFI modules + Archive.lean +
Tar.lean for limit-policy docstrings); ZipTest/ at 22 files,
unchanged (P3.2 added test cases inside existing modules);
testdata/ fixture counts unchanged (11 / 12 / 6 across
zip/malformed, tar/malformed, tar/security); 16 new
progress entries in this batch; toolchain v4.29.1.
12-PR batch (Apr 22): Track E audit-completion wave (summarize #1629):
This batch is the third concentrated Track E wave. It finishes off
the checklist items that were still open after summarize #1598 —
P3.3 (fuzz harness) lands as #1602 and P5.1 (proof-friendly
bounded-read helpers) lands as #1608 — and executes three of the
five entries in the SECURITY_INVENTORY.md "Recommended policy"
block (Rec. 1, 3, 5). A fourth recommendation (Rec. 2, streaming FFI
default-flip) is set up structurally by #1610, which adds the
maxDecompressedSize parameter to the three streaming FFI decoders
but leaves the default at 0 (half-closed, no policy flip). As in
the prior two Track E waves, no DEFLATE / Huffman / LZ77 logic
changed; no spec file was touched; grep -rc sorry Zip/ stayed at
0 throughout. The window runs from 04:18Z (#1600 meditate) to
07:35Z (#1623 Rec. 1 default-flip) on 2026-04-22. The audit
checklist source of truth remains
plans/track-e-current-audit-checklist.md.
Track E feature work (7 PRs), grouped by SECURITY_INVENTORY.md
recommendation or checklist item:
- Rec. 1 — whole-buffer FFI default → 1 GiB. #1623 flips
Zip.Basic.decompress,Zip.Gzip.decompress, andZip.RawDeflate.decompressfrommaxDecompressedSize = 0(no limit) to1073741824(1 GiB). Per-decoder bomb-limit regression tests added toZipTest/Zlib.lean,ZipTest/Gzip.lean, andZipTest/RawDeflate.lean. - Rec. 3 — per-entry archive extractor default → 1 GiB. #1618
flips
Archive.extract,Archive.extractFile,Tar.extract,Tar.extractTarGz, andTar.extractTarGzNativefrommaxEntrySize = 0to1073741824. Fast-followed by the rebase fix #1620 (superseded; its progress entry landed as the ride-along #1622). - Rec. 5 — native-side uniformity at 1 GiB. #1617 lifts the
native
Zip.Native.Inflate.inflate,Zip.Native.GzipDecode.decompress,Zip.Native.ZlibDecode.decompress, andZip.Native.ZlibDecode.decompressAutodefaultmaxOutputSizefrom the previous non-uniform mix to a single 1 GiB cap matched with the FFI side. - Streaming FFI
maxDecompressedSizeparameter (not a default flip). #1610 adds the parameter toGzip.decompressStream,Gzip.decompressFile, andRawDeflate.decompressStream. The default stays at0(no limit) in this PR — it half-closes theSECURITY_INVENTORY.mdRec. 2 gap by making the cap expressible; the default flip is a separate follow-up, filed as issue #1625. - P3.3 — fuzz harness for whole-buffer and streaming inflate
entry points. #1602 adds
ZipTest/FuzzInflate.lean, a randomized harness that exercises both FFI and native backends across random seeds; harness quality was audited out-of-band by review #1607. - P5.1 — proof-friendly helpers for bounded reads and validated
spans. #1608 lands four helpers —
readBoundedSpanFromHandle,readBoundedExactFromHandle,readBoundedEntryData, andreadBoundedExactFromStream— plusZipTest/BoundedReadTest.leanexercising each. #1608 deliberately left its checklist box unticked; the box is the concern of the follow-up P5.3 caller migration. - Inventory-drift detector (tooling, not a recommendation).
#1612 lands
scripts/check-inventory-links.sh, a line-number / fixture-path drift checker forSECURITY_INVENTORY.mdthat fires at PR review time.
Review rounds (3 PRs). All three are doc-only and pair with a specific feature PR:
- #1604 — standalone review of
SECURITY_INVENTORY.md/ audit-checklist reference drift (pairs with no single feature PR; fixes line-number drift and cross-references). - #1607 — quality audit of #1602 (fuzz harness) before Track E's 7-day exit-criterion window closes.
- #1616 — quality audit of #1610 (streaming FFI
maxDecompressedSizeparameter).
Infrastructure (2 PRs).
- #1600 — meditate session that catalogued the patterns and friction of the prior Track E wave (~15 PRs since #1569). Drafted skill notes in its progress entry; the SKILL.md file edits themselves were either part of the prior wave (#1579) or reserved for a later meditate.
- #1622 — ride-along progress entry for the #1620 rebase fix of
PR #1618 (per-entry
maxEntrySizedefault flip). Doc-only.
Remaining gaps (as of batch close, 07:35Z). Per the issue body, three Track E items remained outside this batch:
- Rec. 2 — streaming FFI default-flip. Tracked as issue #1625 at summarize-issue filing; structurally set up by #1610.
- Rec. 4 — whole-archive
maxTotalSizecap. Tracked as issue #1621 at summarize-issue filing; not started in this batch. - P5.2 — validated-span / bounded-read lemma proofs over the P5.1 helpers; filed fresh mid-batch as issue #1628.
- P5.3 — caller migrations in
Zip/Archive.lean/Zip/Tar.leanto consume the P5.1 helpers. The issue body flagged PR #1626 as "in flight at summarize time"; in reality #1626 had merged at 07:48:16Z, ~2 minutes before the summarize issue was filed (07:50Z). It is out of the 12-PR batch by issue scope, but the P5.3 checklist box is closed at the time this summary lands. Since batch close, the three remaining gaps above have also landed (#1630 Rec. 4 at 08:10Z, #1631 Rec. 2 at 08:28Z, #1636 P5.2 at 09:17Z); they belong to the next summarize batch.
Scope discipline. Same rule as the prior two Track E waves: the
only Zip/*.lean edits in the batch are narrow parameter-addition
and default-flip changes on FFI / native / extractor entry points
(#1610 streaming-FFI parameter; #1617 native-side default unification;
#1618 extractor maxEntrySize; #1623 FFI whole-buffer
maxDecompressedSize), plus the new P5.1 helper family in
Zip/Archive.lean and Zip/Tar.lean (#1608). No spec file was
touched. No proof was changed. The library's proof corpus (42
spec files, 20,606 LOC, 0 sorries) remains intact; the only spec
LOC movement (20,516 → 20,606) is from a spec file not in this
batch's window, inherited post-#1598. The ]! count is still 0
and Zip/Native/ retains 0 runtime bounds assertions.
Quality metrics: 0 sorries across Zip/ (unchanged; the issue
body's "4 → 4 XxHash" baseline claim is stale — Zip/Spec/XxHash.lean
does not exist in this repository, and grep -rc sorry Zip/ sums
to 0); 0 runtime ]! (unchanged); Zip/Spec/ at 42 files, 20,606
LOC (+90 LOC relative to prior summarize; not attributable to a PR
in this batch); Zip/Native/ at 7 files, 1,852 LOC (was 1,813;
+39 LOC from #1617 default-unification docstrings + #1608 helper
support); Zip/ (FFI / archive / tar / gzip / basic / checksum) at
6 files, 1,833 LOC (was 1,511; +322 LOC from #1608 bounded-read
helper family landing in Zip/Archive.lean + Zip/Tar.lean, plus
the streaming-FFI parameter in #1610 and the default-flip docstring
updates in #1617 / #1618 / #1623; Zip/Checksum.lean is a new
sixth file present in the file list but inherited from earlier);
ZipTest/ at 24 files (+2 — ZipTest/FuzzInflate.lean from #1602
and ZipTest/BoundedReadTest.lean from #1608); testdata/ fixture
counts 11 / 14 / 6 (the +2 in testdata/tar/malformed/ is from
pre-batch PR #1597 — it merged 03:43Z but was outside the prior
summarize's stated scope); toolchain v4.29.1.
16-PR batch (Apr 22–24): Track E CD/EOCD and CD/LH dimension closure (summarize #1765):
Sixteen PRs merged across three threads in the window between
summarize #1721 (merge commit 8766ba9, 23:26Z 2026-04-22) and
PR #1761 (merge commit b1d38d5, 05:52Z 2026-04-24). No spec
file touched; grep -rc sorry Zip/ stayed at 0.
Track E feature PRs (8). #1728 (CD/LH uncompsize + crc fixtures,
closing the per-entry sextet), #1733 (EOCD totalEntries
consistency), #1736 (CD/LH versionNeededToExtract one-sided
LH ≤ CD), #1742 (EOCD disk-number — both fields == 0 post-ZIP64-
override), #1752 (EOCD-internal numEntriesThisDisk vs.
totalEntries; extends findEndOfCentralDir tuple 6 → 7), #1754
(ZIP64/standard-EOCD override sentinel, six-field "sentinel ∨
numeric match" check; lifts findEndOfCentralDir's outer
container from Option to IO (Option …)), #1759 (per-entry CD
diskNumberStart §4.4.11 sibling of #1742), #1761 (ZIP64 EOCD64
self-declared size of record field sanity == 44). Five
archive-level EOCD dimensions landed this wave; coverage 1 / 9
→ 6 / 9 closed. The SECURITY_INVENTORY.md CD-vs-EOCD Missing
work bullet (line 202) is now down to comment-length only
(tracked by in-flight repair PR #1743 / issue #1739).
Paired-review PRs (6). #1741 (reviews #1728), #1747 (reviews
#1733), #1748 (reviews #1736), #1753 (reviews #1742), #1760
(reviews #1752). #1737 is a standalone post-ladder proof-quality
audit of Zip/Native/Crc32.lean (−5 LOC, zero statement change),
not paired with an in-wave feature PR. Median paired-review
latency for the five feature pairs: 51 min (inside the meditate
#1651 §4 ≤ 1-hour target; the #1728 → #1741 pair at 228 min is
the sole outlier, crossing the overnight window between the
opening housekeeping PRs and the main wave's 03:11Z
resumption).
Housekeeping / infra (2). #1724 names 0xFFF7 as the private
constant dataDescriptorBitMask in Zip/Archive.lean (no
behavior change; readability refactor of the CD/LH flags check
added by #1715). #1727 sweeps SECURITY_INVENTORY.md's CD/LH
region to reconcile the throw-message-line vs. unless-line
citation convention.
Scope discipline. The only source changes this wave touched
Zip/Archive.lean (+147 LOC from the five archive-level checks
and #1759's per-entry check; #1724 refactor is LOC-neutral) and
Zip/Native/Crc32.lean (−5 LOC from #1737). Zip/Spec/ stayed
at 42 × 21,067 LOC with zero edits. ZipTest/ZipFixtures.lean
grew +219 LOC from nine new fixture/assert blocks. Fixtures in
testdata/zip/malformed/ grew from 12 to 21 (nine new); tar
fixture counts (14 / 6) unchanged.
Quality metrics: 0 sorries across Zip/ (unchanged); 0 runtime
]! across Zip/Native/ and Zip/*.lean (unchanged);
bash scripts/check-inventory-links.sh → errors=0, warnings=34
(34 line-anchor warnings inherited from the five-Archive-edit
stack; the fixture-path and cross-reference gates stay clean).
12-PR batch (Apr 24): ZIP64 extra-field layout-smuggling closure + Track E CD-parse cadence (summarize #1797):
Twelve PRs merged in the ~5-hour window between summarize #1770
(merge commit 3679495, 06:23Z 2026-04-24) and PR #1796 (merge
commit 25c4318, 11:16Z 2026-04-24). No spec file touched;
grep -rc sorry Zip/ stayed at 0 throughout.
Track E feature PRs (4), grouped by closed attack-class cluster.
-
CD-parse-time intra-entry invariant (1 PR). #1773 adds the method 0 (stored)
compressedSize == uncompressedSizetautological invariant toparseCentralDir. APPNOTE §4.4.5 defines method 0 as "no compression", so the equality is a tautology; crafted archives with mismatched sizes are malformed.Archive.listnow rejects them at CD-parse time, before the late post-decode"size mismatch"guard at Zip/Archive.lean:811. Complements the CD/LHuncompressedSizeconsistency check landed earlier (cd-lh-uncompsize-mismatch.zip): that caught CD-vs-LH skew, this catches intra-CD invariant violation with no CD/LH divergence. Fixture:cd-stored-size-mismatch.zip.SECURITY_INVENTORY.mdRecent wins (:343-358). -
ZIP64 extra-field layout-smuggling (3 PRs). Three dimensions of the APPNOTE §4.5 CD/LH extra-data smuggling class closed together at the CD/LH boundary:
- #1785 — inner-0x0001 block
dataSize == 8 * Nexactness check inparseZip64Extra, rejecting CD entries whose ZIP64 (headerId 0x0001) extra-fielddataSizeexceeds the8 * Nbytes consumed by theNsentinel-gated 32-bit standard fields (APPNOTE §4.5.3). Trailing slack past the consumed prefix is attacker-controllable and a parser-differential smuggling vector. Fixture:zip64-extra-oversized-datasize.zip.SECURITY_INVENTORY.mdRecent wins (:281-289). - #1788 — outer sub-field structural walk
validateExtraFieldStructure, invoked unconditionally on the blob at the CD site (parseCentralDir) and the LH site (readEntryData) before any sentinel guard. Pre-PR, the outer iteration lived only insideparseZip64Extra, whichbreaks silently on a malformed sub-field; the caller skippedparseZip64Extraentirely when no ZIP64 sentinel was set, so the anomaly was invisible in the no-sentinel case. Fixture:cd-extra-overrun-datasize.zip.SECURITY_INVENTORY.mdRecent wins (:262-280). - #1793 — duplicate-0x0001 block rejection via a new
hasDuplicateZip64Extrahelper that walks the TLV structure once, invoked at both the CD-side caller (parseCentralDir) and the LH-side caller (readEntryData) beforeparseZip64Extra. APPNOTE §4.5 forbids more than one instance of any registered header ID per entry; for ZIP64 in particular the layout of each block depends on which standard 32-bit fields are at the0xFFFFFFFFsentinel, so two blocks with different payloads make the resolved sizes/offset ambiguous (first-wins vs. last-wins parser-differential). Two error wordings ("duplicate ZIP64 extra field"vs"duplicate ZIP64 local extra field") keep CD/LH attribution distinct. Fixtures:cd-zip64-extra-duplicate.zip,lh-zip64-extra-duplicate.zip.SECURITY_INVENTORY.mdRecent wins (:296-323).
Together these three PRs close the ZIP64 extra-field layout- smuggling attack class at the CD/LH boundary (three dimensions: inner block self-length exactness, outer TLV structural soundness, uniqueness of the 0x0001 block).
- #1785 — inner-0x0001 block
Paired-review PRs (6). #1772 (reviews #1754), #1776 (reviews #1759), #1779 (reviews #1761), #1783 (reviews #1769), #1784 (reviews #1773), #1792 (reviews #1785). Four of the six (#1772/#1776/#1779/#1783) pair with feature PRs from the prior #1770 wave — they close out the post-wave review queue inherited across the summarize boundary. Two (#1784/#1792) pair with in-wave features: #1773 → #1784 at ~206 min and #1785 → #1792 at ~34 min — both inside the 30 min to several hours bracket noted in the Track E post-#1773 cadence entry progress/20260424T105643Z_c4046753-paired-review-1785.md §F. The remaining two in-wave feature PRs (#1788 and #1793) have no paired-review entry at batch close — they are the next planner's backlog, inherited across this summarize boundary just as the prior wave's #1754/#1759/#1761/#1769 pairs were.
Doc / chore (2).
- #1794 (doc) — fixes the PR-number attribution in three
ZipTest/ZipFixtures.leanprecedence-shift comments, replacing#1770with the correct#1773authorship. Follow-up to the prior-wave paired-review PR #1778 §F.2 attribution-drift finding. - #1796 (chore) — extends
scripts/check-inventory-links.shwith a placeholder-PR linter rule that fires on any(this PR)/(PR #TBD)text in theSECURITY_INVENTORY.mdRecent wins / Minimized Reproducer Corpus blocks, and sweeps the outstanding placeholders in the ZIP64 extra-field Recent wins block so they reference committed#NNNNvalues. Closes #1786.
Scope discipline. Source-code changes this wave touched only
Zip/Archive.lean (+87 net LOC: +89/−2 from four CD-parse checks
— one stored-method invariant, three ZIP64 extra-field
dimensions) and ZipTest/ZipFixtures.lean (+130 net LOC:
+135/−5 from five new fixture/assert blocks plus the #1794
comment fixes). Zip/Spec/ stayed at 42 × 21,067 LOC with zero
edits, matching the scope discipline of the prior two Track E
waves.
Quality metrics: 0 sorries across Zip/ (unchanged — none of
these PRs touched proofs); 0 runtime ]! across Zip/Native/
and Zip/*.lean (unchanged); bash scripts/check-inventory-links.sh
→ errors=0, warnings=0 post-#1796 sweep (the placeholder-PR
linter now fires if any (this PR) / (PR #TBD) text reappears,
and all inherited line-anchor warnings have been reconciled).
Fixtures in testdata/zip/malformed/ grew from 22 to 27 (five
new: cd-stored-size-mismatch.zip,
zip64-extra-oversized-datasize.zip,
cd-extra-overrun-datasize.zip,
cd-zip64-extra-duplicate.zip,
lh-zip64-extra-duplicate.zip). Tar fixture counts (14 / 6)
unchanged. Type mix across the 12 PRs: feature 4 / paired-review
6 / doc 1 / chore 1. Toolchain v4.29.1.
12-PR batch (Apr 24): Track E CD-parse defensive-check cadence + EOCD64 upper-bound sibling (summarize #1839):
Twelve PRs merged in the ~4.5-hour window between summarize #1803
(merge commit 6ed456d, 11:48:25Z 2026-04-24) and PR #1836 (merge
commit 41bf587, 16:17:05Z 2026-04-24). No spec file touched;
grep -rc sorry Zip/ stayed at 0 throughout.
Track E feature PRs (6). Each PR closes an independent CD-parse- time defensive dimension on the CD entry or the ZIP64 EOCD64 record; no two dimensions share a field.
- CD
versionNeededToExtract > 45upper bound (#1807). Rejects CD entries claiming aversionNeededToExtractgreater than45(the APPNOTE 6.3 ZIP64 ceiling) atparseCentralDir(Zip/Archive.lean:546), before the existing CD-vs-LH one-sided downgrade check (PR #1736) can be bypassed by both sides claiming an unsupported value. Fixture:cd-version-needed-too-high.zip. Distinct error substring ("unsupported versionNeededToExtract") from the later CD/LH downgrade check ("LH versionNeededToExtract"at :889); the.claude/skills/error-wording-catalogue/SKILL.mdentries for both live side-by-side so future tests unambiguously pick the upper-bound vs. the downgrade message. - Per-entry
localOffset + 30 ≤ cdOffsetarchive-layout invariant (#1813). Per-entry micro-counterpart of the in-flight archive-level macrocdOffset + cdSize ≤ eocdPosguard (issue #1799 / PR #1809). APPNOTE §4.3.6 pins the archive layout as[LH+data]* [CD] [EOCD], so every entry's LH must be readable strictly before the CD start. Pre-PR,Archive.listhad no gate at all — only the extract path's late LH-signature check caught a subset of the construction. Fires at Zip/Archive.lean:665 post-ZIP64-resolution so the resolvedUInt64localOffis checked, not the0xFFFFFFFFsentinel; uses asymmetricSpanInFile-shaped subtraction to avoidUInt64wrap on crafted very-large values. Fixture:cd-entry-localoffset-past-cdstart.zip.SECURITY_INVENTORY.mdRecent wins (:414-442). internalFileAttributesreserved-bits guard (#1819). Rejects CD entries with any bit other than bit 0 ("apparent ASCII/text data") set in APPNOTE §4.4.10internalFileAttributes(CD +36, UInt16). MaskinternalAttrs &&& 0xFFFE == 0preserves Info-ZIP text-flag interop (spot-check overtestdata/zip/interop/:go-unix.zip,go-test.zip,go-crc32-not-streamed.zipset bit 0 on apparent-text files; the remaining interop fixtures use0x0000) while rejecting smuggled reserved-bit values. Fires at Zip/Archive.lean:537 pre-ZIP64-resolution. Contiguous writer-zeroUInt16sibling of PR #1759 (CD +34diskNumberStart): the three writer-zero CD fields+34 → +36 → EOCD.numEntriesThisDisknow fire their guards in the order the fields appear. Fixture:cd-entry-internal-attrs-reserved.zip.SECURITY_INVENTORY.mdRecent wins (:443-473).- CD flag bit-5 (compressed patched data) rejection (#1824).
Rejects CD entries whose APPNOTE §4.4.4 general-purpose flag
bit 5 (PKWARE's proprietary compressed-patched-data format §4.6)
is set at
parseCentralDir(Zip/Archive.lean:619), pre-ZIP64-resolution. lean-zip implements neither creation nor extraction of the format; the writer emitsflags = 0x0800(bit 11 UTF-8 names) only. Third independent GPF bit dimension closed; siblings #1818 (bit 4 enhanced deflating) and #1822 (bits 7-10/12/14/15 reserved/unused) are in flight at batch close. Once those land, the GPF word is fully validated except bits 1-3 (deflate/data-descriptor) and bit 11 (UTF-8), which are legitimate. Mask-equality form (flags &&& 0x0020 == 0) matches the0xFFF7bit-3-masking convention inreadEntryData. Fixture:cd-patched-data-flag.zip.SECURITY_INVENTORY.mdRecent wins (:474-499). - ZIP64 EOCD64
versionMadeByspec-version upper bound (#1826). Rejects EOCD64 records whoseversionMadeByfield (APPNOTE §4.4.2.2, atbufPos + 12) carries a lower byte greater than63(spec version 6.3) atfindEndOfCentralDir(Zip/Archive.lean:337). Only the lower byte is checked — real archives vary widely in host-OS code (upper byte: Info-ZIP emits3, Windows producers11NTFS, etc.); the lower byte is a pure spec-version field with a well-defined APPNOTE maximum. Interop sweep:testdata/zip/interop/go-zip64.zip— the only interop fixture with an EOCD64 — hasversionMadeBy=0x002d(low byte45), comfortably below the bound. Archive-level counterpart to the per-entry CDversionMadeBy > 63guard (issue #1812 / PR #1820 in flight at batch close); together the pair closes theversionMadeByupper-bound dimension across both ZIP layers. Fixture:zip64-eocd64-versionmadeby-too-high.zip.SECURITY_INVENTORY.mdRecent wins (:262-289). - CD entry name NUL-byte rejection (#1831). Rejects raw CD
nameBytescontaining0x00atparseCentralDir(Zip/Archive.lean:559), before the UTF-8 decode at :562-572. A NUL in the filename is a classic parser-differential / filesystem-truncation smuggling vector: POSIXopen/stattreatsevil.txt\x00.zipasevil.txt, whileArchive.listcallers and strict peer readers see the full NUL-embedded string. Guarding on the rawByteArraybefore UTF-8 decode closes both the UTF-8 and Latin-1 decode branches uniformly and keeps the error message NUL-free. Simultaneously closesArchive.list(silent NUL-path surfacing) andArchive.extract(silent truncated-filename drop) dimensions. Interop pre-flight swepttestdata/zip/{interop,malformed}/*.zipfor pre-existing NUL-in-name fixtures — zero hits. Fixture:cd-nul-in-name.zip.SECURITY_INVENTORY.mdRecent wins (:500-532).
Paired-review PRs (5). #1808 (reviews #1801, the prior-wave feature #1803 excluded from its 12-PR scope — inherited-backlog closure at 38 min), #1815 (reviews #1807, in-wave at 43 min), #1830 (reviews #1813, in-wave at 2 h 35 min), #1832 (reviews #1819, in-wave at 2 h 40 min), #1835 (reviews #1824, in-wave at 2 h 37 min). The three mid-wave in-wave pairs all crossed the 2 h mark — still inside the 30 min to several hours bracket documented in progress/20260424T105643Z_c4046753-paired-review-1785.md §F, but the median has slid from 43 min (wave start) to ~2.5 h (wave tail) as the paired-review queue filled faster than it drained. #1826 and #1831 have no paired-review entry at batch close; paired-review issues #1828 and #1833 are unclaimed in the queue, inherited across the summarize boundary the same way the prior wave's #1788/#1793 pairs were.
Doc (1). #1836 fixes a PR-number misattribution in
SECURITY_INVENTORY.md's CD flag bit-5 Recent wins entry
(#1820 → #1824) plus a related-class label drift. The particular
drift was a real #NNNN value pointing at the wrong PR, not a
placeholder; the #1796 placeholder-PR linter did not catch it, so
the error was found by reviewer cross-check. A small footprint
follow-up to the #1794/#1796 attribution-hygiene cluster of the
prior wave.
Scope discipline. Source-code changes this wave touched only
Zip/Archive.lean (999 → 1113 LOC, +114 from five new CD-parse
checks — four field-per-field validations plus the archive-layout
invariant — plus the EOCD64 versionMadeBy guard) and
ZipTest/ZipFixtures.lean (687 → 841 LOC, +154 from six new
fixture/assert blocks). Zip/Spec/ stayed at 42 files × 21,067
LOC with zero edits, matching the scope discipline of the prior
three Track E waves.
Dispatcher snapshot. At batch close, coordination orient
listed 11 open PRs in the merge-conflict state, all Track E
CD-parse cascades from the rapid Archive.lean edits across this
and the prior wave. This is flagged as a known-state observation
(the pr-repair lane will drain it), not a summarize concern — the
same cascading-conflict pattern follows every multi-PR Track E
wave that edits a common section of parseCentralDir.
Quality metrics: 0 sorries across Zip/ (unchanged — none of
these PRs touched proofs); 0 runtime ]! across Zip/Native/
and Zip/*.lean (unchanged); bash scripts/check-inventory-links.sh
→ errors=0, warnings=0 post-wave. Fixtures in
testdata/zip/malformed/ grew from 28 to 34 (six new:
cd-version-needed-too-high.zip,
cd-entry-localoffset-past-cdstart.zip,
cd-entry-internal-attrs-reserved.zip,
cd-patched-data-flag.zip,
zip64-eocd64-versionmadeby-too-high.zip,
cd-nul-in-name.zip). Tar fixture counts (14 / 6) unchanged.
Type mix across the 12 PRs: feature 6 / paired-review 5 / doc 1.
Toolchain v4.29.1.
Note: the pre-wave baseline of 28 fixtures is the actual tree
count at #1803 merge, one higher than the 27 the prior
summarize's Current State bullet recorded — #1801's
cd-bad-method-early.zip landed 10 min before #1803 and was
outside the prior summarize's 12-PR scope, so its fixture was
omitted from the tally but was present in the tree.
14-PR batch (Apr 24): Track E CD-parse filename-validation trio + ZIP64 EOCD64 archive-layout / version-field bounds closure + Tar cross-format NUL-byte closure + inventory drift-tooling progression (summarize #1869):
Fourteen PRs merged in the ~3-hour window between summarize #1843
(merge commit bb2f514, 17:10:20Z 2026-04-24) and PR #1868 (merge
commit 33275aa, 20:24:21Z 2026-04-24). Spanning four dominant
themes — CD-parse filename-validation trio closure, ZIP64 EOCD64
archive-layout and version-field two-sided-bound invariants, Tar
cross-format NUL-byte smuggling closure, and a terminating one-shot
→ sweep → detector progression on inventory drift — the wave
consolidates three adjacent audit axes that had been expanding in
parallel across the prior three waves. No spec file touched;
grep -rc sorry Zip/ stayed at 0 throughout. Source edits landed
only in Zip/Archive.lean (1113 → 1253 LOC, +140 from five new
CD-parse and EOCD64 checks) and Zip/Tar.lean (+two NUL-byte
guards at the GNU-long-name and PAX-record layers), plus
ZipTest/ZipFixtures.lean + ZipTest/TarFixtures.lean fixture
assertions. The filename-validation trio closes the "smuggled
name" attack class at CD parse across three distinguishable forms
(NUL-embedded #1831, zero-length #1848, path-unsafe #1840). The
CD-parse mathematical-invariant family adds its second column —
uncompSize == 0 → crc == 0 (#1857), sibling of the stored-method
size invariant #1773. The ZIP64 EOCD64 archive-layout invariants
trio closes at three granularities: per-entry (#1813, prior wave),
archive (#1809, in-flight), ZIP64 record (#1856); the EOCD64 version-
field pair versionMadeBy ≤ 63 (#1826, prior wave) + versionNeededToExtract ≤ 63
(#1852) closes the two-sided upper bounds at archive level. The
Tar NUL-byte closures — GNU long-name / long-link (#1865) and PAX
record key/value (#1866) — combined with the ZIP CD-layer #1831
span all three code-guarded user-supplied string-field layers
(ustar base name / linkname is structurally NUL-safe by
construction). Inventory drift: #1850 (one-shot misattribution
fix) → #1867 (one-shot placeholder-PR and inverted-range sweep) →
#1868 (automated warnings-only detector for both drift shapes),
a deliberate terminating progression at this layer — the next
drifts of these two shapes will be caught by CI rather than
discovered by cross-check during review. Paired-review cadence
tightened noticeably: three in-wave pairs landed with a median
latency of ~1 h 17 min (vs. ~2 h 37 min tail in the prior wave) as
the queue drained faster than new reviews arrived. Type mix across
the 14 PRs: feature 7 / paired-review 4 / inventory 3. Fixtures in
testdata/zip/malformed/ grew from 34 to 39 (+5: cd-path-unsafe.zip,
cd-empty-name.zip, zip64-eocd64-versionneeded-too-high.zip,
zip64-eocd64-overlap-locator.zip, cd-empty-entry-crc-nonzero.zip);
fixtures in testdata/tar/malformed/ grew from 14 to 16 (+2:
gnu-longname-nul-in-name.tar, pax-path-nul-in-value.tar). At
wave close: 11 PRs in the repair queue (all Track E CD-parse
cascades from the rapid Archive.lean edits across this + prior
two waves), feature queue empty, review queue at 2 (#1862
EOCD64-layout paired-review, #1863 empty-entry-CRC paired-review).
Toolchain v4.29.1.
Note: the issue body's title said "~13 PRs" but the actual post- #1843 window contains 14 — PR #1840 (CD path-unsafe, merged 12 min after #1843) was in-window but not tallied by the planner; it is included in this summary under the filename-validation trio framing. See the progress entry's Wave scope and PR-count note for the full accounting. Full progress file: progress/20260424T203421Z_90d1e22c-summarize-post-1843.md.
10-PR batch (Apr 24): Track E CD-parse math-invariant family column 3 + ZIP64 EOCD64 v2-record explicit-shape fixture + Tar UStar header NUL closure (summarize #1894):
Ten PRs merged in the ~2-hour 43-min window between summarize
#1869 (merge commit 233fe98, 20:40:48Z 2026-04-24) and PR #1893
(merge commit 9200042, 23:23:56Z 2026-04-24). The wave's
organising theme is the simultaneous closure of three Track E
sub-stories that the post-#1843 summarize had flagged as in-flight
or follow-up gaps: the CD-parse mathematical-invariant family
second column extends to method=8 with an explicit compSize == 0 → uncompSize == 0 invariant (closing the deflate gap in the
#1773 / #1857 family), the ZIP64 EOCD64 v2-record sentinel gets
explicit fixture coverage against APPNOTE §4.3.14.2 strong-encryption
extensions, and the cross-format NUL-byte closure extends to
UStar's last user-supplied string-field layer — converting name
/ linkname / prefix from structurally NUL-safe by construction
to explicitly code-guarded. No spec file touched;
grep -rc sorry Zip/ stayed at 0 throughout.
Track E feature PRs (3).
- #1886 — CD-parse
compSize == 0 ∧ uncompSize > 0rejection. Method-agnostic mathematical invariant atparseCentralDir, rejecting CD entries that promise non-empty uncompressed output from empty compressed input. Closes the method=8 gap in the CD-parse mathematical-invariant family — siblings #1773 (stored- methodcompSize == uncompSize) and #1857 (uncompSize == 0 → crc == 0) covered the prior two columns. Fixture:cd-deflate-zero-compsize.zip. - #1880 — Tar UStar header NUL-byte rejection. Adds explicit
0x00-byte rejection onname/linkname/prefixof the plain ustar 100-byte fields, converting the layer from structurally-NUL-safe-by-construction to explicitly code-guarded. Together with #1865 (Tar GNU long-name / long-link), #1866 (Tar PAX record key/value), and #1831 (ZIP CD entry name) this completes all four user-supplied string-field layers carrying explicit NUL-byte code guards across both the ZIP and Tar parsers. Fixture:ustar-name-nul-in-name.tar. - #1889 — explicit v2-shape ZIP64 EOCD64 record fixture. Adds
zip64-eocd64-v2-record.zipas regression coverage for the existing PR #1761 record-size guard (size of record == 44) against APPNOTE §4.3.14.2 strong-encryption v2 EOCD64 extensions. Closes the v2-record-coverage gap flagged in the post-#1843 progress entry's Follow-up planning hooks — the guard already exists in code but had no positive-rejection fixture pinning it.
Paired-review PRs (6). #1874 reviews #1856 (EOCD64 archive-layout invariant, prior-wave carryover at 1 h 54 min), #1875 reviews #1857 (empty-entry CRC, prior-wave carryover at 1 h 50 min), #1882 reviews #1866 (Tar PAX NUL, prior-wave carryover at 1 h 46 min), #1884 reviews #1880 (Tar UStar NUL, in-wave at 29 min), #1890 reviews #1886 (CD-parse column 3, in-wave at 23 min), #1893 reviews #1889 (EOCD64 v2 fixture, in-wave at 23 min). The three in-wave pairs all landed inside 30 min — the tightest cluster in the Track E wave series so far — while the three carryover pairs drained the post-#1843 paired-review backlog at ~1 h 50 min each. Every in-wave feature PR has a matching review entry; the prior-wave #1865 paired-review remains in flight as PR #1876 (CI failed, in the repair queue at wave close), but #1856 / #1857 / #1866 — three of the four prior-wave unpaired features the post-#1843 progress entry's What remains section flagged — are all paired by the end of this wave.
Inventory bookkeeping (1). #1881 substitutes three #N
placeholder-PR rows in SECURITY_INVENTORY.md (:641 → #1848,
:683 → #1857, :1106 → #1866) — the one-shot follow-up the
post-#1843 progress entry's What remains section anticipated,
closing the three drifts the post-#1843 #1868 detector flagged
that #1867's sweep could not substitute (because their feature PRs
hadn't yet merged at #1867's drafting time).
Quality metrics: 0 sorries across Zip/ (unchanged — none of
these PRs touched proofs); 0 runtime ]! across Zip/Native/ and
Zip/*.lean (unchanged). Fixtures in testdata/zip/malformed/
grew from 39 to 41 (+2: cd-deflate-zero-compsize.zip,
zip64-eocd64-v2-record.zip); fixtures in testdata/tar/malformed/
grew from 16 to 17 (+1: ustar-name-nul-in-name.tar). Type mix
across the 10 PRs: feature 3 / paired-review 6 / inventory 1.
Toolchain v4.29.1.
15-PR batch (Apr 24–25): Track E per-slot ZIP64-override family closure (4/5) + local-offset→LH validation trio completion + PAX duplicate-key closure + paired-review batch (summarize #1927):
Fifteen PRs merged in the ~3-hour 32-min window between summarize
#1894 (merge commit 1bb9465, 00:30:23Z 2026-04-25) and PR #1926
(merge commit f7d1100, 03:07:43Z 2026-04-25); the lower bound
includes four stragglers (#1896, #1897, #1899, #1903) that merged
in the 67-min window between PR #1893 — the last PR captured in
the #1894 summarize body — and #1904 itself merging, so the
authoritative pre-wave anchor is summarize #1894 even though four
of the wave's PRs predate it by clock time. The wave's organising
theme: closure of two Track E sibling-trio families on the same
day. The per-slot ZIP64-override family opened by #1745 (cdOffset
slot, post-#1718 wave) closes 4 of its 5 remaining slots in a
single ~2-hour cadence (cdSize / totalEntries / diskWhereCDStarts
/ numEntriesThisDisk), leaving only the numberOfThisDisk slot
outstanding via in-flight PR #1909; and the local-offset →
local-header validation trio opened by #1813 (level-1
cd-entry-localoffset-past-cdstart, post-#1803 wave) closes its
level-2 (#1921 cd-entry-past-cdend, the existing entryEnd > cdEnd
guard at Zip/Archive.lean:614-615) and level-3 (#1903
cd-bad-lh-signature, the late LH-signature guard at
Zip/Archive.lean:1081) levels, so all three precedence layers
now have explicit fixture coverage. Separately, #1899 closes the
second parser-differential dimension on PAX-record decode
(duplicate-key rejection in parsePaxRecords, companion to the
prior #1866 NUL-byte closure on the same surface). No spec file
touched; grep -rc sorry Zip/ stayed at 0 throughout. Source edits
landed in Zip/Archive.lean (per-slot ZIP64-override fixture
assertions), Zip/Tar.lean (+one duplicate-key guard at the PAX
layer), ZipTest/ZipFixtures.lean + ZipTest/TarFixtures.lean
fixture assertions, and SECURITY_INVENTORY.md (placeholder-PR
substitutions and post-merge bookkeeping).
Track E feature PRs (6): per-slot ZIP64-override family closure (4/5).
- #1905 — explicit per-slot fixture
eocd-zip64-override-cdsize-mismatch.zipfor thecdSizeslot. Second slot in the 6-slot per-slot ZIP64-override family (sibling of #1745cdOffsetslot which opened the family). - #1908 — explicit per-slot fixture
eocd-zip64-override-totalentries-mismatch.zipfor thetotalEntriesslot. Third slot. - #1911 — explicit per-slot fixture
eocd-zip64-override-diskcd-mismatch.zipfor thediskWhereCDStartsslot. Fourth slot. - #1922 — explicit per-slot fixture
eocd-zip64-override-entriesthisdisk-mismatch.zipfor thenumEntriesThisDiskslot. Fifth slot. After this PR only thenumberOfThisDiskslot remains; tracked by issue #1902 with PR #1909 currently in the repair queue (merge-conflict at wave close).
Track E feature PRs continued: local-offset → local-header validation trio completion (level-2 + level-3).
- #1903 — level-3: explicit fixture
cd-bad-lh-signature.zipfor the late LH-signature guard atZip/Archive.lean:1081. Probes the third precedence layer of the local-offset → LH chain; the prior two levels were #1813 (level-1cd-entry-localoffset-past-cdstart, post-#1803 wave) and #1921 (level-2, this wave). - #1921 — level-2: explicit per-entry CD-overrun fixture
cd-entry-past-cdend.zipfor the existingentryEnd > cdEndguard atZip/Archive.lean:614-615. With #1903 + #1813 this closes the trio so all three precedence layers (local-offset bound, CD-end overrun, LH-signature mismatch) carry explicit per-entry fixtures.
Track E feature PRs continued: PAX duplicate-key closure.
- **#1899 — Tar PAX duplicate-key rejection in
parsePaxRecordspax-duplicate-path.tarfixture.** Closes the second parser-differential dimension on the PAX-record decode surface (companion to #1866 NUL-byte rejection on the sameparsePaxRecordssurface). After this PR both parser-differential dimensions on PAX records are closed (NUL-byte smuggling #1866 + duplicate-key first-wins-vs-last-wins #1899).
Inventory placeholder-PR substitution sweeps (2).
- #1896 — substitute two
#Nplaceholder-PR rows inSECURITY_INVENTORY.md(:766 → #1880,:1128 → #1880) post-#1880 one-shot bookkeeping. - #1897 — substitute five
#Nplaceholder-PR occurrences (:261 → #1889,:740 / :777 / :1191 → #1886,:1223 → #1889) post-#1886/#1889 one-shot sweep. Note: subsequent #1928 (post-wave) added thepax-duplicate-path.tarcorpus row + Tar Parser/Extractor recent-wins bullet that PR #1899 had itself omitted — see the next wave for that follow-up.
Paired-review PRs (6). #1913 reviews #1899 (PAX duplicate-key,
in-wave at 1 h 34 min), #1914 reviews #1903 (level-3 LH-signature
fixture, in-wave at 1 h 31 min), #1919 reviews #1905
(cdSize per-slot fixture, in-wave at 1 h 29 min), #1920 reviews
#1908 (totalEntries per-slot fixture, in-wave at 1 h 27 min),
#1925 reviews #1911 (diskWhereCDStarts per-slot fixture, in-wave
at 1 h 39 min), #1926 reviews #1921 (level-2 CD-overrun fixture,
in-wave at 38 min). Five of six in-wave pairs landed inside ~1 h
40 min — the cluster median tightens vs. the post-#1869 wave's
in-wave triplet (~25 min) only because the per-slot family's
review pairs spaced themselves out across the family's ~2-hour
landing cadence rather than catching up at the end. Every in-wave
feature PR has a matching review entry except #1922 (numEntriesThisDisk
per-slot fixture, paired-review carried into the next wave as
issue #1924 in the unclaimed review queue at wave close).
Quality metrics: 0 sorries across Zip/ (unchanged — none of
these PRs touched proofs); 0 runtime ]! across Zip/Native/ and
Zip/*.lean (unchanged). Fixtures in testdata/zip/malformed/
grew from 41 to 47 (+6: cd-bad-lh-signature.zip,
cd-entry-past-cdend.zip,
eocd-zip64-override-cdsize-mismatch.zip,
eocd-zip64-override-totalentries-mismatch.zip,
eocd-zip64-override-diskcd-mismatch.zip,
eocd-zip64-override-entriesthisdisk-mismatch.zip); fixtures in
testdata/tar/malformed/ grew from 17 to 18 (+1:
pax-duplicate-path.tar). Type mix across the 15 PRs: feature 7 /
paired-review 6 / inventory 2. At wave close: 13 PRs in the repair
queue (12 carryover Track E CD-parse cascades from prior waves
plus #1909 numberOfThisDisk per-slot fixture freshly entered the
queue this wave); feature queue has 2 unclaimed UStar interior-NUL
per-slot fixtures (#1929 linkname slot, #1930 prefix slot, both
filed at 03:32Z); review queue at 1 (#1924, paired-review for
#1922). Toolchain v4.29.1.
15-PR batch (Apr 25): Track E terminal closure of the 5-slot UStar interior-NUL family + terminal closure of the 2-slot GNU long-name / long-link family + first defense-in-depth-extension family closure in Track E history (summarize #1964):
Fifteen PRs merged in the ~3-hour 39-min window between summarize
#1931 (merge commit b4ec7ce, 03:40:09Z 2026-04-25) and PR #1963
(merge commit 154c1f5, 07:31:44Z 2026-04-25). The wave's
organising theme: two terminal per-slot fixture-family closures
landing in near-simultaneous bursts on the same day, plus the
first defense-in-depth extension of any per-slot fixture
family in Track E history. The 5-slot UStar interior-NUL family
opened by PR #1880 (name slot, post-#1869 wave) extended through
the 3-slot filesystem-reaching arm (linkname PR #1934, prefix
PR #1937 — terminal of the 3-slot arm) and then crossed into a
new structural mode: the 4th uname slot (PR #1944) is
defense-in-depth because the field does not flow into
Tar.extract's filesystem operations — it is exposed only via
Tar.list's entry.uname for callers routing on a trust
decision. PR #1957 (gname slot) closes the 5-slot family at 5/5
slots; together with PR #1944 the 2-slot defense-in-depth arm
(uname / gname) joins the closed 3-slot filesystem-reaching
arm as one fully-closed 5-slot family. Concurrently, the 2-slot
GNU long-name / long-link interior-NUL family — opened by PR
#1865 (long-name slot, post-#1843 wave) which already added both
guards in source — closes its per-slot fixture asymmetry at PR
#1953 (long-link slot), reaching 2/2 closure ~30 min before the
5-slot UStar family closes at PR #1957. No spec file touched;
grep -rc sorry Zip/ stayed at 0 throughout. Source edits landed
only in Zip/Tar.lean (per-slot UStar interior-NUL guards
extending the 3-slot forEntries block to 5 slots), plus
ZipTest/TarFixtures.lean fixture assertions, four
SECURITY_INVENTORY.md placeholder-PR substitution sweeps, and
one error-wording-catalogue skill row registration.
Track E feature PRs (5): per-slot UStar interior-NUL family closure (#1934 / #1937 / #1944 / #1957) + per-slot GNU long-link interior-NUL fixture (#1953).
- #1934 — explicit per-slot fixture
ustar-linkname-nul-in-name.tarfor thelinknameslot. Second slot in the 5-slot UStar interior-NUL family (sibling of PR #1880nameslot which opened the family). - #1937 — explicit per-slot fixture
ustar-prefix-nul-in-name.tarfor theprefixslot. Third slot — closes the 3-slot filesystem-reaching arm at 3/3 slots. First fully-closed-on-master per-slot family of the post-#1928 wave (paired-reviewed by PR #1947 as the terminal-closure paired-review for the 3-slot arm). - #1944 — extends the UStar interior-NUL guard to the
unameslot +ustar-uname-nul-in-uname.tarfixture. First defense-in-depth extension in Track E history: theunamefield is a user-attestation string that does not flow intoTar.extract's filesystem operations, so the 4th-slot guard narrows the attack surface from filesystem archive-slip (the 3-slot arm's class) to parser-differentialTar.listsmuggling on a caller routing onentry.unamefor a trust decision. - #1953 — explicit per-slot fixture
gnu-longlink-nul-in-link.tarfor the GNU long-link slot (closes 2-slot family at 2/2). Companion to PR #1865's long-name slot fixture — both guards already existed in source from PR #1865, so this PR is fixture-only, closing the per-slot fixture asymmetry PR #1865 left behind. - #1957 — per-slot UStar
gnameinterior-NUL guard +ustar-gname-nul-in-gname.tarfixture. 5th-and-final slot of the UStar family — terminal closure of the 5-slot family (3-slot filesystem-reaching armname/linkname/prefix- 2-slot defense-in-depth arm
uname/gname). Mirrors PR #1944's defense-in-depth structure.
- 2-slot defense-in-depth arm
Per-slot family closures and extensions (post-#1931 wave):
| Family | Slot count | Closure / extension state at end of wave |
|---|---|---|
| UStar interior-NUL fs-reaching arm | 3 | 3/3 closed at PR #1937 (this wave — terminal of the 3-slot arm) |
UStar interior-NUL with uname (defense-in-depth) |
4 | 4/4 landed at PR #1944 (this wave — first defense-in-depth extension in Track E history) |
UStar interior-NUL with gname (final) |
5 | 5/5 closed at PR #1957 (this wave — terminal of the 5-slot family) |
| GNU long-name / long-link | 2 | 2/2 closed at PR #1953 (this wave — terminal; sibling of PR #1865 long-name) |
| EOCD ZIP64-override mismatch | 6 | 5/6; gated on PR #1909 in repair queue (issue #1902 — carried into post-#1931 wave) |
| PAX path / linkpath NUL | 2 | 1/2; path covered by PR #1866; linkpath gated on unclaimed issue #1855 |
The defense-in-depth distinction is structurally novel for Track E:
the uname and gname fields are user-attestation strings (not
filesystem-routing strings) that strict peer parsers preserve at
full fidelity but lean-zip's Binary.readString would silently
truncate at an interior NUL without the new guards. The 5-slot
UStar interior-NUL family is the uniquely structured family
in Track E so far — all earlier per-slot families had every slot
reaching the filesystem (or, for the EOCD ZIP64-override family,
every slot mathematically homologous). Future per-slot families
that mix filesystem-reaching with defense-in-depth slots can
reuse this wave's family-closure-vs-extension framing.
Paired-review PRs (5). #1940 reviews #1934 (linkname slot,
in-wave at 35 min), #1947 reviews #1937 (prefix slot — terminal
of 3-slot arm, in-wave at 1 h 9 min — labeled the first
fully-closed-on-master per-slot family of the post-#1928 wave),
#1951 reviews #1944 (uname slot — first defense-in-depth-
extension paired-review in Track E history, in-wave at 53 min),
#1962 reviews #1953 (long-link slot — terminal of 2-slot GNU
family, in-wave at 49 min), #1963 reviews #1957 (gname slot —
terminal of 5-slot UStar family, in-wave at 40 min). All five
in-wave feature PRs have an in-wave matching paired-review entry,
extending the post-#1904 wave's tightening cluster trend (median
~49 min vs. post-#1904's median ~1 h 31 min): every closure event
now triggers a paired-review issue exactly once, and the
paired-review queue drains within an hour of the closure. PR
#1932 (paired-review for PR #1922 numEntriesThisDisk slot, the
last unpaired feature from the post-#1904 wave) landed at the
start of this wave (~12min into the window); count it as the
prior-wave carryover that flushed the post-#1904 paired-review
queue to zero.
Inventory placeholder-PR substitution sweeps (3).
- #1941 — three-row sweep (
SECURITY_INVENTORY.mdplaceholder rows for #1903, #1921, #1922 — post-#1904 follow-up). - #1949 — four-row sweep (placeholder rows for #1903, #1921, #1934, #1937 — sibling of PR #1941, extending coverage to the two newly-landed UStar interior-NUL fixtures).
- #1958 — one-row sweep (placeholder row for #1944's
ustar-uname-nul-in-uname.tarat line 1314 — post-#1944 one-shot).
Skill registration (1). PR #1948 — error-wording-catalogue rows for the tar interior-NUL family + PAX duplicate-key (#1899). The out-of-scope follow-up to PR #1880's paired-review surfaced as a discrete skill PR; registers the per-slot error-substring family in the catalogue so future tests can pick the right match string the first time.
Out-of-scope cleanup queued at wave close. Issue #1956
(cleanup-list registration for PR #1865's gnu-longname-nul-in-name.tar)
and issue #1960 (post-#1953 + post-#1957 inventory sweep — closes
two #N placeholders for gnu-longlink-nul-in-link.tar →
#1953 and ustar-gname-nul-in-gname.tar → #1957) were queued at
wave close; both landed in the post-#1963 follow-up window as PR
#1967 and PR #1968 respectively, so they will appear in the next
wave block.
Quality metrics: 0 sorries across Zip/ (unchanged — none of
these PRs touched proofs); 0 runtime ]! across Zip/Native/ and
Zip/*.lean (unchanged). Fixtures in testdata/zip/malformed/
unchanged at 47 (no ZIP fixtures landed this wave); fixtures in
testdata/tar/malformed/ grew from 18 to 23 (+5: the four
UStar interior-NUL slot fixtures ustar-linkname-nul-in-name.tar,
ustar-prefix-nul-in-name.tar, ustar-uname-nul-in-uname.tar,
ustar-gname-nul-in-gname.tar, plus the GNU long-link slot
gnu-longlink-nul-in-link.tar). Type mix across the 15 PRs:
feature 5 / paired-review 5 (in-wave) + 1 (carryover #1932) /
inventory 3 / skill 1 — call it 5 / 6 / 3 / 1 if folding the
carryover paired-review in, or 5 / 5 / 3 / 1 + 1 carryover. At
wave close: repair queue at 14 PRs (12 carryover Track E CD-parse
cascades from prior waves plus #1909 numberOfThisDisk per-slot
fixture, plus the new #1959 prior-wave inventory line-anchor sweep
that entered the queue with merge-conflict status); feature
queue empty (all post-#1928 per-slot fixture issues — #1929 /
#1930 / #1942 / #1945 / #1946 — landed via PRs #1934, #1937,
#1944, #1953, #1957 by wave close); review queue empty (all
in-wave paired-reviews landed; #1924 paired-review for #1922
landed as PR #1932 at the start of this wave). lake build clean
(191 jobs); lake exe test all green; bash scripts/check-inventory-links.sh
exits 0 with 104 warnings (pre-existing line-anchor drift).
Toolchain v4.29.1.
11-PR batch (Apr 25): Track E third terminal closure of the post-#1928 wave at PR #1979 (2-slot PAX value-side override family at 2/2 — last unclosed Tar interior-NUL family) + post-closure cadence (paired-review + inventory row tightenings + Recent-wins bullet sequencing + skill updates) (summarize #1989):
Eleven PRs merged in the ~2-hour 34-min window between PR #1967
(merge commit c2786e1, 07:45:32Z 2026-04-25) and PR #1988 (merge
commit 8f63bac, 10:19:43Z 2026-04-25). The window opens ~40 min
before the prior summarize PR #1971 (merged 08:24:42Z) submitted:
the three post-issue-creation tail PRs from the prior wave landed at
07:45–08:08Z, after issue #1964's 07:38Z creation but before
#1971's 08:24Z merge — explicitly deferred to this wave per
progress/20260425T082152Z_1f65eccf-summarize-post-1931.md
(line 46). The wave's organising theme is the third terminal
closure of the post-#1928 per-slot Tar interior-NUL family arc:
PR #1979 closes the 2-slot PAX value-side override family
(path / linkpath) at 2/2 by adding the per-slot linkpath
regression fixture pax-linkpath-nul-in-value.tar (the symmetric
valueBytes-arm guard already existed in source from PR #1866's
(keyBytes / valueBytes) raw-NUL guard at parsePaxRecords,
paired with PR #1866's path-slot fixture
pax-path-nul-in-value.tar). With this closure, all three named
per-slot Tar interior-NUL families flagged by PR #1971's wave-block
remaining-work list are terminally closed and paired-reviewed:
- 5-slot UStar interior-NUL family — terminal at PR #1957 / paired-review #1963 (prior wave).
- 2-slot GNU long-name / long-link — terminal at PR #1953 / paired-review #1962 (prior wave).
- 2-slot PAX value-side override (path / linkpath) — terminal at PR #1979 / paired-review #1986 (this wave).
PR #1986 completes the paired-review trio (#1962 / #1963 /
#1986 reviewing #1953 / #1957 / #1979 respectively). The wave is
otherwise post-closure cleanup: three inventory row tightenings
(#1985, #1987, #1988), one Recent-wins bullet add for the
second terminal closure of the post-#1928 wave (#1978 GNU
long-name / long-link, sequenced ahead of the still-pending PAX
value-side bullet which remains queued under issue #1983 at wave
close), and two skill updates (#1973 malformed-fixture-builder
per-slot printable-prefix discipline; #1975
inventory-reconciliation terminal-closure tightening cadence).
Source edits were minimal: only the new
testdata/tar/malformed/pax-linkpath-nul-in-value.tar fixture and a
matching ZipTest/TarFixtures.lean builder + assertion entry from
PR #1979; Zip/Tar.lean itself was not touched by any wave PR.
grep -rc sorry Zip/ stayed at 0 throughout.
Tail-deferred from prior wave (3): PRs landed between issue #1964's 07:38Z creation and PR #1971's 08:24Z merge.
- #1967 (07:45Z) — test cleanup: register
gnu-longname-nul-in-name.tarinZipTest/TarFixtures.lean's cleanup-list array (one-line follow-up to PR #1865's missing cleanup-list registration; closes issue #1956). - #1968 (07:50Z) — inventory two-row placeholder sweep:
substitute
#NPR-refs inSECURITY_INVENTORY.mdforgnu-longlink-nul-in-link.tar→ #1953 andustar-gname-nul-in-gname.tar→ #1957 (terminal closure of the post-#1928 placeholder set per the prior wave block's tracking; closes issue #1960). - #1969 (08:07Z) —
error-wording-catalogueskill refresh: credit PR #1957 on the UStar interior-NUL row and PR #1953 on the GNU long-name / long-link row, retire stale "deferred to follow-up planner cycle issue #1946" and "in flight via per-slot family-extension issue #1945" clauses, add the defense-in-depth annotation notinguname/gnamedo not reachTar.extract.
Per-slot fixture terminal closure (1): PR #1979 — third terminal closure of the post-#1928 wave; sibling of pre-wave PR #1866 path slot.
- #1979 (09:36Z) — explicit per-slot fixture
pax-linkpath-nul-in-value.tarfor the PAXlinkpathslot (closes 2-slot PAX value-side override family at 2/2). Sibling of PR #1866 (pathslot, pre-wave) which added both arms of the(keyBytes/valueBytes) raw-NUL guard atparsePaxRecordsin one source landing but emitted only thepathfixture. PR #1979 closes the per-slot fixture asymmetry — fixture-only PR (no source change), structurally parallel to PR #1953 (GNU long-link slot, prior wave) which closed the GNU 2-slot family at 2/2 with the same fixture-only shape. Third terminal closure of the post-#1928 wave after PRs #1957 (5-slot UStar) and #1953 (2-slot GNU long-name / long-link); the PAX value-side override family was the last Tar interior-NUL family flagged by PR #1971's remaining-work list, and its closure terminates the post-#1928 Tar interior-NUL closure arc.
Per-slot family closures (post-#1971 wave):
| Family | Slot count | Closure state at end of wave |
|---|---|---|
| UStar interior-NUL family | 5 | 5/5 closed at PR #1957 (prior wave — terminal) |
| GNU long-name / long-link | 2 | 2/2 closed at PR #1953 (prior wave — terminal) |
| PAX value-side override (path / linkpath) | 2 | 2/2 closed at PR #1979 (this wave — terminal; sibling of PR #1866 path slot) |
| EOCD ZIP64-override mismatch | 6 | 5/6; gated on PR #1909 in repair queue (carried over from prior wave, merge-conflict status) |
With the PAX value-side family's closure, all three named per-slot
Tar interior-NUL families flagged by PR #1971's wave-block
remaining-work list are terminally closed; the only remaining
in-flight per-slot family at wave close is the EOCD ZIP64-override
mismatch (5/6, gated on PR #1909's numberOfThisDisk slot, still
in the repair queue with merge-conflict status as it has been
since the prior wave).
Paired-review (1). PR #1986 reviews #1979 (PAX linkpath slot —
terminal closure of the 2-slot PAX value-side override family,
in-wave at 35 min) — the third terminal-closure paired-review
in the post-#1928 wave arc, completing the paired-review trio
(#1962 / #1963 / #1986 for #1953 / #1957 / #1979 respectively).
At wave close the review queue is empty.
Recent-wins bullet add (1). PR #1978 — adds a
SECURITY_INVENTORY.md Recent-wins bullet under Tar
Parser/Extractor for the GNU long-name / long-link interior-NUL
family closure (PRs #1865 + #1953); the second terminal-closure
bullet of the post-#1928 wave after the existing UStar bullet.
Also tightens the UStar bullet's sibling-of paragraph to credit the
per-slot long-link slot (PR #1953). The third terminal-closure
bullet (post-#1979 PAX value-side) remains queued under issue
#1983 at wave close and is deferred to the next wave per the
post-issue-creation tail deferral pattern (and explicitly is not
yet landed at the moment of issue #1989's creation at 10:23Z).
Inventory row tightenings (3).
- #1985 —
gnu-longname-nul-in-name.tarrow tightening atSECURITY_INVENTORY.md:1300: credit PR #1953 long-link slot fixture (retire stale "covered by symmetric code review rather than a dedicated fixture" clause), retire stale "PAX path/linkpath NUL-byte coverage is the sibling issue #1855" clause (#1855 was closed by PR #1866 for thepatharm; thelinkpatharm is separately tracked), re-anchor staleZip/Tar.lean:588line citation to current master:672. - #1987 — placeholder-PR substitution sweep (one-row): row
1341 (
pax-linkpath-nul-in-value.tar)#N→#1979. Closes the post-merge inventory-sweep deferral flagged inprogress/20260425T093345Z_78e1378b-feature-1976.md. Sibling shape of post-#1928 placeholder-sweep precedents PR #1958 / PR #1968 / PR #1949. - #1988 —
pax-path-nul-in-value.tarrow tightening atSECURITY_INVENTORY.md:1343: per-slot family-closure annotation crediting the 2-slot PAX value-side override family closure 2/2 (PR #1866 path slot + PR #1979 linkpath slot); tightens the trailing 'trio' sibling-of paragraph from three families to four (ZIP CD name + GNU 2/2 + UStar 5/5 + PAX 2/2 — now including the PAX value-side family).
Skill updates (2).
- #1973 —
malformed-fixture-builderper-slot printable-prefix discipline section. Encodes slot-identity in the smuggled value's printable ASCII prefix (post-#1928 wave examples:evil.txt/evil.lnk/badpfx/trusted/safe.lnk— including the.lnkextension as a novel slot-identity marker for link-typed slots from PR #1953); documents the trailing-NUL invariant for interior-NUL fixtures (last byte must be non-NUL orstripTrailingNulswould erase the interior NUL the test intends); documents the writer-side override-hook pattern (pathOverride-style for transformed slots likeprefix'ssplitPath, no override needed for verbatim-written slots likelinkname/uname/gname). - #1975 —
inventory-reconciliationterminal-closure tightening cadence + per-slot fixture-naming asymmetry. Codifies the post-#1928 wave's 5-step #1880 → #1934 → #1937 → #1944 → #1957 precedent (terminal closure → paired-review → inventory row tightening → Recent-wins bullet → placeholder sweep). Independent of in-flight #1966malformed-fixture-builderskill update — different skill file, different cadence layer.
Post-#1928 wave audit-trail callout. The post-#1928 Tar interior-NUL closure arc is structurally complete: all three named per-slot Tar interior-NUL families flagged by the post-#1931 wave block are now terminally closed and paired-reviewed (PR #1986 completes the paired-review trio for the third closure). The post-closure cadence (paired-review + inventory row tightening + Recent-wins bullet + skill updates) continues into the next wave only for the queued PAX Recent-wins bullet (issue #1983) and the post-issue-creation tail deferral skill (issue #1984).
Post-issue-creation tail (out of wave scope). No PRs for issue
#1983 (PAX Recent-wins bullet — third terminal-closure bullet of
the post-#1928 wave; sibling shape of PR #1978) or issue #1984
(skill: codify the post-issue-creation tail deferral pattern in a
tracked .claude/skills/ file) had landed at the moment of issue
#1989's creation (2026-04-25T10:23Z); both were in the unclaimed
feature queue. Per the post-issue-creation tail deferral pattern,
any PRs that land between this issue's creation and the wave PR's
submission will be deferred to the next wave block; this wave's
scope is frozen at the 11 PRs enumerated above per the
issue-body-as-source-of-truth invariant.
Quality metrics: 0 sorries across Zip/ (unchanged — none of
these PRs touched proofs); 0 runtime ]! across Zip/Native/ and
Zip/*.lean (unchanged). Fixtures in testdata/zip/malformed/
unchanged at 47 (no ZIP fixtures landed this wave); fixtures in
testdata/tar/malformed/ grew from 23 to 24 (+1:
pax-linkpath-nul-in-value.tar from PR #1979). Type mix across
the 11 PRs: feature 1 (#1979) / paired-review 1 (#1986) /
inventory 4 (#1968 + #1985 + #1987 + #1988) / Recent-wins bullet 1
(#1978) / skill 3 (#1969 + #1973 + #1975) / test cleanup 1
(#1967). The character is wave-cleanup rather than
wave-headline: the wave carries one terminal closure plus the
post-closure follow-on cadence (inventory tightening, paired-review,
skill refinements), in contrast to PR #1971's wave which carried
two terminal closures + one defense-in-depth-extension first. At
wave close: repair queue at 14 PRs (unchanged from prior wave —
no new entrants, no salvages); feature queue at 2 (issue #1983
PAX Recent-wins bullet, issue #1984 skill); review queue empty
(PR #1986 drained the post-#1979 paired-review entry within 35 min
of merge). lake build clean (191 jobs); lake exe test all
green; bash scripts/check-inventory-links.sh exits 0 with 104
warnings (pre-existing line-anchor drift, unchanged from prior
wave). Toolchain v4.29.1.
35-PR batch (Apr 25): post-#1989 wave — pure inventory-anchor refresh wave (no terminal closures, no Track E feature work) covering 8 distinct SECURITY_INVENTORY.md sub-tracks + 2 tail-deferred PRs from the prior wave; first maintenance-only wave of the post-#1928 closure arc (summarize #2074):
Thirty-five PRs merged in the ~3-hour 39-min window between
PR #1996 (10:53:21Z 2026-04-25) and PR #2072 (14:32:30Z
2026-04-25), opening immediately after PR #1990 (the prior
summarize wave PR) merged at 10:34:48Z. The wave's organising
character is maintenance saturation: the post-#1813 /
#1857 / #1886 / #1903 / #1921 CD-parse-guard wave shifted all
Zip/Archive.lean line anchors uniformly by +111 lines, and
the post-#1866 / #1880 / #1934 / #1937 / #1944 / #1953 /
#1957 / #1979 / #1899 Tar parser growth wave shifted
Zip/Tar.lean anchors by mixed +29 / +91 / +97 / +154 line
amounts. Each cited row in SECURITY_INVENTORY.md then
received its own atomic re-anchor PR per the established
one-PR-per-row / one-PR-per-cluster cadence. There is no
terminal closure in this wave; the post-#1928 Tar
interior-NUL closure arc (which dominated the post-#1928 /
post-#1931 / post-#1971 waves with three named per-slot
family closures and their paired-reviews) is structurally
complete, and the planner-side activity has shifted entirely
to anchor maintenance. Source code (Zip/, c/,
testdata/) was not touched by any of the 35 PRs;
grep -rc sorry Zip/ stayed at 0 throughout.
Tail-deferred from prior wave (2): PRs landed between issue #1989's 10:23Z creation and PR #1990's 10:34Z merge.
- #1996 (10:53Z) — Recent-wins bullet: add
SECURITY_INVENTORY.mdRecent-wins bullet under Tar Parser/Extractor for the 2-slot PAX value-side override (path/linkpath) NUL-byte silent-skip family closure 2/2 (PR #1866 path slot + PR #1979 linkpath slot — third terminal closure of the post-#1928 wave); shape sibling of PR #1978 (post-#1953 GNU bullet). Closes issue #1983 from the prior planning cycle (the queued third-terminal-closure bullet flagged at the post-#1971 wave-block close). - #1997 (11:02Z) —
summarize-flowskill landing: codify the post-issue-creation tail deferral pattern in a tracked.claude/skills/file (summarize-flow/SKILL.md, documenting the issue-body-as-source-of-truth invariant for summarize waves and the worked precedent of PR #1971 deferring tail PRs #1967 / #1968 / #1969). Replaces rejected #1974 (which targeted the off-limits.claude/CLAUDE.md). Closes issue #1984 from the prior planning cycle.
Inventory: Tar fixture row anchor refresh (13 PRs) —
Zip/Tar.lean line citations across the
SECURITY_INVENTORY.md Minimized Reproducer Corpus tar
block. All shifts are subsets of the Tar parser growth wave;
each PR re-anchors a single row or a same-anchor cluster.
- #1998 (11:05Z) — row 1332:
gnu-longname-nul-in-name.tar(:576 → :667long-name primary cell +:672 → :679long-link sibling-of clause). - #1999 (11:09Z) — row 1328:
gnu-longlink-nul-in-link.tar(:672 → :679primary cell + bare prose'long-name slot at line 660'→'line 667'). - #2000 (11:11Z) — rows 1330 + 1331:
gnu-longname-invalid-utf8.tar(:575 → :670) +gnu-longname-no-terminator.tar(:528 → :619). - #2005 (11:20Z) — row 1350:
ustar-uname-nul-in-uname.tar4-anchor sweep (uname primary:532 → :537+ name / linkname / prefix sibling-throw markdown links). - #2007 (11:28Z) — rows 1416 / 1417 / 1418:
testdata/tar/security/{hardlink-outside,symlink-absolute,symlink-slip}.tar(typeHardlink silent-skip + 2 symlink-target throws inTar.extract;:710 → :812/:701 → :799/:703 → :801). - #2008 (11:32Z) — row 1391:
bad-checksum.tar(parseHeader checksum mismatch:444 → :487). - #2012 (11:44Z) — row 1399:
no-magic.tar(parseHeader magic check:448 → :491). - #2014 (11:53Z) — row 1409:
truncated.tar(Tar.extractshort-read throw:686 → :783). - #2017 (12:05Z) — row 1402:
pax-inconsistent-length.tar(parsePaxRecordsrecordEnd silent-skip:79 → :118). - #2024 (12:29Z) — rows 1403 / 1404:
pax-invalid-utf8-{key,value}.tar(String.fromUTF8?if-let guard:122 → :144). - #2037 (13:07Z) — rows 1406 / 1408:
pax-oversized-length+pax-truncated-record(digitCount:95 → :108+ recordEnd:105 → :118). - #2043 (13:19Z) — rows 1397 + 1401:
gnu-longname-oversized-size+pax-extended-oversized-size(readEntryData maxHeaderSize cap throw:222 → :252). - #2044 (13:22Z) — row 1416:
hardlink-outside.tarre-anchor (typeHardlink silent-skip policy comment:812 → :807; substring-mismatch flavour after thetypeHardlinksubstring drifted outside the ±2 window of PR #2007's anchor).
Inventory: ZIP fixture row anchor refresh (7 PRs) —
Zip/Archive.lean line citations across the Minimized
Reproducer Corpus zip block. All shifts are within the
Archive CD-parse-guard / late-section growth waves.
- #2025 (12:34Z) — row 1421:
bad-crc.zip(post-extraction CRC32 verify:1088 → :1199). - #2032 (12:49Z) — row 1422:
bad-method.zipdual-anchor (CD-parse:668 → :713+ late dispatch:1083 → :1194). - #2045 (13:25Z) — rows 1444 + 1457 + 1458 + 1459:
cd-stored-size,oversized-compressed,oversized-zip64-compressed,oversized-zip64-uncompressed4-row same-anchor sweep (:743 → :786). - #2046 (13:28Z) — rows 1456 + 1460:
no-eocd+too-short2-row same-anchor sweep (findEOCDthrow:945 → :1056). - #2047 (13:31Z) — rows 1461 + 1463 + 1464:
zip64-eocd64-{bad-recsize,versionmadeby-too-high,versionneeded-too-high}.zip3-row contiguous-block sweep (uniform+26shift). - #2052 (13:43Z) — row 1432:
cd-extra-overrun-datasize.zipdual-anchor (CD-side:690 → :739+ LH-side:1006 → :1117). - #2053 (13:46Z) — row 1424:
cd-bad-method-early.zip(:668 → :713single-anchor; designed-in-isolation sibling of dual-anchor row 1422).
Inventory: ZIP CD/LH consistency row anchor refresh (2 PRs) —
the cd-lh consistency 'metadata always agrees' /
usesDataDescriptor-gated row clusters in the Minimized
Reproducer Corpus zip block.
- #2018 (12:10Z) — rows 1434 / 1435 / 1436 / 1439:
4-row contiguous-block sweep on the always-checked
consistency quartet
(
cd-lh-{flags,method,modtime,version}-mismatch.zip, insidevalidateAgainstLocalHeader's top half;:1036/:1027/:1056/:1046 → :1147/:1138/:1167/:1157, uniform +111 shift). - #2023 (12:26Z) — rows 1433 / 1437 / 1438:
3-row contiguous-block sweep on the
usesDataDescriptor-gated trio (cd-lh-{crc,size,uncompsize}-mismatch.zip, insidevalidateAgainstLocalHeader's bottom half;:1067/:1061/:1064 → :1178/:1172/:1175, uniform +111 shift).
Inventory: Decompression Limit Inventory anchor refresh (2 PRs) — the Public extraction APIs row clusters in the Recommended policy table (distinct from the Minimized Reproducer Corpus sweeps).
- #2034 (13:00Z) — Archive cluster (rows 1186-1191):
6-row contiguous-block sweep on
def list/def extract/def extractFile(:1098/:1122/:1167 → :1209/:1233/:1278, uniform +111 shift). - #2036 (13:05Z) —
Tar.extractTarGzNativecluster (rows 1196-1198): 3-row contiguous-block sweep (:848 → :945partial def +:851 → :948maxOutputSize parameter, uniform +97 shift).
Inventory: Local guard inventory anchor refresh (8 PRs) —
the Local guard inventory for Handle.read and Stream.read
table rows + intro prose. This is the most concentrated
sub-track of the wave because both shift waves intersect
across the table.
- #2054 (13:48Z) — row 1308:
forEntriesper-block-header read (:565 → :656single-anchor, +91). - #2058 (13:58Z) — row 1313:
extractTarGztarStream wrapper (:793 → :890single-anchor, +97). - #2059 (14:01Z) — row 1310:
skipEntryData4-anchor sweep across directory / symlink / unsupported-typeflag /Tar.listpayload-skip callsites (:619/:650/:657/:671 → :767/:805/:812/:826, non-uniform +148/+155/+155/+155). - #2068 (14:18Z) — row 1312:
Tar.extractpadding loop per-chunk read (:695 → :792single-anchor, +97). - #2069 (14:24Z) — row 1309:
readBoundedEntryData4-callsite GNU long-name / long-link / PAX extended / PAX global header sweep +readEntryDatabody cap anchors (8-anchor sweep + prose update). - #2070 (14:27Z) — prose lines 1290-1295: Local guard
intro paragraph 5-anchor sweep mixing
Zip/Archive.lean(+111) andZip/Tar.lean(+29 / +91) shifts. - #2071 (14:29Z) — row 1302:
readExactStreamcallsite + Nat→USize roundtrip dual-anchor (Zip/Archive.lean:883 → :994 / :884 → :995). - #2072 (14:32Z) — row 1303:
readExact h tailSizeEOCD tail dual-anchor (:941 → :1052 / :938 → :1049, uniform +111).
Inventory: in-prose anchor refresh (1 PR) — the Boundary-Facing Subsystems → ZIP Archive Reader/Extractor prose section.
- #2033 (12:57Z) — row 1066: in-prose 'Symlink/hardlink
extraction policy' section,
Zip/Tar.lean:210 → :251(function-rename refactor: prose updated from the old callsite to the currentpartial def extractdefinition line).
Inventory-table coverage summary at wave close.
| Inventory sub-track | Coverage state at wave close |
|---|---|
| Minimized Reproducer Corpus tar block | Refresh sweep terminal at this wave (12 PRs cleared all stale Tar fixture-row anchors). |
| Minimized Reproducer Corpus zip block | Refresh sweep terminal at this wave (7 PRs cleared all stale Archive fixture-row anchors). |
| Minimized Reproducer Corpus CD/LH consistency rows | Refresh sweep terminal at this wave (2 PRs cleared the consistency quartet + usesDataDescriptor trio). |
| Decompression Limit Inventory Public extraction APIs | Archive cluster (rows 1186-1191) + Tar extractTarGzNative cluster (rows 1196-1198) refreshed in-wave; sibling Tar extract + extractTarGz cluster (rows 1192-1195) outstanding via repair-queue PR #2035 (issue #2027). |
Local guard inventory for Handle.read / Stream.read |
Structurally complete pending issue #2066 (rows 1305+1306) and issue #2067 (row 1307) unclaimed; PR #2073 (row 1304, issue #2065) and PR #2060 (row 1311, issue #2055) in repair queue. |
| Boundary-Facing Subsystems → ZIP Archive Reader/Extractor prose | Row 1066 refreshed in-wave (PR #2033); other in-prose anchors not touched this wave. |
Wave-character callout. This is the first
maintenance-only wave of the post-#1928 closure arc — no
feature PRs, no fixture additions, no Zip/ source edits.
The contrast with the immediately-prior waves is sharp:
post-#1971 carried 1 terminal closure (PR #1979 closing the
PAX value-side family 2/2) plus its post-closure cadence;
post-#1931 carried 2 terminal closures (PR #1957 5-slot UStar,
PR #1953 2-slot GNU) + 1 defense-in-depth-extension family
closure (PR #1944 uname slot); post-#1928 carried the opening
3 closures (PR #1880 UStar code-guard, PR #1934 linkname,
PR #1937 prefix). With the post-#1971 wave terminating the
named per-slot Tar interior-NUL closure arc and the planner
queue empty of Track E feature issues, the post-#1989 wave
is the natural wind-down: 33 of 35 PRs (~94%) are inventory
re-anchor PRs, 1 is the third-terminal-closure Recent-wins
bullet (#1996 — the queued post-#1979 PAX value-side bullet
deferred from the post-#1971 wave's tail), and 1 is the
post-issue-creation tail-deferral skill (#1997 — also
deferred from the prior wave). The wave demonstrates that
when concentrated parser-source growth shifts multiple
inventory line citations at once, the one-PR-per-row /
one-PR-per-cluster cadence scales: 33 inventory PRs in
~3.5 h, average ~6.5 min between merges, no merge-conflict
collisions on the inventory file (which is append-only at
the row level).
Repair queue snapshot at wave close. 19 PRs in
conflict / failing CI per coordination list-pr-repair (18
merge-conflicts + 1 failed CI on PR #1876). Carry-over
entirely from prior waves; this wave neither added new
entrants nor salvaged any existing entries. The repair
queue's 5 oldest entries (#1725 / #1743 / #1755 / #1764 /
#1771) trace back to the Track E CD-parse cascade that
predates the post-#1928 closure arc, while the 4 most recent
entries (#2073 / #2060 / #2035 / #2013) are sibling-row
inventory PRs that conflicted with in-wave landings on the
same SECURITY_INVENTORY.md cluster.
Post-issue-creation tail (out of wave scope). Per the
post-issue-creation tail deferral pattern codified in
.claude/skills/summarize-flow/SKILL.md (landed in this wave
as PR #1997 — the first wave to apply the codified pattern
in the planning loop), any PRs that land between issue
#2074's 14:41Z creation and this wave PR's submission are
deferred to the next wave block. At the moment of issue
#2074's creation, the unclaimed planner queue listed two
inventory issues for the still-pending Local guard table
rows (#2066 rows 1305+1306, #2067 row 1307); their PRs
will appear in the next wave block per the issue-body-as-
source-of-truth invariant. This wave's scope is frozen at
the 35 PRs enumerated above.
Quality metrics: 0 sorries across Zip/ (unchanged — none
of these PRs touched proofs); 0 runtime ]! across
Zip/Native/ and Zip/*.lean (unchanged — no source edits
this wave). Fixtures in testdata/zip/malformed/ unchanged
at 47; fixtures in testdata/tar/malformed/ unchanged at 24.
Spec-line counts unchanged (no Zip/Spec/ PRs). Type mix
across the 35 PRs: inventory 33 / Recent-wins bullet 1
(#1996) / skill landing 1 (#1997) — ratio ~94% inventory.
At wave close: repair queue at 19 PRs (carry-over); feature
queue at 2 unclaimed inventory issues (#2066, #2067 — both
Local guard table rows); review queue empty (no closure
events generated paired-review entries this wave). lake build
clean (191 jobs); lake exe test all green; bash scripts/check-inventory-links.sh
exits 0 with 40 warnings (pre-existing line-anchor drift,
slightly reduced from prior wave's 104 as 33 inventory
re-anchor PRs cleared most stale citations). Toolchain
v4.29.1.
12-PR batch (Apr 25): post-#2074 wave — second consecutive maintenance-only wave of the post-#1928 closure arc; pure inventory-anchor refresh wave (no terminal closures, no Track E feature work, no source code) covering three narrower shift sub-classes (CD-parse-guard +43–+45 / Archive late-section +111 / ZIP64-extra +49) (summarize #2108):
Twelve PRs merged in the ~58-min window between PR #2080
(15:02:09Z 2026-04-25) and PR #2105 (16:00:33Z 2026-04-25),
opening immediately after PR #2074 (the prior summarize wave
PR) merged at 14:51:36Z. The wave is structurally a shift-
class refinement of the prior post-#1989 wave: where the
35-PR post-#1989 wave broad-stroked across 8 inventory
sub-tracks at once, the post-#2074 wave is concentrated on
three narrower shift classes that landed slightly later in the
post-#1813 / #1857 / #1886 / #1903 / #1921 CD-parse-guard
wave's tail — specifically the +43–+45 fine-grained CD-parse
shift (5 PRs), the +49 ZIP64-extra body shift (2 PRs sharing
sibling parseZip64Extra arms), and the +111 Archive
late-section shift on three remaining EOCD / LH duplicate-block
guards (3 PRs). One Local guard cluster row (1305+1306,
PR #2080) and one cap-of-EOF guard row (1442, PR #2090) round
out the wave at +111. There is no terminal closure in this
wave; the post-#1928 Tar interior-NUL closure arc remains
structurally complete (terminal at PR #1979 in the
post-#1971 wave), and the planner-side activity continues to
be anchor maintenance. Source code (Zip/, c/, testdata/)
was not touched by any of the 12 PRs; grep -rc sorry Zip/
stayed at 0 throughout.
Inventory: CD-parse-guard +43–+45 sub-class (5 PRs) — the
Zip/Archive.lean CD-parse late-arm guards that took the +43
or +45 shift from the CD-parse-guard wave's fine-grained tail.
- #2082 (15:02Z) — row 1428:
cd-entry-disknum-mismatch.zipCD-entry diskNumberStart-mismatch throw (:549 → :592, +43). - #2089 (15:23Z) — row 1430:
cd-entry-localoffset-past-cdstart.zipoverlap-CD throw (:728 → :771, +43). - #2101 (15:47Z) — row 1454:
invalid-utf8-with-flag.zipinvalid-UTF-8 throw (:595 → :638, +43). - #2104 (15:55Z) — row 1443:
cd-patched-data-flag.zippatched-data flag bit 5 throw (:682 → :725, +43). - #2105 (16:00Z) — row 1440:
cd-nul-in-name.zipCD-name NUL-byte throw (:589 → :632, +43).
Inventory: Archive late-section +111 sub-class (3 PRs) — the
Zip/Archive.lean EOCD / LH-duplicate-block guards that took
the +111 shift on three rows the prior wave did not reach.
- #2091 (15:28Z) — row 1446:
eocd-disknum-mismatch.zipEOCD disk-number throw (:480 → :523, +43; Archive late-section sub-class but small +43 line shift on this particular row because the throw lives in the EOCD-validate early-arm rather than the late-arm cluster). - #2093 (15:32Z) — row 1448:
eocd-numentries-thisdisk-mismatch.zipEOCD numEntriesThisDisk throw (:484 → :533, +49). - #2098 (15:40Z) — row 1455:
lh-zip64-extra-duplicate.zipLH duplicate ZIP64 local extra throw (:1013 → :1124, +111; LH-side counterpart of CD-side row 1445 in the ZIP64-extra sub-class below).
Inventory: ZIP64-extra +49 sub-class (2 PRs) — sibling
parseZip64Extra arms at the same function body, +49 shift.
- #2099 (15:42Z) — row 1465:
zip64-extra-oversized-datasize.zipmalformed-extra throw (:697 → :746). - #2100 (15:45Z) — row 1445:
cd-zip64-extra-duplicate.zipCD-side duplicate-block throw (:694 → :743; CD-side counterpart of LH-side row 1455 PR #2098 above).
Inventory: Local guard cluster +111 (1 PR, dual-row sweep) —
the readBoundedSpanFromHandle LH-guard cluster rows.
- #2080 (15:02Z) — rows 1305+1306:
readBoundedSpanFromHandlelocal-header + local-name+extra for{label}rows, 2-row contiguous-block sweep (:967 → :1078row 1305, three occurrences per row;:995 → :1106row 1306, three occurrences per row; uniform +111 shift).
Inventory: cap-of-EOF +111 (1 PR) — the
cdOffset + cdSize ≤ fileSize guard row.
- #2090 (15:26Z) — row 1442:
cd-past-eof.ziparchive-layout invariant throw (:946 → :1057on the unless line, +111; convention pinned by in-flight repair-queue PR #2073's row 1304 which uses the identical line shift on the same guard).
Inventory-table coverage summary at wave close.
| Inventory sub-track | Coverage state at wave close |
|---|---|
| Minimized Reproducer Corpus zip block | 11 of 12 wave PRs landed here; sibling rows 1429 / 1447 still pending in repair-queue PRs #2083 / #2092. |
Local guard inventory for Handle.read / Stream.read |
Rows 1305+1306 refreshed in-wave (PR #2080); rows 1304 / 1307 / 1311 still pending in repair-queue PRs #2073 / #2081 / #2060. |
| Decompression Limit Inventory Public extraction APIs | Unchanged from prior wave: Tar extract + extractTarGz cluster (rows 1192-1195) still outstanding via repair-queue PR #2035. |
| Other sub-tracks | Untouched this wave (CD/LH consistency rows, in-prose anchors, Tar fixture rows already terminal at post-#1989 wave; pre-existing UStar interior-NUL via PR #1959). |
Wave-character callout. This is the second consecutive
maintenance-only wave of the post-#1928 closure arc, and
the first wave to apply the post-issue-creation tail
deferral pattern as a routine cadence (the post-#1989 wave
applied it as the inaugural wave under the codified skill
landed by PR #1997). All 12 PRs are inventory re-anchor PRs
— no fixtures, no Track E feature, no Zip/ source edits, no
skill landing, no Recent-wins bullet, no paired-review entry.
The wave's narrower shift-class character (3 sub-classes vs.
the prior wave's 8 sub-tracks) reflects natural saturation:
the prior wave cleared the bulk of the +111 Archive
late-section / +97 Tar parser growth shifts, leaving the
post-#2074 wave to mop up the +43–+45 fine-grained tail and
the +49 ZIP64-extra arms. With 27 inventory-link warnings
remaining at wave close (down from the prior wave's 40 and
the post-#1971 wave's 104), the maintenance saturation arc is
nearing termination.
Repair queue snapshot at wave close. 22 PRs in
conflict / failing CI per coordination list-pr-repair (21
merge-conflicts + 1 failed CI on PR #1876). The queue grew by
3 net entrants since the prior wave close (PRs #2092 / #2083 /
#2081 freshly conflicted on SECURITY_INVENTORY.md,
sibling-row inventory PRs that landed in-wave on the same
clusters; PR #2073 was already in the queue at the prior wave
close). The 5 oldest entries (#1725 / #1743 / #1755 / #1764 /
#1771) trace back to the Track E CD-parse cascade that predates
the post-#1928 closure arc; the 6 most recent entries (#2092 /
#2083 / #2081 / #2073 / #2060 / #2035) are all sibling-row
inventory PRs that conflicted with in-wave landings on the
same SECURITY_INVENTORY.md clusters. Pattern: each wave's
inventory landings reliably generate a fixed number of repair-
queue entrants from sibling-row PRs that were in flight at
wave open — the inventory conflict tax.
Post-issue-creation tail (out of wave scope). Per the
post-issue-creation tail deferral pattern in
.claude/skills/summarize-flow/SKILL.md, any PRs that land
between issue #2108's 16:12:15Z creation and this wave PR's
submission are deferred to the next wave block. At the moment
this wave PR is submitted, PR #2109 has merged at
16:17:17Z (the eocd-zip64-override-nosentinel.zip row-1453
re-anchor sweep, planner-queued issue #2102 from the same
planning cycle that created issue #2108). PR #2109 is
out of wave scope per the issue-body-as-source-of-truth
invariant — the issue's deliverables enumeration fixes the
wave's PR list at 12 PRs. The unclaimed planner queue at
issue #2108's creation listed three additional inventory
issues (#2103 row 1426 cd-empty-entry-crc-nonzero.zip,
#2106 row 1441 cd-path-unsafe.zip, #2107 Tar.lean row 1407
pax-path-nul-in-value.tar); PR #2109 plus those three issues'
PRs (when landed) will appear in the next wave block. This
wave's scope is frozen at the 12 PRs enumerated above.
Quality metrics: 0 sorries across Zip/ (unchanged — none
of these PRs touched proofs); 0 runtime ]! across
Zip/Native/ and Zip/*.lean (unchanged — no source edits
this wave). Fixtures in testdata/zip/malformed/ unchanged
at 47; fixtures in testdata/tar/malformed/ unchanged at 24.
Spec-line counts unchanged (no Zip/Spec/ PRs). Type mix
across the 12 PRs: inventory 12 / non-inventory 0 — ratio
100% inventory (vs. ~94% in the prior wave; reaching pure
saturation). At wave close: repair queue at 22 PRs (up from
19 at prior wave close); feature queue at 4 unclaimed
inventory issues (#2102 / #2103 / #2106 / #2107 — three
Archive corpus rows + one Tar.lean PAX row); review queue
empty (no closure events). lake build clean (191 jobs);
lake exe test all green; bash scripts/check-inventory-links.sh
exits 0 with 27 warnings (down from the prior wave's 40 as
the 12 re-anchor PRs cleared most remaining stale citations).
Toolchain v4.29.1.
26-PR batch (Apr 25): post-#2110 wave — third consecutive maintenance-only wave of the post-#1928 closure arc; pure inventory-anchor refresh wave (no terminal closures, no Track E feature work, no source code) covering five distinct shift sub-classes (CD-parse-guard +43-67 / Archive late-section +25-225 / PAX-UStar parser-growth +16-23 / Tar parser-growth +95-117 / writer-side ±4-6) plus a multi-anchor narrative-paragraph cluster (3 PRs); longest pure-doc inventory wave to date (summarize #2168):
Twenty-six PRs merged in the ~2h 47min window between PR #2111
(16:28:07Z 2026-04-25) and PR #2164 (19:15:16Z 2026-04-25),
opening immediately after PR #2110 (the prior summarize wave
PR) merged at 16:25:20Z. The wave is structurally a breadth
expansion of the prior post-#2074 wave: where the 12-PR
post-#2074 wave concentrated on three narrow shift classes
(CD-parse-guard +43-45, Archive late-section +111, ZIP64-extra
+49), the post-#2110 wave reaches across five distinct shift
classes plus a multi-anchor narrative-paragraph cluster of
three PRs that consolidate sibling-deferral chains within a
single paragraph. The 26 PRs split as: 6 CD-parse-guard +43-67
PRs, 7 Archive late-section +25-225 PRs (including a notable
+225 outlier on the row-478 stored-method size invariant
narrative paragraph), 6 PAX/UStar parser-growth +1-23 PRs, 2
Tar parser-growth +95-117 PRs, 2 writer-side ±4-6 PRs, and 3
multi-anchor narrative-paragraph sweeps — including PR #2154's
first 5-anchor in-row sweep in the inventory family (the
cd-path-unsafe narrative paragraph). There is no terminal
closure in this wave; the post-#1928 Tar interior-NUL
closure arc remains structurally complete (terminal at PR
#1979 in the post-#1971 wave), and the planner-side activity
continues to be anchor maintenance. Source code (Zip/,
c/, testdata/) was not touched by any of the 26 PRs;
grep -rcF sorry Zip/ stayed at 0 throughout.
Inventory: CD-parse-guard +43-67 sub-class (6 PRs) — the
Zip/Archive.lean CD-parse late-arm guards on the +43, +44,
+45, and +67 fine-grained tail of the CD-parse-guard wave.
- #2114 (16:40Z) — row 1441:
cd-path-unsafe.ziprow-table primary cell (:631 → :676, +45). - #2120 (16:55Z) — line 652:
cd-patched-data-flag.zipnarrative-paragraph follow-up to PR #2104's row-table update (:682 → :725, +43). - #2121 (17:00Z) — line 677:
cd-nul-in-name.zipnarrative-paragraph follow-up to PR #2105's row-table update (:589 → :632, +43). - #2137 (17:47Z) — line 486: CD-entry compression-method
allowlist narrative paragraph (
:668 → :713, +45; prose-paragraph follow-up to PR #2053's row-1424 row-table update). - #2146 (18:14Z) — line 478:
cd-empty-name.zipnarrative-paragraph dual-anchor (:535 → :578, +43;:570 → :614, +44). - #2161 (19:03Z) — line 414: ZIP64-extra dataSize
exactness narrative (
:428 → :495, +67 — fine-grained tail of the CD-parse-guard wave on theparseZip64Extraarm).
Inventory: Archive late-section +25-225 sub-class (7 PRs) —
the Zip/Archive.lean late-section guards that took +25, +111,
or +225 shifts in the post-#2110 cumulative wave.
- #2111 (16:28Z) — row 1426:
cd-empty-entry-crc-nonzero.ziprow-table trailing cite'CRC32 mismatch'throw (:1088 → :1199, +111). - #2122 (17:02Z) — line 491: CD-parse method allowlist
narrative
'unsupported method'throw (:1083 → :1194, +111). - #2129 (17:24Z) — row 1441:
cd-path-unsafe.ziprow-table trailing dual-anchor on the extract-timeBinary.isPathSafecall sites (:1133 → :1244;:1107 → :1248). - #2131 (17:30Z) — line 478: stored-method size invariant
narrative
'size mismatch'throw (:976 → :1201, +225 — broadest single-anchor shift in the wave). - #2132 (17:33Z) — line 529:
localOffset+30 ≤ cdOffsetnarrative'bad local header signature'throw (:970 → :1081, +111). - #2133 (17:35Z) — line 814: empty-entry CRC invariant
narrative
'CRC32 mismatch'throw (:1088 → :1199, +111). - #2162 (19:07Z) — line 347: ZIP64 EOCD64 overlap-locator
narrative
'ZIP64 EOCD64 record overlaps locator'throw (:306 → :331, +25 — small tail of the Archive late-section shift on a paragraph that fell outside the +111 cluster).
Inventory: PAX/UStar parser-growth +1-23 sub-class (6 PRs) —
the Zip/Tar.lean PAX and UStar parser guards that took small
shifts (+1, +16, +20, +22, +23) from the post-#1899 / post-#1944
PAX-NUL-guard insertion wave.
- #2115 (16:46Z) — row 1407:
pax-path-nul-in-value.tarparsePaxRecordskeyBytes NUL guard (:122 → :145, +23). - #2130 (17:28Z) — row 1402:
pax-inconsistent-length.tardual-anchor splitting collapsed citations across distinct guard sites (:118 → :121eqPos scan loop;:118 → :126silent-skip guard). - #2138 (17:50Z) — line 992: PAX duplicate-key narrative
forEntries typePaxExtendedrethrow (:669 → :691, +22). - #2145 (18:11Z) — row 1400:
pax-duplicate-path.tardual-anchor (:147 → :148parsePaxRecords duplicate-key guard;:669 → :691forEntries rethrow, +22). - #2155 (18:39Z) — row 1411:
ustar-linkname-nul-in-name.tardual-anchor (:517 → :533linkname throw;:515 → :531name-arm guard reference, +16). - #2156 (18:42Z) — row 1413:
ustar-prefix-nul-in-name.tartriple-anchor (:519 → :535prefix throw;:515 → :531name-arm guard;:522 → :542pfx ++ "/" ++ namecombination, +16/+16/+20).
Inventory: Tar parser-growth +95-117 sub-class (2 PRs) — the
Zip/Tar.lean Tar.extract-side guards that took +95 / +97 /
+117 shifts on the Binary.isPathSafe rejection sites and the
per-chunk skipEntryData cap.
- #2123 (17:05Z) — rows 1415 / 1419 / 1420:
testdata/tar/security/{backslash-slip,tar-absolute,tar-slip}.tarthree-row same-anchor sweep (:639 → :756, +117). - #2139 (17:54Z) — row 1310:
skipEntryDatarow trailing dual-anchor (:539 → :634, +95 per-chunk cap;:661 → :758, +97 Tar.extract maxEntrySize check).
Inventory: writer-side ±4-6 sub-class (2 PRs) — the small
writer-side shift class on Zip/Archive.lean writer-internal
hard-coded constants and Checksum.crc32 call sites.
- #2153 (18:33Z) — line 853:
cd-deflate-zero-compsize.zipwriter-sideChecksum.crc32 0 fileDatacall (:195 → :189, -6). - #2164 (19:15Z) — line 328:
zip64-eocd64-versionneededwriter-side hard-coded45forversionNeededToExtractinsidewriteEndRecords(:150 → :154, +4).
Inventory: Multi-anchor narrative-paragraph sweeps (3 PRs) — in-row consolidation of sibling-deferral chains that mix multiple shift classes within a single paragraph.
- #2147 (18:17Z) — line 794:
cd-empty-entry-crc-nonzero.ziptriple-anchor (:794 → :820parse-time throw, +26 CD-parse- guard;:195 → :189×2 writer-sideChecksum.crc32call, -6 writer-side; mixes CD-parse with writer-side classes). - #2154 (18:36Z) — line 706-740:
cd-path-unsafe.zip5-anchor sweep (:631 → :676,:651 → :696,:1133 → :1244,:1107 → :1248,:1131 → :1242; first 5-anchor in-row consolidation in the inventory family, mixing CD-parse-guard with Archive late-section). - #2157 (18:44Z) — line 419-446: duplicate ZIP64 (headerId
0x0001) extra-block rejection narrative — triple-anchor with non-uniform shifts (:406 → :449+43 helper def;:693 → :742+49 CD-side caller;:983 → :1123+140 LH-side caller; mixes three shift classes in one PR).
Inventory-table coverage summary at wave close.
| Inventory sub-track | Coverage state at wave close |
|---|---|
| Recommended policy narrative paragraphs | 14 of 26 wave PRs landed here; sibling narrative paragraphs at lines 296 / 328 (writer-side closed in-wave by #2164) / 347 (closed in-wave by #2162) / 414 (closed in-wave by #2161) form the cluster, with line 296 (versionMadeBy upper-bound) outstanding via planner-queued #2163. |
| Minimized Reproducer Corpus zip block (rows 1426-1465) | 4 of 26 wave PRs landed primary cells (#2111, #2114); sibling rows 1429 / 1447 still pending in repair-queue PRs #2083 / #2092 / #2144. |
| Minimized Reproducer Corpus tar block (rows 1393-1420) | 6 of 26 wave PRs landed here (#2115, #2123, #2130, #2145, #2155, #2156); UStar interior-NUL row 1412 outstanding via repair-queue PR #2144 (terminal 6-anchor sweep). |
Local guard inventory for Handle.read / Stream.read |
Trailing-anchor row 1310 closed by #2139; rows 1304 / 1307 / 1311 still pending in repair-queue PRs #2073 / #2081 / #2060. |
| Standalone audit sections (lines 1126 / 1140 / 1156) | All three queued at wave close as planner-issues #2165 / #2166 / #2167; not landed in-wave. |
| Other sub-tracks | Untouched this wave (Decompression Limit Inventory cluster rows 1192-1195 still in repair-queue PR #2035; intro-paragraph creator-side h.read row 1285 queued as #2160). |
Wave-character callout. This is the third consecutive
maintenance-only wave of the post-#1928 closure arc, and the
longest pure-doc inventory-anchor refresh wave to date (26
PRs vs. the prior post-#2074 wave's 12, the broad-stroke
post-#1989 wave's 35, the post-#1971 wave's 11, and the
post-#1964 wave's 15). All 26 PRs are inventory re-anchor
PRs — no fixtures, no Track E feature, no Zip/ source edits,
no skill landing, no Recent-wins bullet, no paired-review
entry. The wave's broader shift-class character (5 sub-classes
plus the multi-anchor cluster vs. the prior wave's 3
sub-classes) reflects the natural saturation pattern: the
prior wave cleared the bulk of the +43-45 / +111 / +49
clusters on the row-table side, and the post-#2110 wave
reaches across to the narrative-paragraph side (16 of 26 PRs
edit Recommended policy narrative paragraphs vs. row-table
primary cells), with PR #2154's first 5-anchor in-row
sweep marking a deliberate consolidation of sibling-deferral
chains within a single paragraph. With 18 inventory-link
warnings remaining at wave close (down from the prior wave's
27 and the post-#1971 wave's 104), the maintenance saturation
arc is in its terminal phase — the residual 18 warnings trace
exclusively to the in-flight repair-queue PRs and a handful of
trailing in-source line-content heuristic mismatches.
Repair queue snapshot at wave close. 23 PRs in
conflict / failing CI per coordination list-pr-repair (22
merge-conflicts + 1 failed CI on PR #1876). The queue grew by
1 net entrant since the prior wave close (PR #2144, the row
1412 6-anchor terminal sweep that conflicted on the same
SECURITY_INVENTORY.md cluster as the in-wave row 1411 / 1413
sweeps PRs #2155 / #2156 — the same inventory conflict tax
the prior wave flagged). The 5 oldest entries (#1725 / #1743 /
#1755 / #1764 / #1771) trace back to the Track E CD-parse
cascade that predates the post-#1928 closure arc; the recent
entries (#2092 / #2083 / #2081 / #2073 / #2060 / #2035 / #2144)
are sibling-row inventory PRs that conflicted with in-wave
landings on the same SECURITY_INVENTORY.md clusters.
Post-issue-creation tail (out of wave scope). Per the
post-issue-creation tail deferral pattern in
.claude/skills/summarize-flow/SKILL.md, any PRs that land
between issue #2168's 19:22:20Z creation and this wave PR's
submission are deferred to the next wave block. At the moment
issue #2168 was claimed (19:25Z), no PRs had merged after
the wave's terminal PR #2164 (19:15:16Z) — the
post-issue-creation tail is empty. The wave's scope is
frozen at the 26 PRs the issue body enumerates with no
deferrals required. The unclaimed planner queue at issue
#2168's creation listed six additional inventory issues for
the next wave seed (#2160 row 1285 creator-side h.read
intro-paragraph; #2163 line 296 ZIP64 EOCD64 versionMadeBy
narrative; #2165 / #2166 / #2167 the three standalone
audit-section refreshes at lines 1126 / 1140 / 1156; sibling
narrative-paragraph re-anchor at line 296 deferred from #2161
/ #2164's wave PRs); their PRs (when landed) will appear in
the next wave block.
Quality metrics: 0 sorries across Zip/ (unchanged — none of
these PRs touched proofs); 0 runtime ]! across Zip/Native/
and Zip/*.lean (unchanged — no source edits this wave).
Fixtures in testdata/zip/malformed/ unchanged at 47;
fixtures in testdata/tar/malformed/ unchanged at 24.
Spec-line counts unchanged (no Zip/Spec/ PRs). Type mix
across the 26 PRs: inventory 26 / non-inventory 0 — ratio
100% inventory (third consecutive 100% wave; prior wave was
also 100%, post-#1989 wave was ~94%). At wave close: repair
queue at 23 PRs (up from 22 at prior wave close — net +1 from
PR #2144); feature queue at 6 unclaimed inventory issues
(#2160 / #2163 / #2165 / #2166 / #2167 plus the planner-queued
sibling line-296 deferral); review queue empty (no closure
events). bash scripts/check-inventory-links.sh exits 0 with
18 warnings (down from the prior wave's 27 as the 26 re-anchor
PRs cleared most remaining stale citations on the
narrative-paragraph and row-table sides). Toolchain v4.29.1.
12-PR batch (Apr 25): post-#2168 wave — fourth consecutive maintenance-only wave of the post-#1928 closure arc; pure inventory-anchor refresh / heuristic-suppression wave (no terminal closures, no Track E feature work, no source / test / fixture changes) covering five distinct inventory-section sub-classes (standalone audit sections / Recommended-policy narrative paragraphs / Local guard intro + table parenthetical / Minimized Reproducer Corpus rows / drift-detector heuristic suppressions) (summarize #2191):
Twelve PRs merged in the ~1h 33min window between PR #2170
(19:35:10Z 2026-04-25) and PR #2190 (21:08:32Z 2026-04-25),
opening immediately after PR #2168 (the prior summarize wave PR)
merged at 19:32:20Z. The wave is structurally a pivot off the
shift-class axis that organized the prior three pure-doc waves
(post-#1989 / post-#2074 / post-#2110): where those waves split
their PRs by line-shift magnitude (CD-parse-guard +43-67 /
Archive late-section +25-225 / PAX-UStar parser-growth +16-23 /
Tar parser-growth +95-117 / writer-side ±4-6), the post-#2168
wave instead splits its 12 PRs across five inventory-section
sub-classes — the structural sections of SECURITY_INVENTORY.md
that the anchors live in, rather than the line-shift magnitudes
on the source side. The 12 PRs split as: 3 standalone-audit-section
refreshes (lines 1126 / 1140 / 1156), 3 Recommended-policy
narrative-paragraph refreshes (lines 296 / 515 / 630), 2 Local
guard sub-section refreshes (intro paragraph at line 1285 +
row-1302 parenthetical), 2 Minimized Reproducer Corpus refreshes
(rows 1411 / 1413 — UStar interior-NUL fixture rows), and 2
drift-detector heuristic suppressions (rows 1309 / 1310 —
readEntryData and skipEntryData per-chunk-cap body anchors).
There is no terminal closure in this wave; the post-#1928
Tar interior-NUL closure arc remains structurally complete,
and the planner-side activity continues to be anchor maintenance.
Source code (Zip/, c/, testdata/) was not touched by any
of the 12 PRs; grep -rcF sorry Zip/ stayed at 0 throughout.
Inventory: standalone-audit-section sub-class (3 PRs) — the
SECURITY_INVENTORY.md standalone audit sections at lines 1126
/ 1140 / 1156 (sections 1 / 2 / 3 of the audit checklist), pinning
the def-line anchor precedent for those sections.
- #2172 (19:39Z) — line 1126: section 1 "ZIP metadata to
Handle.read" (Zip/Archive.lean:491 → :980, +489 from cumulative parser-growth + Archive late-section wave); first standalone-audit-section anchor refresh, sets the def-line precedent. - #2174 (19:53Z) — line 1140: section 2 "Tar UTF-8 partial
functions" (
Zip/Tar.lean:246 → :317, +71 from post-#1899/#1944/#1957/#1979 Tar parser growth wave plushasInteriorNulhelper insertion);splitPathdef line. - #2177 (20:19Z) — line 1156: section 3 "Unlimited
decompression knobs" (
Zip/Archive.lean:783 → :1233, +450 from cumulative Archive late-section growth wave);Archive.extractdef line; closes the audit-section trio (sections 1 / 2 / 3 all on the def-line anchor precedent).
Inventory: Recommended-policy narrative-paragraph sub-class (3 PRs) — narrative-paragraph refreshes that restore fixture-cite ↔ narrative-cite agreement on three distinct guard sites.
- #2171 (19:37Z) — line 296: ZIP64 EOCD64
versionMadeByupper-bound throw (Zip/Archive.lean:337 → :363, +26 from later-landing throw insidefindEndOfCentralDir); corrected replacement for issue #2152 (which misattributed to the adjacent record-size paragraph). - #2181 (20:32Z) — line 515:
cd-entry-localoffset-past-cdstartper-entry archive-layout invariant (Zip/Archive.lean:728 → :771, +43 from CD-parse-guard insertion wave); restores fixture-cite ↔ narrative-cite agreement for PR #1813. - #2182 (20:35Z) — line 630:
cd-entry-internal-attrs-reservedCD-entryinternalFileAttributesreserved-bits check (Zip/Archive.lean:567 → :612, +45 from CD-parse-guard insertion wave); per-row split sibling of issue #2077 (corpus row 1429 cite, repair-queue PR #2083) — narrative-paragraph cite at line 630 vs. corpus row at line 1429 are structurally isolated.
Inventory: Local guard sub-section sub-class (2 PRs) — intro
paragraph creator-side h.read exclusion + row-1302
parenthetical bare-text re-anchor.
- #2170 (19:35Z) — line 1285: Local guard intro paragraph
creator-side
h.readexclusion (Zip/Tar.lean:466 → :599, +133 from post-#1899/#1944/#1957/#1979 Tar parser growth wave plushasInteriorNulhelper insertion). - #2178 (20:21Z) — row 1302 parenthetical: bare-text
re-anchor "(inner s.read at line 695)" → "(line 1000)"
(+305 from helper-block reordering below
parseCentralDir); structurally isolated from sibling Local guard table rows in repair-queue PRs #2073 / #2081 / #2060 / #1959.
Inventory: Minimized Reproducer Corpus sub-class (2 PRs) — the UStar interior-NUL fixture rows 1411 and 1413 (sibling-deferral follow-ups to the in-#2110-wave PRs #2155 / #2156).
- #2186 (20:54Z) — row 1411:
testdata/tar/malformed/ustar-linkname-nul-in-name.tarmixed-form 2-anchor sweep — markdown-link prefix-arm parenthetical (Zip/Tar.lean:519 → :535) + bare-text "name slot at line 515" → "line 531" (+16 fromhasInteriorNuluname/gname-arm insertion wave); sibling-deferral pattern from PR #2155 (issue #1955). - #2187 (20:56Z) — row 1413:
testdata/tar/malformed/ustar-prefix-nul-in-name.tarbare-text 3-anchor sweep — "lines 515 / 517 / 519" → "lines 531 / 533 / 535" inside per-slot family-closure clause (linker-undetected drift, +16 shift); sibling-deferral pattern from PR #2156 (which explicitly took the markdown-link form only).
Inventory: drift-detector heuristic-suppression sub-class (2
PRs) — first body-anchor table-row variant of the drift-detector
pattern previously seen on SECURITY_INVENTORY.md:1176-1178.
- #2188 (20:59Z) — row 1309:
readEntryDataper-chunk-cap body anchor (Zip/Tar.lean:256/ :267 inside while loop, def at :249 outside the ±2 window); 1-line single-anchor doc-only sweep matchingSECURITY_INVENTORY.md:1176-1178precedent — first body-anchor table-row variant of the drift-detector pattern. - #2190 (21:08Z) — row 1310:
skipEntryDataper-chunk-cap body anchor (Zip/Tar.lean:634inside while loop, def at :630 outside the ±2 window); immediate sibling of #2188 — same root-cause class.
Wave-character callout. This is the fourth consecutive
maintenance-only wave of the post-#1928 closure arc (post-#1989
35 PRs → post-#2074 12 PRs → post-#2110 26 PRs → post-#2168 12
PRs) and the shortest of the four (joint with post-#2074 at
12 PRs each). All 12 PRs are inventory re-anchor or
heuristic-suppression PRs — no fixtures, no Track E feature, no
Zip/ source edits, no skill landing, no Recent-wins bullet, no
paired-review entry. The wave's structural character (5
inventory-section sub-classes vs. the prior wave's 5 line-shift
sub-classes) reflects a deliberate axis pivot: prior waves
cleared the bulk of the +43-67 / +111 / +49 / +95-117 shift
clusters along the line-shift axis on a smaller number of
inventory sections, and the post-#2168 wave instead spans five
different structural sections of SECURITY_INVENTORY.md
(audit sections / narrative paragraphs / Local guard / corpus
rows / drift-detector comments) on a smaller number of
line-shift magnitudes. Group 1 (standalone-audit) closes the
audit-section trio at lines 1126 / 1140 / 1156, pinning the
def-line anchor precedent for those sections (planner-queued
issues #2165 / #2166 / #2167 from the prior wave). Group 5
(drift-detector) opens the body-anchor table-row sub-pattern
of the drift-detector heuristic-suppression class — the prior
applications (SECURITY_INVENTORY.md:1176-1178) were narrative
paragraphs, but the row 1309 / 1310 sweeps extend the pattern
to table rows where the cited body-line anchor is inside a
while loop with the def line outside the ±2 quoted-substring
window. With 14 inventory-link warnings remaining at wave close
(down from the prior wave's 18), the maintenance saturation arc
is in its terminal phase — the residual warnings trace
exclusively to in-flight repair-queue PRs and a handful of
trailing in-source line-content heuristic mismatches.
Repair queue snapshot at wave close. 19 PRs in conflict + 1
failing CI per coordination list-pr-repair at issue #2191
creation time (#2144 / #2092 / #2083 / #2081 / #2073 / #2060 /
#2035 / #2013 / #2006 / #1959 / #1909 / #1825 / #1820 / #1814 /
#1809 / #1802 / #1777 / #1771 / #1764 + failed-CI #1876). The
queue is structurally unchanged from prior wave close — no net
new entrants this wave because the 12 PRs landed across 5
different inventory sections without colliding with the existing
repair-queue clusters (the row 1411 / 1413 sweeps in group 4
are sibling-deferral follow-ups to in-#2110-wave PRs, not
sibling-row repair PRs). The 5 oldest entries (#1725 / #1743 /
#1755 / #1764 / #1771) trace back to the Track E CD-parse
cascade that predates the post-#1928 closure arc.
Post-issue-creation tail (out of wave scope). Per the
post-issue-creation tail deferral pattern in
.claude/skills/summarize-flow/SKILL.md, any PRs that land
between issue #2191's 21:24:00Z creation and this wave PR's
submission are deferred to the next wave block. At the moment
this wave PR is submitted, PRs #2192 (drift-detector row 1402
suppression) and #2197 (drift-detector row 1066 suppression)
have merged after the wave's terminal PR #2190 (21:08:32Z) —
both at PR-create timestamps strictly after issue creation
(#2192 created 21:27:58Z, merged 21:29:05Z; #2197 created
21:45:05Z, merged 21:46:16Z). These two are the queued
continuation of group 5 (drift-detector heuristic suppressions,
siblings of #2188 / #2190 — issues #2185 / #2189 mentioned in
the issue body as the queued continuation) and are out of
wave scope per the issue-body-as-source-of-truth invariant;
they will appear in the next wave block. Four additional
inventory issues were also created post-issue (#2193 / #2194 /
#2195 / #2196 — FFI Decompression Limit Inventory cluster rows
1177 / 1179 / 1180 / 1181 covering Gzip.decompress /
decompressStream / decompressFile + RawDeflate.decompressStream),
and remain unclaimed at wave-PR submission; their PRs (when
landed) will likewise appear in the next wave block.
Quality metrics: 0 sorries across Zip/ (unchanged — none of
these PRs touched proofs); 0 runtime ]! across Zip/Native/
and Zip/*.lean (unchanged — no source edits this wave).
Fixtures in testdata/zip/malformed/ unchanged at 47; fixtures
in testdata/tar/malformed/ unchanged at 24. Spec-line counts
unchanged (no Zip/Spec/ PRs). Type mix across the 12 PRs:
inventory 12 / non-inventory 0 — ratio 100% inventory (fourth
consecutive 100% wave; prior wave was also 100%, post-#1989
wave was ~94%). At wave close: repair queue at 20 PRs (19
merge-conflicts + 1 failed-CI on PR #1876, structurally
unchanged from prior wave close at 23 PRs net of any
intra-wave queue shifts on the prior wave's seed set);
feature queue at 2 unclaimed inventory issues at issue-#2191
creation (#2185 row 1402 drift-detector / #2189 row 1066
drift-detector — both queued separately as the post-#2188 /
#2190 continuation; both subsequently claimed and merged in
the post-issue-creation tail as PRs #2192 / #2197); review
queue empty (no closure events). bash scripts/check-inventory-links.sh exits 0 with 14 warnings
(down from the prior wave's 18 as the 12 re-anchor /
heuristic-suppression PRs cleared further stale citations
across all 5 inventory-section classes — the 4 reduction is
attributable to the 2 drift-detector suppressions plus the 2
narrative/audit-section refreshes that fell within the
pre-existing warning set). Toolchain v4.29.1.
- Multi-agent coordination via
podwith worktree-per-session isolation - GitHub-based coordination (agent-plan issues, auto-merge PRs)
- Session dispatch: planners create issues, workers claim and execute
- ~660 sessions (Feb 19 – Apr 22)
- ~653 merged PRs (approximate; authoritative count via
gh pr list) - 100% module docstring coverage across all source files
- Full linter compliance (all warnings eliminated)
- Repository split into
lean-zip+lean-zip-common+lean-zstd(#1487, 2026-03-27) - Agent skills:
lean-wf-recursion(#349),proof-review-checklist(#386, updated #925, #1325), bare-simp-resistant pattern catalog (#386),lean-zstd-patterns(#491),agent-pr-recovery(#546, updated #597),lean-zstd-spec-pattern(#623, updated #711, #840, #925),lean-monad-proofs(updated #711, #840),lean-content-preservation(#891),lean-parsing-completeness(#1127, updated #1171),proven-bounds(#1420)