Upstream punkzip 0882f03 fixes the long-standing dir-mtime limitation
via a deferred post-extraction stamping pass (windows direct
CreateFileW BACKUP_SEMANTICS + SetFileTime; Io best-effort elsewhere),
pinned by tests at the stored epoch including the -d route and the
build roundtrip. Notes bullet updated from proven-fixable to fixed.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
File mtimes preserved exactly (no regression old->new exe); atimes and
directory mtimes not preserved - the dir case is the long-standing zig
limitation, still present in 0.16 Io, but proven fixable via direct
CreateFileW(BACKUP_SEMANTICS, WRITE_ATTRIBUTES) + SetFileTime with a
deferred post-extraction pass. Also records the DOS-time-as-UTC vs
UT-extra-field semantics difference vs bsdtar for the parity suite.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Upstream punkzip 572d893 (v2.3.0, 130/130) turns the January build
scaffolding into a working streaming recursive zip writer per user
decision (finish rather than remove - the shipped tool should be useful
from the start). Progress records the design (per-member streaming,
sorted deterministic entries, dir members, zip64 refusal) plus the
second zig-0.16 trap found en route (OpenFileOptions.allow_directory
defaults true, breaking file-vs-dir probes). Remaining-for-acceptance
list now starts at vendoring.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Upstream punkzip 7f88096 (v2.2.0, suite 128/128) delivers both
upstream-side prerequisites from the goal's Approach: the
extract-to-directory argument and the machine-readable listing mode
with its format pinned by CLI tests. Progress records the v1 porcelain
format contract for the parity-suite work; remaining-for-acceptance
list trimmed accordingly. Also noted: user-committed reference/
hwzip 2.4 C source upstream (cb2a731).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Upstream punkzip commits 3b87087 (0.16.0 port, floor moved, Init/Io
threading, build-graph install-order fix, msys overlapped-stdout fix,
v2.1.3, verified 841-member CRC extraction from the file-relative
piperepl kit) and 2e49c75 (three prefixed-zip container tests pinning
base_offset for both offset conventions; suite 125/125). Notes gain the
overlapped-stdout trap record (applies to the G-128 tool as well);
Progress section added with the remaining-for-acceptance list.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Status proposed -> active in the index with the detail-file mirror
updated. Pre-activation baseline is recorded in the detail file Notes;
first work item is the zig 0.16 port of the upstream checkout.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Upstream c:/repo/jn/zig/punkzip commits e9b5b553 (0.15.2 migration
completed, zig build test 122/122 green - bitstream wiring fixed, CLI
suite compiling and pinning current output) and 78453fc0 (localtime
banner restored dependency-free on stderr; zeit stays out per user
decision). Context refreshed; Notes record the decisions, the
stdout/stderr contract, and the known upstream gaps (base_offset
untested zig-side, build subcommand scaffolding, 0.16 argsAlloc).
Non-contract detail-file updates only; status stays proposed -
activation flip awaits user confirmation. goals_lint clean.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
punk::zip could write a zip and split an executable-prefixed one, but nothing in the tree
could READ a zip without zipfs, vfs::zip or tcllib's zipfile::decode - a system package
punkshell does not vendor, with exactly one consumer: make.tcl's zipfs-less kit
extraction. Measured at drafting: msys2's /usr/bin/tclsh8.6 has no tcllib, so a bake
driven from that host could not lift the runtime's own zip (its tcl_library) into the kit
and the artifact did not boot.
punk::zip 0.1.1 -> 0.2.0 adds three commands, stock Tcl only. archive_info reports where
the archive begins inside its host file and which offset convention it uses; members
walks the central directory and returns one dict per entry without decompressing
anything; unzip extracts all or part of it with every member's crc32 verified,
directories materialized, and the writer's 2MB whole-member-vs-streaming threshold
mirrored. All three accept a plain .zip or a zip attached to an executable or script
prefix, so "what is inside this kit / runtime / modpod" is now answerable on a runtime
without zipfs.
The offset derivation is stated once and shared: offsetbase = cdiroffset - diroffset.
Positive means an external preamble with archive-relative offsets, zero means the offsets
are file-relative (or there is no preamble); dataoffset is the split point either way.
Because every surface reads the ORIGINAL file at that base, the file-relative shape stops
being a special case - the broken split-off intermediate is never produced.
tclsh90b4_piperepl.exe is the store's one file-relative artifact: the old
split-then-tcllib path still fails on it with exactly "Bad zip file. Bad closure.", and
it now extracts all 841 members crc-verified in 735ms.
extract_preamble consumes that machinery instead of carrying its own scan, and two
defects went with the refactor: it read 28 bytes of a 30-byte local file header, and it
derived the file-relative base from the FIRST central directory record only (its own
'#todo! loop through all cdr file headers'). The shared walk takes the minimum over ALL
records and then validates that a PK\3\4 really sits there. Its five puts stdout debug
lines are gone.
Refusals are decided for every selected member before any file is opened, so an
unsupported archive cannot leave a half-populated directory: encrypted entries, zip64
size/offset fields, unknown compression methods, crc mismatch (partial file removed) and
member paths that would escape the target are each named. Store and deflate only,
mirroring the writer's own primitives.
make.tcl's zipfs-less extraction now calls punk::zip::unzip on the original runtime and no
longer creates the extracted_<runtime>.zip intermediate. No punkshell code requires
zipfile::decode any more. Two categories of hit remain and are deliberate: tcllib's own
zip/decode.tcl shipped as a LIBRARY inside some .vfs payloads, and one guarded optional
probe inside the vendored third-party ooxml1.10/ooxml.tcl.
First tests punk::zip has ever had (src/tests/modules/punk/zip/), covering the three
archive shapes from one source tree, the introspection output, the named refusals, and
the mkzip -> read round trip - the first coverage that what punkshell WRITES is readable.
Verification, all 2026-07-26. Reference host for the dependency-free claim: msys2
tclsh8.6 8.6.12, probed as zipfs / vfs::zip / zipfile::decode all ABSENT - suite driven
there harness-free, 28 passed, 2 skipped (the zipfs-gated cross-checks only), 0 failed.
30/30 under Tcl 9.0.3, 28 + 2 skipped under native 8.6. Full source-tree suite after the
change: 1142 tests, 1 failure - core/tcl exec-14.3, the documented baseline. Independent
cross-check: 868 members of tclsh905.exe extracted byte-identical against the same
archive mounted with tcl zipfs. Bake demonstration: 'bake -confirm 0 punk905' driven by
the msys2 tclsh8.6 (host=msys-x86_64, target=win32-x86_64) produced
extracted_tclsh905.exe/{tcl_library,lib} with no intermediate .zip, and the deployed kit
boots with tcl_library at //zipfs:/app/tcl_library, 95 encodings, msgcat 1.7.1, http
2.10.2 - and reads itself with the new reader.
Trap worth keeping: zlib stream inflate's 'get' returns only what the stream has already
produced, so one get per put silently truncates a large member to the first few chunks
(measured 327680 of 5048890 bytes on both 8.6.12 and 9.0.3). The inner drain loop is
load-bearing, and the >2MB streaming test exists to catch its removal.
Standing question answered and pinned: zipfs does NOT misidentify punk::zip::mkzip
directories. It keys on the trailing slash (which punk::zip::walk always emits), not the
stored permission bits, exactly as the current cli-999999.0a1.0.tm wording says - the
older, less precise wording survives only in frozen module snapshots under
src/vfs/mkzipfix.vfs and src/vfs/project.vfs. The real difference from zipfs mkzip is
that zipfs writes no directory entries at all.
The achieved flip archives the entry and the detail file, and sweeps the live tier: G-034
(the reader is its extract-rather-than-mount alternative, plus the measured finding that
make.tcl on 8.6 cannot mount its own templates modpod), G-101 (the read half of a
zipfs-less 8.6 container is solved), G-125 (the gate now has a named failure reason to
report), G-126 (the member-dict keys and fixture set its parity suite must reproduce, and
a back-pointer to G-128 whose only recorded bridge was this goal), G-128 (archive_info
answers "is this artifact safe to move the overlay of?" in one call).
One acceptance clause is reported for review rather than claimed silently: "NO
zipfile::decode requirement remains anywhere in the shipped tree" is satisfied for
punkshell's own code, with the vendored-third-party residue named above.
The archived detail file's diff is a rename plus the flip's evidence write-up.
Assisted-by: harness=claude; primary-model=claude-opus-5[1m]; api-location=anthropic.com
Three goals arising from the G-122 work, each drafted with its overlap survey and
approved before writing.
G-124 - dependency-free zip reading in punk::zip. Nothing in the tree can READ a zip
without zipfs, vfs::zip or tcllib's zipfile::decode, which is a system package punkshell
does not vendor and which has exactly one consumer: make.tcl's zipfs-less kit extraction.
The goal removes that requirement outright (not as an optional fallback), adds an
introspection surface so punk::zip is useful to script programmers, and gives the writer
its first round-trip coverage. Measured motivation recorded in the detail file: msys2's
tclsh8.6 has no tcllib and so bakes an unbootable kit, and extract_preamble's live
'#todo - need to adjust offsets' means a FILE-relative prefixed executable
(tclsh90b4_piperepl.exe) splits into a .zip tcllib rejects with "Bad closure" - reading
the original file at its derived base offset removes that failure mode by construction.
G-125 - a kit that cannot boot is not deployed. Today a failed extraction emits a
recapped BUILD-WARNING and then deletes and replaces the deployed kit anyway; field-
observed 2026-07-26 replacing a working punk91.exe with one that dies on init.tcl. The
gate tests the boot precondition on the assembled vfs, so a kit whose payload legitimately
supplies its own tcl library still builds.
G-126 - punkzip as a VENDORED zip accelerator behind G-124's pure-Tcl floor, with zig
kept optional (a tclsh-only clean checkout must still build and bake). Feasibility
measured and recorded: the source builds clean under its declared zig 0.15.2, and under
0.16 fails on one surfaced error (std.process.argsAlloc), with the OS-facing surface
confined to 3 of ~20 files while the ~10k-line compression core is untouched; zig 0.16.0
is already unpacked in bin/tools. Benchmarks captured for the speedup claim.
Non-contract Notes back-pointers added to G-034 (the reader is a candidate for its own
Acceptance's "alternative modpod mount path" branch) and G-101 (an 8.6 container has no
zipfs, so it needs a reader that works without one).
Assisted-by: harness=claude; primary-model=claude-opus-5[1m]; api-location=anthropic.com