You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

28 KiB

G-101 kit container strategy for zig-built Tcl 8.6 runtimes (no zipfs)

Status: proposed Scope: investigation + decision record under src/buildsuites/suite_tcl86/ (or successor mechanism); src/buildsuites/suite_tcl86/patches/ (tracked sdx wrap.tcl provenance capture); TEMP_REFERENCE/{metakit,KitCreator,kbs,sdx,tcl9} (read-only guidance - inventory in the detail file); src/runtime/mapvfs.config ('kit' type consumers); src/make.tcl kit-wrap path (as consumer); bin/sdx.kit (the tracked wrapping tool - provenance is part of this goal) Goal: a decided, demonstrated strategy for wrapping zig-built 8.6 runtimes into self-contained punk86-class kit executables in the absence of zipfs - candidate containers: the metakit/mk4vfs tclkit lineage, cookfs, and zipvfs-over-tclvfs boot - so the prebuilt third-party kit runtimes (tclkit86bi.exe, tclkit-win64-dyn.exe) can eventually be replaced by suite-built, provenance-carrying ones. Acceptance: the candidate containers are assessed and recorded here against punkshell's kit expectations (compatibility with make.tcl's 'kit' wrap path or a documented replacement path; boot-time vfs mount without an installed Tcl; loading binary dlls from or via the container; buildable from source under the zig-only policy); one strategy is demonstrated end-to-end - a kit wrapped from the suite-built 8.6 runtime boots on a path without any Tcl installation and runs a script from inside its vfs; the decision, rationale, and the follow-on work needed to reach punk86-kit parity are recorded in this file.

Context

The genuinely new problem in the 8.6 arc: suite_tcl90's kits came almost for free because Tcl 9's zipfs makes exe+zip self-mounting (tclsh90szip.exe, punk905_beta zip-wrap). 8.6 has no zipfs, so a kit needs a vfs container and a boot mechanism:

  • metakit/mk4vfs: the classic tclkit lineage (the current prebuilt runtimes are this). TEMP_REFERENCE/metakit and TEMP_REFERENCE/KitCreator were flagged by the user (2026-07-20, during the G-098 (achieved 2026-07-20) vlerq cleanup) as the guidance for when tclkit builds are investigated - the old vlerq/vqtcl references removed from the 9.0 recipe belonged to an earlier attempt at this same territory.
  • cookfs: used by some modern kit builders (tcl-sfe lineage); C extension plus scripted boot.
  • zipvfs over tclvfs: pure-tcl-plus-tclvfs zip mounting (vfs::zip is already built by the suites) with a boot preamble; no zipfs required but bootstrapping the vfs package itself from inside the container is the chicken-and-egg to solve.

make.tcl's existing 'kit' wrap type drives sdx/mk4-style wrapping; whichever container is chosen must either fit that path or come with a documented successor.

The G-103 runtime kit family (achieved 2026-07-22 - see goals/archive/G-103-runtime-kit-family.md; plain/punk/bi kits with thread + tclvfs + tcllib batteries in the attached container) extends to 8.6 THROUGH this investigation: the user notes (2026-07-20) that in the 8.6 case tclvfs may have to be STATIC and there may be metakit-specific patching - both are part of this goal's assessment, and the 8.6 family variant is blocked on its outcome.

Separated from G-099/G-100 deliberately: runtime + libs + test gating are well-understood ports of the 9.0 work, while this is open investigation - keeping it out of their acceptance keeps the 8.6 suite arc incrementally landable.

SEQUENCING: proposed; naturally last in the 8.6 arc (needs the G-099 runtime to wrap). Both predecessors have landed - G-099 (achieved 2026-07-26 - see goals/archive/G-099-suite-tcl86-buildsuite.md) built the runtime this goal wraps, and G-100 (achieved 2026-07-26 - see goals/archive/G-100-suite-tcl86-tk-tcllib.md) completed the dependency set that becomes this container's payload. The arc is unblocked; activation is the user's call.

