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.
 
 
 
 
 
 

12 KiB

G-125 A kit that cannot boot is not deployed

Status: achieved 2026-07-27 Scope: src/make.tcl (kit extraction outcome handling, deploy step gating, the no-extraction BUILD-WARNING as the current behaviour being replaced); src/modules/punkboot/utils-999999.0a1.0.tm (the boot-precondition predicate, called guardedly from make.tcl as vcs_dirty_warnings is); src/tests/modules/punkboot/utils/ (predicate characterization over directory fixtures); src/tests/shell/testsuites/punkexe/ (characterization of the make.tcl-side gate reporting); src/AGENTS.md (bake failure-mode documentation) Goal: a bake that could not lift a runtime's own payload (its tcl_library) into the kit fails that kit instead of building and deploying an artifact that cannot boot - so the worst outcome of a missing extraction capability is a red kit, never a silently replaced working executable. Acceptance: when extraction from the source runtime yields nothing and the kit's .vfs does not itself supply a bootable tcl library, the kit is recorded in FAILED KITS with a reason naming the cause and NEITHER src/_build nor bin receives a new artifact - the previously deployed kit is left untouched, verified by mtime and byte comparison across such a run; a kit whose .vfs legitimately supplies its own tcl library still builds and deploys, so the gate tests for the boot precondition rather than for the extraction step having run; the check is cheap enough to run on every kit and executes no artifact; the condition and its remedy are documented in src/AGENTS.md; existing punkexe suites pass unchanged and the gate itself is characterized.

Context

The kit machinery already knows when it failed to extract anything from a source runtime. Under G-122 (achieved - see goals/archive/G-122-host-target-platform-split.md) that notice was promoted from an inline stderr block to a recapped BUILD-WARNING: naming the kit and what was tried - but the build still proceeds, assembles an artifact, DELETES the deployed kit and copies the new one over it. The pre-deploy process sweep will even close running instances of the kit first.

Field-observed 2026-07-26: an msys2-hosted bake of punk91 produced a 49,501,792-byte bin/punk91.exe that failed at startup with

application-specific initialization failed: Cannot find a usable init.tcl

because the host's tclsh had no tcllib and therefore could not extract the runtime's own zip. The previously working kit had already been replaced by then. G-124 removes the particular cause, but not the class: a runtime with no attached payload at all, an extraction that fails for any other reason, or a future container type reaches the same state, and the build's response is still "warn and deploy".

The existing precedent in the same code path is vfs_startup_script_warning, which warns about a missing main.tcl and continues - deliberately, because a kit without a startup script is legal. A kit without a resolvable tcl library is not in the same category: it cannot initialise at all.

Approach

  • Gate on the BOOT PRECONDITION, not on the extraction step having run. Some .vfs folders legitimately supply their own tcl library (the merged targetvfs is what matters), so the check inspects the assembled tree rather than the extraction outcome - a runtime that needed no extraction must keep building.
  • Fail the kit the same way every other failure in that loop does (append to failed_kits with a reason, end the punkcheck event FAILED) BEFORE the artifact is renamed into place, so neither src/_build/<kit> nor bin/<kit> is touched and the punkcheck records stay consistent with what is on disk.
  • Keep it cheap and non-executing: a structural check of the merged vfs, no artifact launch, so it can run for every kit on every bake.
  • The predicate lives in punkboot::utils and make.tcl calls it through a guarded require, exactly as it already does for vcs_dirty_warnings - Scope extended at activation (2026-07-27, user-approved) because a predicate private to the build script cannot be characterized, and that module is the established home for make.tcl's gate helpers.
  • The recapped BUILD-WARNING wording changes from "will not initialise unless the vfs supplies one" to a failure reason, since the gate now enforces what the warning described.

Alternatives considered

  • Keep warning and deploying, on the grounds that the developer sees the recap - rejected: the recap arrives after the deployed kit has already been replaced, and the failure surfaces later as an unexplained broken shell. A build tool should not make a working executable worse.
  • Boot the freshly built artifact as the check - rejected: it cannot work for cross-target kits (G-122 makes those routine), costs a process launch per kit, and a kit that boots into an interactive shell is awkward to probe safely.
  • Emit to src/_build but refuse only the deploy - rejected as a half-measure that leaves a punkcheck record claiming a good build product; a later run would consider it current.