Notes

  • From G-128 (2026-07-29; achieved 2026-07-29 - see goals/archive/G-128-portable-pe-resource-stamping.md): when this goal settles the 8.6 container, include a POST-HOC STAMPING-SUPPORT review for whatever it chooses - the punkres overlay taxonomy handles zip payloads natively, metakit was MEASURED move-safe (a punksys.exe copy stamped via -allow-opaque-overlay boots and loads punk::lib from the moved payload; metakit self-locates from the file end), and any OTHER container remains unclassified (opaque-consent path, move-safety unmeasured). The G-128 acceptance's 'kit' shape is treated as satisfied-for-now on that measurement (user decision 2026-07-29, recorded in the archived G-128 detail).
  • DOWNSTREAM: G-131 (back-pointer added 2026-07-27) wants a runtime that boots from EITHER container - metakit or zipfs - detected at boot, so that the container becomes a bake-time choice and the deliverable count stops being multiplied by container. That has a direct bearing on how this goal's decision should be framed: if G-131 is wanted, the useful outcome here is not "pick one container for 8.6 and close the question" but "establish that metakit is carryable AND that the zipfs backport is carryable, and record which is the default". Both candidates are surveyed in this file and both look additive - the zipfs backport is a new source file plus a Makefile object (see the zipfs section), and the mk4tcl port to 9 is bounded (see the metakit section). The two sections were written independently as competing candidates; G-131 is the reason to read them as a possible pair. G-131 depends on the port work this goal would do; it does not block this goal's decision, and this goal can be decided without committing to G-131.
  • Related: G-125 (achieved 2026-07-27 - see goals/archive/G-125-unbootable-kit-deploy-gate.md) - a new container type is exactly where an extraction path can silently produce nothing, and the bake now refuses such a kit instead of deploying it. Concretely for this goal: whichever container is chosen must land a tcl library in the merged vfs at tcl_library/ or lib/tcl<major>.<minor>/ (with a companion file beside init.tcl), or the gate's location list in punkboot::utils::vfs_boot_library_report needs extending to recognise the new shape - the check is the place where "this container produced a bootable kit" is asserted.
  • Related: G-124 (achieved 2026-07-26 - see goals/archive/G-124-punkzip-reader.md) - an 8.6 container has no zipfs by definition, so whichever container this goal picks needs a reader that works without it. That reader now exists and is verified on 8.6 with no zipfs, no vfs::zip and no tcllib: punk::zip::members lists an archive and punk::zip::unzip extracts it, including from an executable-prefixed image under either offset convention (punk::zip::archive_info reports which). If this goal picks a zip-shaped container, the read half is already solved, and the optional punkzip extraction accelerator now exists too (G-126, achieved 2026-07-27 - see goals/archive/G-126-punkzip-accelerator.md: punk::zip::accelerator + the accelerated unzip path, x2.3-4.9 at kit member counts).
  • Related: G-116 (suite-built tcltls with a zig-built crypto backend) - the payload question is shared: whatever container this goal picks carries the battery set, and G-116's bi-family payload contract is generation-shaped, not 9-only. Recorded 2026-07-26 at the G-100 archive sweep, where the pair's only bridges (G-100, G-103) both left the live tier.
  • Payload inventory as of 2026-07-26: G-100 (achieved - see goals/archive/G-100-suite-tcl86-tk-tcllib.md) completed the 8.6 dependency set this container would wrap - Tk 8.6.17 (tk86t.dll + lib/tk8.6), tklib 0.9, tcllib 2.0 + tcllibc accelerators, alongside G-099's thread 2.8.13 / tclvfs 1.4.2 / registry / dde.
  • 2026-07-21: the prebuilt runtimes this goal replaces (tclkit86bi.exe, tclkit-win64-dyn.exe) are Metakit tclkits - self-contained (Tcl + VFS embedded) yet DYNAMICALLY linked to libc. That dynamic libc is what lets them 'load' binary extensions (OS dlopen/LoadLibrary + Tcl's stubs table, with the extension copied out of the mk4vfs to a temp dir first; the host does NOT need to export Tcl symbols - stubs hand the table to the extension via the interp). Consequence for the container choice: "loadable binary batteries" implies a DYNAMIC-libc runtime - a fully-static-libc 8.6 kit would hit the same 'load' limitation G-105 records for linux (the compiled-in static-packages alternative is G-058, achieved 2026-07-10 - see goals/archive/G-058-static-runtime-packages.md). The container question (metakit/cookfs/zipvfs) is orthogonal to the libc-linking question; keep them separate at activation.
  • 2026-07-21: temp-copy cleanup applies here too - Metakit/starkit 'load' copies the extension out of the VFS to a temp dir (same as zipfs), and cleanup of those copies is a known wart (Tcl core TIP 741, windows-framed). This goal's "loading binary dlls from or via the container" acceptance should characterize that cleanup per platform - TIP 741 detail + requirement in G-103 Notes.
  • 2026-07-22: the G-103 family attached-image contract this goal was promised as its concrete payload target now EXISTS (G-103 Progress 2026-07-22; archived): a family runtime's image is tcl_library/ at the container root position the boot searches, tm tree at tcl9/ (8.6: tcl8/), batteries at lib/ with installed-shape pkgIndexes + dlls at bin/, and a one-line stock auto_path hook at lib/pkgIndex.tcl. The 8.6 container investigation targets delivering THAT payload shape (adjusted for 8.6 boot: no zipfs /app anchor - the metakit/cookfs/zipvfs mount point plays the /app role and the hook mechanism must be re-verified against 8.6's tclPkgUnknown).
  • 2026-07-25: launch-mechanism ladder for Tk apps launched from Explorer/shortcuts (spike-validated on punk91/punk86): (1) punkapp::hide_console stays dev-only - SW_HIDE on the console HWND hides the whole terminal window under ConPTY/tabbed terminals; (2) in-place FreeConsole + close/open-NUL std reassignment works on 8.6 and on stock 9.x + 9.0.x-era punk kits (Tcl installs the next created channel into the vacated std slot - tclIO.c Tcl_CreateChannel; recipe chan close stderr; open NUL w yields a channel auto-named stderr), and since the 9.1-kit std-slot pin was fixed (G-120, achieved 2026-07-25 - a leaked punk::libunknown static-probe child interp held the std channels; libunknown 0.2.2 - see goals/archive/G-120-scriptmode-std-channel-pin.md) it works on the current 9.1 kit too, so in-place stdio repair is available across current kits; (3) relaunch-self via twapi::create_process -noconsole is the primary cross-generation mechanism (hidden console, stdio + package require Tk verified working, no channel surgery; the brief console flash is OS-unavoidable for console-subsystem exes; sign release binaries against self-spawn AV heuristics); (4) a GUI-subsystem punkw-class wrapper is the only zero-flash option (prior art: punkwish91.exe is a full GUI-subsystem punk kit); DETACHED_PROCESS semantics are a trap (no std handles; Tk load fails on Tcl 9). bgerror must be guarded or stdio repaired before detach: punk::repl::bgerror writes stderr (silent output loss on 9.x post-detach; not fatal on 9.1 per spike).
  • 2026-07-25: trial-kit naming convention (user direction): the G-103 scheme holds for 8.6 family runtimes - artifacts tclsh[-punk[-bi]]-r.exe; during container trials the container tag rides the artifact modifier chain (e.g. tclsh8.6.17-punk-cookfs-r1.exe / -mk4 / -zipvfs) and drops once the container decision lands; kit names stay punk86-class. tclkit/tclkitsh naming is not revived for punk-built artifacts (the -sh suffix implies a GUI sibling that will not exist; current kit downloads already use bare tclkit for console builds). Any future zero-flash launcher takes the w-suffix convention (punkw) rather than -gui/-cli (which in kit lineage encode Tk linkage, not subsystem).
  • G-121 (archived) built bakelist/selective-bake over the parsed mapping without assuming zip-only types ('kit'-type entries list and bake by name), so 8.6 container entries join that surface automatically - see goals/archive/G-121-bakelist-selective-bake.md
  • 2026-07-26: pre-activation survey added four sections below - reference-material inventory (with the boot-chain reading order), sdx provenance + the characterize-before-rebuild ordering, the self-mount shadowing gotcha (verified live, container-agnostic), and metakit-under-Tcl-9 feasibility. Two of them carry proposed acceptance riders. The first - capture the sdx wrap.tcl patch somewhere versioned before any sdx rebuild - was DONE the same day (src/buildsuites/suite_tcl86/patches/, with a back-pointer added to TEMP_REFERENCE/AGENTS.md so the survey is not repeated). The second stands: make the end-to-end demonstration wrap a SEPARATE COPY of the runtime so the self-mount shadowing path is actually exercised rather than accidentally avoided.

Reference material (TEMP_REFERENCE, machine-local)

Recorded 2026-07-26 from an agent survey. CAVEAT: TEMP_REFERENCE is ignored by BOTH git and fossil (root .gitignore:59, .fossil-settings/ignore-glob:58-59), as is scratch* (.gitignore:35). Everything named here is unversioned and machine-local

  • it can vanish to a git clean -xdf. Findings derived from it belong in THIS file, not in notes left under that tree.

Metakit boot chain, in reading order (this is the mechanism the mk4 candidate would reproduce for a suite-built 8.6 runtime):

  • kbs/kbskit/sources/kbskit0.4/generic/kitInit.c - the clearest single description, and the closest to an 8.6 target. TclKit_AppInit registers Mk4tcl/rechan/vfs/tclkitpath via Tcl_StaticPackage (:170-177), then installs a pre-init script (:95-135) that opens the exe AS a metakit datafile (mk::file open exe $::tcl::kitpath -readonly), selects boot.tcl out of exe.dirs!0.files, zlib-inflates it when stored size != content length, and evals it. Comment at :88-93 explains why it must be re-entrant: it runs for every interp, including child and thread interps.
  • kbs/kbskit/sources/kbskit0.4/library/boot.tcl - the chicken-and-egg step. VFS is not mounted yet, so it hand-evaluates vfsUtils/vfslib/mk4vfs out of lib/vfs1* with raw mk::select (:24-38), mounts ::vfs::mk4::handler exe, then UNMOUNTS AND REMOUNTS after fixing the system encoding (:109-116). Note :40-44 - "8.6 core zlib does not support this for mk4vfs" forces mk4vfs::zstreamed 0, i.e. whole-blob decompression via vfs::memchan rather than streaming (the two branches are at kbs/kbskit/sources/vfs1.4/library/mk4vfs.tcl:167-180). Directly relevant to this goal's memory/latency profile for a battery-carrying 8.6 kit.
  • kbs/kbskit/sources/kbskit0.4/library/setupvfs.tcl:143-200 - how the kit gets WRITTEN (the counterpart to sdx wrap).
  • metakit/tcl/mk4tcl.cpp (2621 lines) - Mk4tcl_Init -> Mktcl_Cmds (:2559-2597): stubs at "8.1", registers the mkProperty/mkCursor obj types, one MkWorkspace per interp with assoc-data delproc + exit handler + event source, defines the nine mk::* commands. 8.6 compat is a one-line shim at :27-29; CHANGES dates that work to 2007-06-25. The storage engine (metakit/src/) is Tcl-agnostic - only tcl/mk4tcl.cpp + mk4tcl.h are Tcl-coupled.
  • KitCreator/kitsh/buildsrc/kitsh-0.0/{kitInit.c,boot.tcl} - same shape, generalised: storage backend is a compile-time switch (KIT_STORAGE_MK4/_ZIP/_CVFS) driving $::tclKitStorage, and boot.tcl branches on it (:18-90). Best reference for separating metakit-specific steps from generic starkit boot - i.e. for scoping how much of the boot survives if this goal picks cookfs or zipvfs instead. It also re-roots tcl::tm (:158-161), which kbskit's does not and which THIS goal's payload needs.
  • KitCreator/mk4tcl/build.sh + KitCreator/mk4tcl/patches/all/ - the build recipe and EIGHT patches against metakit 2.4.9.7 (64-bit filespace, 32/64 fix, unregisterchans, fixeventchannels, shared-obj linker, ranlib/ar). The TEMP_REFERENCE/metakit checkout is UNPATCHED upstream (github.com/jnorthrup/metakit, last commit "URL fix"), and kbs.tcl:2813-2827 pulls that same unpatched master zip. If mk4 is chosen, deciding which patch lineage to carry is a required step, not a detail.