Notes

  • Related: G-124 (achieved 2026-07-26 - see goals/archive/G-124-punkzip-reader.md) - removed the most common cause: a tcllib-less tclsh can now read a runtime's attached zip, and a bake from msys2's tclsh8.6 carries tcl_library into the kit instead of warning past it. This gate is independent of that and remains valuable: the class survives (a runtime with no attached payload, an extraction that fails for any other reason, a future container type), and the build's response to it is still "warn and deploy". Note for the gate's design: the reader now RAISES with a named reason (encrypted, zip64, unknown compression method, crc mismatch, offsets that do not describe the file) where the old path returned an opaque tcllib error, so the gate has a usable failure reason to report.
  • Related: G-122 (achieved) - promoted the no-extraction notice to a recapped BUILD-WARNING and recorded the field incident that motivates this goal; see goals/archive/G-122-host-target-platform-split.md.
  • Related: G-127 (recorded at activation 2026-07-26 - drafted after this goal, and the activation-freshness survey's top unlinked pair on shared paths: src/make.tcl deploy step, src/tests/shell/testsuites/punkexe/, src/AGENTS.md). It moves non-native kits to bin/kits// and adds its own payload/target mismatch reporting, so this gate must refuse before whichever output location that goal settles on, and the two failure reports should read as one vocabulary rather than two.
  • Related: G-101 - a new 8.6 container type is exactly the situation where an extraction path can silently produce nothing; the gate is what keeps that honest.
  • Related: G-028 - sibling failure-reporting surface in the same deploy step (that goal names the process holding a target the build cannot replace; this one refuses to replace a target with something unbootable).
  • Overlap survey also considered G-057 (icon embedding rides the same wrap steps) and G-115 (declarative .vfs composition decides what the vfs supplies) - neither is a dependency: this gate reads whatever the merged tree contains, however it was composed.

Progress

2026-07-27 - implemented; every Acceptance clause verified. Detail:

  • punkboot::utils 0.2.0 -> 0.3.0 gains vfs_boot_library_report {vfsfolder}, returning ok / locations / rejected / checked / reason. make.tcl calls it through ::punkboot::get_vfs_boot_library_report, a guarded require modelled on the existing get_src_provenance_warnings, so a stale bootsupport snapshot degrades the gate to a NOTE rather than failing every kit. Both branches were observed live: before the bootsupport propagation make.tcl check reported UNAVAILABLE, after it ACTIVE.
  • The gate sits in the kit loop immediately after the _vfscommon.vfs + <kit>.vfs merges and before the kit-type assembly switch - the last point at which no build product exists. A refusal appends to failed_kits, ends the punkcheck event FAILED and continues, so src/_build/<kit> and bin/<kit> are never touched and the punkcheck records stay consistent with what is on disk. Note the merged src/_build/<kit>.vfs tree IS left behind (it is the evidence for the refusal); only the artifact is refused.
  • What qualifies as a tcl library: tcl_library/init.tcl (zipfs-attached kits) or lib/tcl<major>.<minor>/init.tcl (starkit-style), AND at least one companion file (tm.tcl, package.tcl, auto.tcl, clock.tcl, history.tcl, word.tcl) or an encoding/ directory beside it. The companion test is load-bearing: every punkshell kit carries lib/BWidget1.10.1/init.tcl and lib/BWidget1.9.16/init.tcl, so a check that looked for any init.tcl would pass a kit with no tcl library at all. lib/tcllib2.0 is correctly not matched by the lib/tcl[0-9]* glob.
  • The no-extraction BUILD-WARNING was reworded from "will not initialise unless src/vfs/ supplies one" (which described a consequence the build then ignored) to "will FAIL the boot-precondition gate unless src/vfs/ supplies one". It stays a warning rather than becoming the failure, because a .vfs that supplies its own library needs no extraction - the gate decides, on the merged tree.

Verification records (2026-07-27)

  • Predicate over the 10 assembled kit trees in src/_build: all pass, and the two conventions are both represented (tcl_library for punk902z/punk905/punk91/punk9_beta/ punk9bi_beta/punkmagic, lib/tcl8.6 for punk86/punkbi/punksys, lib/tcl9.0 for punkshell902). Of the source .vfs folders three legitimately supply their own library (mkzipfix.vfs, punk8_statictwapi.vfs, punk9test.vfs) - the case the gate must not break.
  • Gate fired end-to-end against a temporary fixture kit (a mapvfs entry pointing a zip kit at a payload-free runtime plus a vfs with no tcl library), with sentinel files standing in for a previously deployed kit and a previous build product: bin/punkgatefixture.exe sha1 2a0700ebfad097e34f70f6d779c8c1c8f2ba7dba and src/_build/punkgatefixture.exe sha1 12c505679ae7cc8fe44527eb4e8f34fa2659894d were byte-identical with unchanged mtimes across the failing run, and the kit was reported under FAILED KITS with the cause named. Adding tcl_library/{init.tcl,tm.tcl} to that same fixture vfs - extraction still yielding nothing - made the kit build and deploy normally (INSTALLED KITS: 1), which is the "tests the precondition, not the extraction step" half of the criterion. The fixture (mapvfs entry, vfs folder, fake runtime, sentinels, build leftovers) was removed afterwards and mapvfs.config restored byte-identical to its backup.
  • Real-kit bake through the gate: bake -confirm 0 punk905 builds, deploys and boots (tcl_library at //zipfs:/app/tcl_library, punkboot::utils 0.3.0 present in the kit).
  • src/tests/modules/punkboot/utils/testsuites/utils/bootlibrary.test 12/12; src/tests/shell/testsuites/punkexe/maketclbootgate.test 2/2.
  • make.tcl workflow renders the gate in the kit data flow with key note K10, verified under the default and PUNKBOOT_PLAIN=1 paths, max line width within the 100 budget.
  • Cost: 0.63ms per kit measured over 200 iterations against the real src/_build/punk905.exe.vfs tree (a glob plus a handful of file isfile tests, no recursion, nothing executed) - negligible beside a bake that takes minutes, which is what lets it run for every kit unconditionally.
  • Full source-tree suite after the change: 1161 tests, 1138 passed, 20 skipped, 3 failed. One is core/tcl exec-14.3, the documented pre-existing baseline. The other two are in modules/argparsingtest/ and belong to unrelated work in progress in the working tree at the time of the run (that module, its buildversion and both of its test files were being edited concurrently); nothing in this change reaches arg-parsing benchmarks.