Other trees in scope: tcl9/ is the fossil Tcl core checkout (currently 9.1b1 per generic/tcl.h - NOT the 9.0.5 the suites build, so init-path claims must be re-verified against 9.0.5); tcl-sfe/ is the cookfs-lineage prior art named in Context; sdx/ is covered below.

sdx: provenance and the wrapping-tool question

Recorded 2026-07-26. The user raised this before activation; findings are concrete enough to shape acceptance.

  • THE PROVENANCE GAP, precisely. There are two distinct sdx binaries on this machine, differing by 121 bytes:
    • bin/sdx.kit == TEMP_REFERENCE/sdx/sdx.kit, sha256 5d4aa629..., 119191 bytes. TRACKED IN GIT. This is the one make.tcl resolves.
    • scratch/sdx.kit == scratch/sdx-20110317.kit, sha256 04dc85d4..., 119312 bytes. UNTRACKED AND GITIGNORED. Unwrapping both (the .vfs trees sit beside them) shows the trees are identical at 64 files each EXCEPT ONE: lib/app-sdx/wrap.tcl. The whole delta is a two-line local patch in the Windows version-resource path: open [file join $idir tclkit.inf] becomes open ... "r" plus fconfigure $fd -translation binary, immediately before ::stringfileinfo::writeStringInfo (wrap.tcl:232-239 in the patched copy). Plausibly a real fix for CRLF corruption of tclkit.inf on Windows. So the tracked tool is a locally-patched sdx whose patch is recorded NOWHERE in version control, and the stock baseline that proves what the patch IS exists only in two ignored directories on this one machine. RESOLVED 2026-07-26: both sides are now captured in tracked form at src/buildsuites/suite_tcl86/patches/ (stock baseline, patched file, diff, and a README with the hashes and the reconstruction rules) - a rebuild can no longer silently drop or silently re-invent the fix. Note the patched path is DORMANT for present builds: it fires only when -runtime is given AND the runtime is a .exe AND tclkit.inf exists in the source .vfs, and no tclkit.inf exists anywhere in this repo. Whether the fix is CORRECT is unverified - no reproducer was recorded with it; confirming or retiring it is part of this goal's characterization work.
  • CHARACTERIZATION PRIMITIVE ALREADY EXISTS. sdx ships version (lib/app-sdx/version.tcl), which walks the mounted kit and prints <mtime> <crc-hi>-<crc-lo>. Verified live under bin/runtime/win32-x86_64/tclkit86bi.exe: the tracked kit reports 2025/06/04 22:56:36 69170-33997, the stock one 2011/03/17 23:39:41 50768-16003 - and the former's timestamp is exactly the patched wrap.tcl's mtime, independently confirming the patch is inside the tracked kit. TRAP for any rebuild-and-compare: traverse hashes file NAME + MTIME + SIZE, not content, so the signature is not reproducible across a re-wrap unless mtimes are normalised (sdx has version -fixtime). A content-level characterization battery needs its own comparison (unwrap both, diff trees; plus behavioural wrap/unwrap/mksplit round-trips), not just this signature.
  • BUNDLED LIBRARIES ARE 2011-ERA, and mostly irrelevant to wrapping: base64 2.4.1, md5 1.4.2, uri 1.2.1, autoproxy 1.5.1, ftpd 0.4, plus an entire Wikit GUI + httpd/ftpd/rexecd server stack. Only app-sdx (wrap/unwrap/mksplit/ mkpack/mkinfo/lsk/qwrap) and stringfileinfo are on this goal's path. That argues a rebuild should be a REDUCTION (drop the server/wiki payload) rather than a library refresh - smaller attack surface, smaller characterization battery, and the dropped packages have no bearing on kit wrapping. ChangeLog ends 2011-03-17 (Pat Thoyts); upstream was svn://svn.equi4.com/sdx/trunk.
  • BOOTSTRAP CIRCULARITY. sdx.kit is itself a metakit starkit (mk4 schema string dirs[name:S,parent:I,files[...]] visible in its tail bytes; contents stored uncompressed - plaintext greps hit inside the kit). Running it needs an existing tclkit, so the tool that would wrap a suite-built 8.6 runtime currently depends on a prebuilt third-party runtime of exactly the kind this goal exists to replace. make.tcl:5048-5097 encodes that fallback ladder and ends at $binfolder/sdx.kit + a tclkit. Breaking the circularity is a real acceptance question: either (a) run sdx under the suite-built dynamic tclsh86t with Mk4tcl loaded as a package rather than under a tclkit, or (b) replace sdx's wrap step with a suite-owned writer (setupvfs.tcl above is the reference for what writing a kit actually requires). Option (a) is the cheaper first probe and also tests whether a suite-built Mk4tcl works at all.
  • STALE WRAPPER: bin/sdx1.bat (and its TEMP_REFERENCE/sdx/sdx.bat twin) invokes %~dp0..\src\runtime\tclkit86bi.exe, a path that no longer exists - runtimes moved to bin/runtime/<os-arch>/. bin/ has no sdx wrapper under that exact name either, so make.tcl's auto_execok $binfolder/sdx probe cannot hit it and resolution falls through to the sdx.kit + tclkit last resort. Cosmetic, but it means the documented sdx entry point in bin/AGENTS.md:54 (external tools keep their own name) is currently unmet.

Self-mount shadowing (the same-path gotcha)

Recorded 2026-07-26, verified live rather than inferred. A running tclkit mounts its VFS AT ITS OWN EXECUTABLE PATH, so from inside itself the exe is a DIRECTORY: probing bin/runtime/win32-x86_64/tclkit86bi.exe gives [file type [info nameofexecutable]] == directory, [file isfile] 0, [vfs::filesystem info] listing that same exe path, and glob $exe/* yielding bin boot.tcl lib tclkit.ico.

Consequences this goal inherits whichever container it picks (zipfs has the same shape via its /app anchor, so this is not an mk4-specific wart):

  • A kit cannot read or copy ITSELF as a file - so it cannot wrap itself, and cannot be handed to sdx as -runtime. sdx's -runtime path does LoadHeader $pfile (wrap.tcl:109-115), a plain file read that fails on a shadowed exe.
  • make.tcl already works around this and the workaround should be preserved as a requirement, not rediscovered: :4995-5005 detects [file type $runtimefile] eq "directory" and substitutes a _BUILDCOPY (comment at :4996 states the rule), and :4980-4981 records that sdx objects when the wrapping shell and the -runtime argument are the same file.
  • Acceptance rider: whichever container lands, the demonstration should include a runtime wrapping a DIFFERENT copy of itself, so the shadowing path is exercised rather than accidentally avoided.

zipfs backported to 8.6 (androwish/undroidwish - bearing on container choice)

Recorded 2026-07-27 from a survey of TEMP_REFERENCE/androwish (fossil checkout; the tree is Tcl 8.6.10). The developer raised it as a third possibility beside mk4 and cookfs: rather than choosing a non-zipfs container for 8.6, bring zipfs itself to the suite-built 8.6 runtime, as undroidwish does.

  • SHAPE IS ADDITIVE, which is what makes it tractable. jni/tcl/generic/zipfs.c (6586 lines) plus jni/tcl/generic/zipfs.h (70), a jni/tcl/library/zipfs1.0/ package dir, and one object added in win/Makefile.in:314 and unix/Makefile.in:309. It is a NEW FILE plus build wiring, not a diffuse patch spread through the core - so it can be carried as a suite patch of the kind src/buildsuites/suite_tcl86/patches/ already holds, rather than a fork.
  • It does use core internals (#include "tclInt.h", "tclFileSystem.h"), so it is pinned to the core version it is compiled against. UNVERIFIED and the first thing to check: whether it applies to 8.6.18 (what suite_tcl86 builds) - the reference tree is 8.6.10.
  • Entry point Zipfs_Init, aliased Tclzipfs_Init / Tkzipfs_Init (zipfs.h:30,42). The Tcl_StaticPackage(interp, "zipfs", ...) registration is gated on ZIPFS_IN_TCL / ZIPFS_IN_TK (zipfs.c:6445-6447) - it is designed to be compiled INTO tcl or tk, which is exactly the shape this goal would want.
  • LICENCE: Copyright (c) 2013-2023 Christian Werner, under the tree's license.terms (Tcl-style BSD) - compatible with punkshell's BSD-2-Clause, and a single named upstream author to attribute per G-063.
  • CAPABILITY DELTA against Tcl 9's zipfs, measured on the shipped runtime (see the table in goals/G-129-kitboot-mountpoint-derivation.md): it provides mount unmount info list exists mkzip mkimg mkkey lmkzip lmkimg but NOT root, provides no ::zipfs ensemble, and is NOT registered in child or thread interps (16 commands in every Tcl 9 interp vs 10 in the 8.6 backport's MAIN interp only). Any punkshell code that must work under it therefore cannot probe with tcl::zipfs::root, cannot call a bare zipfs, and must not assume a codethread can see zipfs at all.
  • MOUNT CONVENTION: it mounts the executable's attached archive at the EXECUTABLE'S OWN PATH, not at //zipfs:/app - the reason G-129 exists (achieved 2026-07-27 - see goals/archive/G-129-kitboot-mountpoint-derivation.md: the boot now derives the mount base from the runtime's own mount table, and the undroidwish family's app/main.tcl in-archive startup hook is served by a second fauxlink in punk8win.vfs).
  • WHERE THE SELF-MOUNT LIVES IS AN OPEN QUESTION. Stock win/tclAppInit.c:192 and unix/tclAppInit.c:130 call Tclzipfs_Init only under #ifdef TCL_TEST, so the boot-time self-mount is in the vanillatclsh/undroidwish application main rather than in the stock appinit. Locating that hook is prerequisite work for adopting the backport, and it is the same hook a dual-container autodetecting boot would need.
  • PROVENANCE OF OUR SPECIMEN: LUCK (undroid/luck/) is a CGI script that repacks a PREBUILT vanillatclsh-* / undroidwish-* binary, selecting packages out of that binary's ZIP using Memchan channels and in-memory ZIP mounts (no temp files). So bin/runtime/win32-ix86/tclsh8.6.10-luck-zip.exe is a repack of androwish's own vanillatclsh build, and the base binaries come from the tree's undroid/build-undroidwish-*.sh scripts. That makes the runtime a working existence proof of zipfs-on-8.6, not merely a claim.
  • UNVERIFIED: whether zipfs.c builds under the pinned zig toolchain. It is C (not C++ like mk4tcl), which is a point in its favour against the metakit candidate.

Metakit under Tcl 9 (bearing on container choice)

Recorded 2026-07-26 from an API survey against TEMP_REFERENCE/tcl9 (9.1b1). Relevant because it decides whether mk4 is an 8.6-only stopgap or ONE container format across both runtime lines - which is the strongest argument for choosing it over cookfs/zipvfs, since Tcl 9 already has zipfs and needs no help.

  • The BOOT hook is easier on 9 than on 8.6: Tcl_SetPreInitScript is public API in 9.1 (generic/tcl.h:2306, run at generic/tclInterp.c:837), where 8.6 forced kbskit to hand-declare the internal TclSetPreInitScript (kitInit.c:50). And LocatePreInitScript honours $tcl_library first and exclusively (tclInterp.c:487-490), so a preinit that mounts the container and sets tcl_library gets init.tcl sourced from inside it.
  • BUT the proc tclInit {} override - which is what boot.tcl IS - is dead in 9.1: tclInterp.c:619-622 says so explicitly, and tcl_libPath is gone (:441-444). A dual-generation design must be preinit-shaped from the start. UNVERIFIED: whether 9.0.5 (what suite_tcl90 builds) exports Tcl_SetPreInitScript; the reference tree is 9.1b1 and its comments describe 9.0 as still tclInit-driven. Check before relying on this.
  • Mk4tcl PORT COST is bounded but real: ~23 Tcl_GetStringFromObj + list/bytearray call sites need Tcl_Size* not int*; stubs bump to "9.0"; one Tcl_ChannelType (mk::channel) against TCL_CHANNEL_VERSION_5 is the fiddliest piece; five Tcl_ObjType initialisers gain a version + abstract-list tail (compile clean under C++ aggregate init, worth making explicit). Tcl_ObjCmdProc still takes int objc (tcl.h:566) unless built -DTCL_NO_DEPRECATED, so the dispatcher signatures survive. Tcl_StaticPackage -> Tcl_StaticLibrary (tcl.h:2309).
  • tclvfs is largely FREE on 9: the forTcl9 branch (1.5.0) already exists upstream and is already recorded as the tcl9 pin in src/buildsuites/suite_tcl86/sources.config:28-29 (user decision 2026-07-22). Tcl_FSRegister / TCL_FILESYSTEM_VERSION_1 unchanged in 9.1. Core zlib push
    • chan create on 9 would also let mk4vfs use real streaming, which the 8.6 note above says it cannot.