Compare commits

..

11 Commits

Author SHA1 Message Date
Julian Noble b887a61349 G-123 drafted (user-approved): third-party runtime tiers via artifact servers 5 days ago
Julian Noble 917916c5be G-122 drafted (user-approved): make.tcl host/target platform split 5 days ago
Julian Noble 5c34951441 build outputs: bootsupport punk::platform-0.1.0.tm + templates-0.2.0.tm modpod sync 5 days ago
Julian Noble d9061b90ce bakelist/bake: loud missing-store-folder diagnosis; punk::platform into bootsupport (punkshell 0.24.1) 5 days ago
Julian Noble 3a52f02773 G-121 detail file -> goals/archive/ (pure rename) 5 days ago
Julian Noble c172912f4b G-121 achieved 2026-07-26: flip + archive record + reference sweep 5 days ago
Julian Noble 7d56aa00ff build outputs: thin-layout make.tcl sync copies (G-121 bakelist + selective bake) 5 days ago
Julian Noble 0abd07ecd7 G-121: make.tcl bakelist + selective bake (punkshell 0.24.0) 5 days ago
Julian Noble 5f3cea1954 G-121 activated (user-directed): bakelist + selective bake implementation begins 5 days ago
Julian Noble f5e3ed1ce0 colour policy: unix-class 8.6 terminals detected via the tty channel signature (-mode) 5 days ago
Julian Noble ac7dcdb02a colour policy: never wrap utf-16 console channels; 8.6 windows console auto-detected by encoding signature 5 days ago
  1. 4
      ARCHITECTURE.md
  2. 58
      CHANGELOG.md
  3. 4
      GOALS-archive.md
  4. 11
      GOALS.md
  5. 1
      goals/G-013-raw-mode-default.md
  6. 1
      goals/G-023-version-named-binaries.md
  7. 1
      goals/G-024-mapvfs-toml.md
  8. 1
      goals/G-028-file-locker-identification.md
  9. 1
      goals/G-057-kit-icon-embedding.md
  10. 1
      goals/G-101-tcl86-kit-container-strategy.md
  11. 1
      goals/G-115-declarative-vfs-composition.md
  12. 104
      goals/G-122-host-target-platform-split.md
  13. 88
      goals/G-123-thirdparty-runtime-tiers.md
  14. 61
      goals/archive/G-121-bakelist-selective-bake.md
  15. 2
      punkproject.toml
  16. 3
      src/AGENTS.md
  17. 1
      src/bootsupport/modules/include_modules.config
  18. BIN
      src/bootsupport/modules/punk/mix/templates-0.2.0.tm
  19. 237
      src/bootsupport/modules/punk/platform-0.1.0.tm
  20. 915
      src/make.tcl
  21. 890
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl
  22. 890
      src/project_layouts/vendor/punk/basic/src/make.tcl
  23. 890
      src/project_layouts/vendor/punk/project-0.1/src/make.tcl
  24. 6
      src/runtime/mapvfs.config
  25. 150
      src/tests/shell/testsuites/punkexe/maketclbakelist.test
  26. 4
      src/tests/shell/testsuites/punkexe/maketclcolour.test

4
ARCHITECTURE.md

@ -80,7 +80,7 @@ Two layers with a deliberate dependency direction (the class never depends on th
## Build, provenance and kits
- **`src/make.tcl` is the single build entry.** Key subcommands: `modules`/`libs`/`packages` (repo-root build outputs), `vfscommonupdate` (regenerate `_vfscommon.vfs`), `vfs`, `bin`, `project` (full kit build into `bin/`), `bootsupport` (snapshot refresh), `vendorupdate`, `check`, `workflow`, `projectversion`. Dispatch and help dogfood `punk::args` (G-030, achieved) and degrade to plain scan/help when bootsupport `punk::args` is stale (`PUNKBOOT_PLAIN=1` forces the degraded mode). Source: `src/AGENTS.md`.
- **`src/make.tcl` is the single build entry.** Key subcommands: `modules`/`libs`/`packages` (repo-root build outputs), `vfscommonupdate` (regenerate `_vfscommon.vfs`), `bake` (kit assembly into `bin/`; optional kit names confine the run to those kits - G-121), `bakelist` (report the configured kit matrix with runtime/vfs presence and deployed state), `bin`, `bakehouse` (packages + bake for a clean checkout; `project`/`vfs` remain as deprecated aliases - G-112 rename, achieved), `bootsupport` (snapshot refresh), `vendorupdate`, `check`, `workflow`, `projectversion`. Dispatch and help dogfood `punk::args` (G-030, achieved) and degrade to plain scan/help when bootsupport `punk::args` is stale (`PUNKBOOT_PLAIN=1` forces the degraded mode). Source: `src/AGENTS.md`.
- **punkcheck** records every install/delete as events in per-folder `.punkcheck` directories - the basis for skip/copy change detection, superseded-module pruning and provenance. Single OO record lifecycle (G-094, achieved) with atomic saves and advisory event-scoped locking for concurrent writers (G-095, achieved).
- **Provenance gates.** Build/promotion commands warn on uncommitted `src/` changes (column-0 `PROVENANCE-WARNING:` token, `-dirty-abort` for strict mode); `vendorupdate` warns for dirty source-project checkouts.
- **Buildsuites and the kit family.** `src/buildsuites/suite_tcl90/` builds Tcl/Tk/tcllib from source with a pinned zig toolchain and produces the runtime kit family (plain / punk / bi) plus artifact metadata (G-096-G-117 era: see archived goals G-096, G-098, G-102, G-103, G-107); artifacts publish to the punkbin repo that `bin/punk-runtime.cmd` fetches from. The 8.6 family and kit container strategy are in-flux - see "In-flux areas".
@ -102,7 +102,7 @@ Mechanics here are documented as pointers only - detailed prose would be obsolet
- Input mode and completion: G-013 (raw-mode default), G-044 (punk::args-driven completion).
- Stored configuration and help: G-014 (punk::config toml), G-042 (subshell help topics), G-043 (subshell definition plugins).
- Console err channel: G-011.
- Launch surface: G-023 (version-named binaries), G-033 (`proj:` package mode), G-077 (`-e` one-liners), G-089 (scriptlib in kits), G-112 (make.tcl subcommand rename).
- Launch surface: G-023 (version-named binaries), G-033 (`proj:` package mode), G-077 (`-e` one-liners), G-089 (scriptlib in kits).
- 8.6 buildsuite family: G-099, G-100, G-101.
## Doc map - which lane holds what

58
CHANGELOG.md

@ -5,6 +5,64 @@ The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.to
Entries are newest-first; one bullet per notable change. See the root `AGENTS.md`
"Project Versioning" section for the bump policy.
## [0.24.1] - 2026-07-26
- make.tcl bakelist/bake: a nonexistent per-platform runtime store folder is now flagged
loudly instead of silently yielding all-missing rows - bakelist prints a stderr WARNING
showing the platform-canon derivation (tcl_platform os/platform -> canon) and marks the
header line (FOLDER MISSING); bake's no-runtimes exit carries the same self-diagnosis.
Classic trigger: an msys/cygwin-runtime tclsh (reports platform unix, canon e.g
mingw64-x86_64) deriving a store name that is not the windows store the kits use.
- make.tcl shell: 'help platforms' now renders (punk::platform added to the bootsupport
module set - it was absent from the snapshot, so the bootsupport-hosted shell degraded
to a package-not-available notice on any interpreter, not just msys hosts).
## [0.24.0] - 2026-07-26
- make.tcl bakelist + selective bake (G-121): new informational `bakelist ?kitname ...?`
subcommand reports the kit outputs configured in src/runtime/mapvfs.config - kit name,
kit type, runtime (with presence in the runtime store), vfs folder, and the deployed
state of bin/<kit> vs the src/_build build product (current|stale|absent|nobuild), with
anomalies (runtime=missing, vfs=missing, rtrev=r<cur><r<max> runtime-materialization
staleness) in a trailing notes column; name filtering doubles as per-kit detail.
`bake ?kitname ...?` now bakes and deploys only the named kits - other kits' _build/bin
artifacts and punkcheck records are untouched, the vfslibs phase narrows to the named
kits' vfs folders, and an unknown name errors before any build, listing the configured
names. Bare `bake` is unchanged (all configured kits). Both surfaces (and the argdoc
kit-name choices) consume one parsed mapping model (::punkboot::lib::mapvfs_*), so the
config-format work (G-024 toml direction) can swap the reader underneath.
## [0.23.3] - 2026-07-25
- make.tcl colour policy: unix-class Tcl 8.6 terminals auto-detected via the tty channel
signature - real ttys (isatty-gated channel type) expose the serial/tty options
(`-mode` etc) that pipes/files lack. Covers linux/WSL and msys2/cygwin-runtime tclsh
builds (which report platform unix even on windows - e.g the scoop-installed msys2
`usr/bin/tclsh8.6`, previously colourless-with-transform on its pty). Verified: WSL
8.6.14 pty mode=tty / piped tcl86-plain; msys2 8.6.12 piped tcl86-plain (interactive
carries `-mode` per field report). With the 0.23.2 windows console-encoding signature
this completes dependency-free 8.6 tty detection on all supported hosts; the force
env vars remain for exotic environments. Also confirmed: 8.6 and 9.x piped help
output is byte-identical - no 8.6-specific rendering defect existed; all observed
mangling was the (fixed) utf-16 transform corruption.
## [0.23.2] - 2026-07-25
- make.tcl colour policy defect fix + 8.6 windows console auto-detection: the G-113
ansistrip transform was being pushed onto Tcl 8.6 windows CONSOLE channels, which
encode utf-16 ("unicode") - at byte level ESC arrives as 1B 00, so sequences passed
unstripped and the hold-back logic deferred/dropped real content, seriously mangling
interactive `punk86`/`punksys src/make.tcl help` layout. The 8.6 windows console is
now detected by that very encoding signature (only console channels report
`-encoding unicode`; pipes/files get the system encoding): mode=tty, colour ON, no
transform - interactive 8.6 windows colour works with no env var and no twapi.
The transform push is now PER CHANNEL and never wraps a utf-16-class channel (e.g
`make.tcl ... > file` from an 8.6 console wraps stdout only; the console stderr
keeps its ANSI). Policy/check lines report the wrapped channels
(`ansistrip=stdout+stderr|stdout|0`). Chunk-boundary handling verified sound over
byte-transparent encodings (invariant across chunk sizes 1..4096); 8.6 unix/WSL
remains plain-by-default pending the probe-ladder goal.
## [0.23.1] - 2026-07-25
- punk-runtime `use` accepts runtime/artifact names with or without the `.exe` suffix

4
GOALS-archive.md

@ -182,3 +182,7 @@ Acceptance: (1) `make.tcl bake` on a clean checkout produces kits byte-comparabl
### G-104 [achieved 2026-07-25] make.tcl buildsuite surface: list / info / build → detail: goals/archive/G-104-maketcl-buildsuite-surface.md
Scope: src/make.tcl (buildsuite subcommand group); src/buildsuites/*/ (suite self-description contract: an 'info'/describe affordance per suite - suite.tcl action or manifest record); documentation (make.tcl help text, src/buildsuites README/AGENTS)
Acceptance: list shows suite_tcl90 (and any copied tree) with descriptions sourced from the suites themselves; info renders the sources.config-derived table plus zig pin and products for a named suite; build forwards arguments and streams driver output, exiting with the driver's status; the surface works when make.tcl runs under a plain tclsh and under punk kit executables; help text documents the group; the self-description contract is documented where suite authors will find it (src/buildsuites docs) and holds for a tree created by copying suite_tcl90.
### G-121 [achieved 2026-07-26] make.tcl bakelist + selective bake: list configured kits, bake by name → detail: goals/archive/G-121-bakelist-selective-bake.md
Scope: src/make.tcl (bake @values + new bakelist subcommand; SUBOPTS/SUMMARIES/HELPTEXTS/SUBGROUPS, help/workflow text); src/runtime/mapvfs.config (as read; todo-comment retirement); src/tests/shell/testsuites/punkexe/ (bakelist characterization if feasible); layout make.tcl copies via established sync channels
Acceptance: `make.tcl bakelist` under a plain tclsh and under a punk kit executable lists every kit output configured in the runtime mapping with name, kit type, runtime (present/missing), vfs folder and a deployed-state indication (bin copy absent/current/stale vs the build product), and `bakelist <kitname>` filters to the named entries; `make.tcl bake <kitname ...>` builds and deploys only the named kit(s) - a recorded verification shows the named kit's artifacts refreshing while other kits' _build/bin artifacts and punkcheck records are untouched - and an unknown name errors without building, listing the configured names; bare `make.tcl bake` processes the same kit set as before the change; help/workflow text documents both per the update contract and the mapvfs.config todo comment is retired to point here; a piped bakelist characterization test lands under punkexe if feasible (else the infeasibility is recorded here); project version minor-bumped with a CHANGELOG entry.

11
GOALS.md

@ -393,6 +393,11 @@ Detail: goals/G-115-declarative-vfs-composition.md
Scope: src/buildsuites/suite_tcl90/ (build_tcltls module + crypto-backend build, sources.config + build.zig.zon source records, kit-family bi payload + metadata extension, test_gate record step); src/vfs kit payloads carrying vendored tcltls binaries (current-state reference + recorded disposition only - removal stays G-004-era work); punkbin (as eventual artifact destination via the G-067 library class); consumers punk::imap4 / punk::netbox (package require tls) as verification context
Detail: goals/G-116-suite-built-tcltls.md
### G-121 [proposed] make.tcl bakelist + selective bake: list configured kits, bake by name
Scope: src/make.tcl (bake @values + new bakelist subcommand; SUBOPTS/SUMMARIES/HELPTEXTS/SUBGROUPS, help/workflow text); src/runtime/mapvfs.config (as read; todo-comment retirement); src/tests/shell/testsuites/punkexe/ (bakelist characterization if feasible); layout make.tcl copies via established sync channels
Detail: goals/G-121-bakelist-selective-bake.md
### G-122 [proposed] make.tcl host/target platform split: bake by target, not host personality
Scope: src/make.tcl (target-platform derivation; store folder, exe suffixing, kit naming, presence checks and process-sweep tooling keyed by target; platform-canon inline copy; zipfs-less zip-type assembly fallback); src/modules/punk/platform-999999.0a1.0.tm (stable canon tags for cygwin-family hosts, help platforms doc); src/runtime/mapvfs.config (per-entry target platform via minimal compatible extension - format ownership coordinated with G-024); bin/runtime/<platform>/ store tiers (as consumed; fixture tier for tests); src/tests/shell/testsuites/punkexe/ (characterization)
Detail: goals/G-122-host-target-platform-split.md
### G-123 [proposed] Third-party runtime tiers via artifact servers: per-target publish, list, fetch, materialize
Scope: src/scriptapps/punk-runtime.ps1 + src/scriptapps/bin/punk-runtime.bash + punk-runtime_wrap.toml (named-tier operations in both payloads; bin/punk-runtime.cmd via the established re-wrap channel); punkbin artifact repo (external, c:/repo/jn/punkbin - per-tier layout, defaults, sha1sums, schema-v2 provenance sidecars incl. retroactive backfill, layout docs); src/buildsuites/suite_tcl90/ (family_artifacts.tcl schema-v2 emission for suite-built artifacts); alternative-server override + trusted-server set (PUNKBIN_URL pattern; consent surface coordinated with G-006); src/tests/shell/testsuites/binscripts/ (fixture-server characterization); bin/runtime/<tier>/ stores (as materialization targets)
Detail: goals/G-123-thirdparty-runtime-tiers.md

1
goals/G-013-raw-mode-default.md

@ -84,3 +84,4 @@ stays (see inventory item 4).
the suite runtimes and kit shape; first enable of a session costs ~0.6-0.7s server
spawn - an eager warm-up at repl init is noted there as a future nicety if raw
becomes the default). See goals/archive/G-106-powershell-consolemode-fallback.md.
- 2026-07-26 field finding (msys/cygwin-runtime tclsh: scoop msys2 tclsh8.6 driving 'src/make.tcl shell' on windows): line mode is largely functional, but RAW mode places the cursor at row 1 after each command - the raw-mode cursor bookkeeping (cursor-position query/screen-state handling) does not yet cater for the msys pty terminal class. Recorded as a raw-mode-default blocker dimension alongside the existing cursor-move issues; the make.tcl host/target platform-split goal (G-122, proposed the same day) is build-tooling only and deliberately excludes this console/repl work.

1
goals/G-023-version-named-binaries.md

@ -90,3 +90,4 @@ binary-artifacts repository). Revisit when the accumulation actually bites.
icon-embedding step hooks the same kit wrap steps; its ordering constraint
(icon the stub before payload append) interacts with this goal's every-build
rewrite cadence.
- G-121 (archived) recorded that scheme-expanded output names should enter the parsed mapping model as expansions (::punkboot::lib::mapvfs_kit_outputs) so generated names join the bakelist / bake-by-name surface automatically when this goal lands - see goals/archive/G-121-bakelist-selective-bake.md

1
goals/G-024-mapvfs-toml.md

@ -75,3 +75,4 @@ with the G-014 direction of toml + tomlish for declared configuration.
goal owns the runtime-to-vfs-to-executable mapping and schemes. Design the two as sibling
grammars in one toml family (shared conventions, not two dialects); G-115's
vendorlib_vfs.toml fold/supersede decision should cite this goal's scheme format.
- G-121 (archived) delivered the shared parsed-mapping reader (::punkboot::lib::mapvfs_parse + mapvfs_kit_outputs): the bake kit machinery, bakelist and the bake/bakelist argdoc kit-name choices all consume the model, never the file format - this goal's toml conversion swaps the reader implementation underneath that surface, and bakelist is where its per-entry validation errors surface (runtime/vfs missing rows) - see goals/archive/G-121-bakelist-selective-bake.md

1
goals/G-028-file-locker-identification.md

@ -83,3 +83,4 @@ the kind of file this fails on.
resource update hits the same locked-target failure class when an inspected
or running kit is being re-wrapped - the locker-report helper covers the icon
step's failures as well as deploy's.
- G-121 (archived) shipped bakelist's deployed-state column (bin/<kit> vs src/_build: current|stale|absent|nobuild) - the reporting sibling of this goal's acting-path deploy-failure naming; a locked-exe deploy failure shows afterwards as deployed=stale - see goals/archive/G-121-bakelist-selective-bake.md

1
goals/G-057-kit-icon-embedding.md

@ -84,3 +84,4 @@ delete-then-write). Requirements sketch agreed 2026-07-10:
version/copyright resources from punkproject.toml - when it is drafted as a
goal, reconcile with G-025's kit stamp and G-117's embedded artifact record
so the three provenance surfaces tell one story.
- G-121 (archived) parameterized the wrap steps this goal's acceptance runs through: 'make.tcl bake <kitname>' now rebuilds a single kit for cheap icon-override verification, and a per-kit icon override would slot into bakelist's filtered per-kit detail - see goals/archive/G-121-bakelist-selective-bake.md

1
goals/G-101-tcl86-kit-container-strategy.md

@ -97,3 +97,4 @@ wrap), and only worth activating once G-099/G-100 land.
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

1
goals/G-115-declarative-vfs-composition.md

@ -59,3 +59,4 @@ for telling the two apart).
declaration are the same design space (toml-declared, pinned, provenance-tracked
external content) - G-065's upstreams feed the sources, this goal's declarations
materialize the folder. Keep the schemas aligned.
- G-121 (archived) noted per-kit payload detail as a candidate later addition to bakelist's per-kit view (name filtering doubles as detail) once this goal's declaration surface exists - see goals/archive/G-121-bakelist-selective-bake.md

104
goals/G-122-host-target-platform-split.md

@ -0,0 +1,104 @@
# G-122 make.tcl host/target platform split: bake by target, not host personality
Status: proposed
Scope: src/make.tcl (target-platform derivation; store folder, exe suffixing, kit naming, presence checks and process-sweep tooling keyed by target; platform-canon inline copy; zipfs-less zip-type assembly fallback); src/modules/punk/platform-999999.0a1.0.tm (stable canon tags for cygwin-family hosts, help platforms doc); src/runtime/mapvfs.config (per-entry target platform via minimal compatible extension - format ownership coordinated with G-024); bin/runtime/<platform>/ store tiers (as consumed; fixture tier for tests); src/tests/shell/testsuites/punkexe/ (characterization)
Goal: make.tcl's kit surfaces (bake/bakelist/bin) operate on an explicit target platform rather than the driving tclsh's personality: any capable host (native windows tclsh, msys/cygwin-runtime tclsh, a FreeBSD host running linux binaries) addresses the same per-target runtime store, artifact naming and process handling for the kits it reports/bakes; non-native-personality runtime tiers (e.g third-party mingw64 builds served from punkbin or an alternative artifact server) are representable as first-class mapping entries with their own store tier; and zip-type kit assembly no longer requires zipfs in the driving tcl.
Acceptance: an msys/cygwin-runtime tclsh driving bakelist/bake on windows reports - and for a changed kit, bakes and deploys - the same win32-x86_64 kit set with identical names and store addressing as a native tclsh, with the pre-deploy process sweep using windows tooling for win32 targets regardless of host personality; punk::platform and the make.tcl inline canon map MSYS_NT/MINGW64_NT/CYGWIN_NT/UCRT64-class hosts to stable documented tags (MSYSTEM-variance documented, no windows build number in the tag) and 'help platforms' lists them; a mapvfs entry declaring a non-default target platform lists in bakelist with its own tier's store presence and bakes to a correctly suffixed artifact, verified against a fixture store tier (a real third-party runtime is not required); a zipfs-less driving tcl (8.6) assembles a zip-type kit via runtime split + punk::zip::mkzip + concatenation (archive-start-relative offsets) and the artifact boots on a zipfs-capable runtime; the linuxulator expectation (a linux-personality tclsh on FreeBSD 14/15 addresses the linux-x86_64 store unchanged) is recorded here with verification deferred to the planned linux-kit-on-FreeBSD trial; native-windows behaviour stays characterization-stable (existing punkexe maketcl tests pass unchanged).
## Context
Field session 2026-07-26: the scoop msys2 tclsh8.6 driving `src/make.tcl
bakelist` on windows showed every configured kit as runtime=missing (and
deployed absent) from two compounding causes: the store folder derives from the
HOST's platform canon (observed `mingw64-x86_64` under a MINGW64_NT os string -
note the tag follows the MSYSTEM environment, so the same binary can canonize
as msys/ucrt64/mingw64 in different shells), and the platform=unix branch
probes suffixless filenames - an assumption that is false for the entire
cygwin family, whose executables are `.exe` files (the msys tclsh itself is
`tclsh8.6.exe`). The interim self-diagnosis shipped in punkshell 0.24.1
(bakelist store-folder WARNING with the canon derivation + `(FOLDER MISSING)`
header mark; bake's no-runtimes exit note) tells the user what happened; this
goal makes the tooling actually cater for it.
The underlying defect is a conflation: make.tcl's scattered
`$::tcl_platform(platform) eq "windows"` tests mix three separable concerns -
host process semantics (cp vs `cmd /c copy`, `ps`/`kill` vs
tasklist/taskkill, path personality), target artifact naming (`.exe`
suffixing, kit/output names), and store addressing (`bin/runtime/<canon>`).
An msys host pulls them apart. The process sweep is the sharpest edge: msys
`ps` sees only msys-descendant processes, so a natively-launched kit exe is
invisible to the pre-deploy sweep (taskkill remains exec-able from msys).
Direction (user, 2026-07-26): even if msys never becomes a zig buildsuite
target, third-party-built runtime tiers (e.g an msys2-packaged tclsh) served
from punkbin or an alternative artifact server must be supportable as kit
runtimes - REPRESENTATION (mapping entry + store tier) belongs here;
publication/fetch is the flagged follow-on goal.
FreeBSD linuxulator parallel (user): a linux-personality tclsh running on
FreeBSD via the linux compat layer is expected to self-identify as linux and
address `bin/runtime/linux-x86_64` correctly - the planned
linux-kit-on-FreeBSD-14/15 trial doubles as verification that target-keyed
stores follow runtime personality, not host OS identity.
Zip offsets (user correction, 2026-07-26): tcl zipfs kits mount with
archive-start-relative offsets - cat-style construction (split the original
runtime first, then append a freshly written zip) is valid; `zipfs mkimg`'s
file-relative offset adjustment is one convention, not a mounting
requirement. The existing zipcat kit type is the in-repo proof, and its
machinery already has zipfs-less fallbacks.
## Approach
- One `target_platform` derivation: default from the host canon, with
cygwin-family hosts on windows defaulting to target `win32-x86_64`;
per-entry mapping override for non-default tiers (a minimal compatible
extension of the current vfsconfig entry - e.g a fourth element - whose
schema home is G-024's toml conversion; the parsed-model seam (G-121,
achieved - see goals/archive/G-121-bakelist-selective-bake.md) keeps every
consumer format-agnostic while the format moves).
- Key store folder, suffixing, kit/artifact naming, presence checks and the
process-sweep tool selection by TARGET; keep copy commands, path handling
and prompt behaviour host-driven.
- punk::platform canon: explicit MSYS_NT/MINGW64_NT/CYGWIN_NT/UCRT64 mapping
to stable documented tags with the MSYSTEM-variance caveat; the make.tcl
inline canon copy stays in sync per its existing comment contract.
- zip-type fallback for zipfs-less driving tcls: reuse the zipcat machinery
(extract_preamble split + punk::zip::mkzip + concatenation).
- Characterization against a fixture store tier so no real third-party
runtime is required; the msys-hosted acceptance run uses the dev machine's
msys tclsh where present (else recorded env-gated, per punkexe suite
conventions).
## Alternatives considered
- Per-site host patches (special-case each windows test) - rejected: the
three-concern conflation just re-accumulates with each new host class.
- Full self-hosted msys platform lineage (msys-personality kits, accelerator
builds, own test baselines) - out of scope: the third-party tier direction
covers the need without a new first-party build lineage.
- Status quo (require a native tclsh to drive make.tcl) - rejected: field
friction (the all-missing bakelist), and upcoming linux/FreeBSD hosts make
host-personality assumptions untenable regardless of msys.
## Notes
- Related: G-024 - owns the mapping format; the per-entry target platform is
drafted as a minimal compatible extension and its schema home is that
goal's toml conversion (make.tcl's own comments already pair the platform
column with the toml change).
- Related: G-105 - cross-target zig builds populate first-party non-native
store tiers; this goal is the consuming/addressing side.
- Related: G-114 - same punk::platform canon vocabulary, applied to
per-platform tm module roots.
- Related: G-101 - 8.6 kit containers ride the same mapping/store surfaces;
lister/bake stay kit-type-agnostic (per the archived G-121 decision).
- Related: G-057 - per-entry mapping extension precedent (per-vfs icon
override rides the same entry shape).
- Related: G-023 - kit naming schemes; target-keyed naming must not collide
with its version-named binaries direction.
- Follow-on: G-123 (drafted 2026-07-26, user-approved) - third-party runtime
tier publication/fetch via punkbin or alternative servers, with schema-v2
provenance labeling and server-trust-keyed consent.
- Out of scope: msys pty raw-mode console/repl behaviour - the cursor-row-1
field finding of 2026-07-26 is recorded in G-013's Notes.

88
goals/G-123-thirdparty-runtime-tiers.md

@ -0,0 +1,88 @@
# G-123 Third-party runtime tiers via artifact servers: per-target publish, list, fetch, materialize
Status: proposed
Scope: src/scriptapps/punk-runtime.ps1 + src/scriptapps/bin/punk-runtime.bash + punk-runtime_wrap.toml (named-tier operations in both payloads; bin/punk-runtime.cmd via the established re-wrap channel); punkbin artifact repo (external, c:/repo/jn/punkbin - per-tier layout, defaults, sha1sums, schema-v2 provenance sidecars incl. retroactive backfill, layout docs); src/buildsuites/suite_tcl90/ (family_artifacts.tcl schema-v2 emission for suite-built artifacts); alternative-server override + trusted-server set (PUNKBIN_URL pattern; consent surface coordinated with G-006); src/tests/shell/testsuites/binscripts/ (fixture-server characterization); bin/runtime/<tier>/ stores (as materialization targets)
Goal: runtime artifacts for tiers beyond the host-native platform - third-party-built personalities (e.g an msys2-packaged mingw64-x86_64 tclsh) and cross-personality consumers (a linux tier used from FreeBSD) - are first-class on punkbin or an alternative artifact server: punk-runtime lists/fetches/uses artifacts for an explicitly named target tier from both payloads, materializing sha1-verified -r<N> artifacts + toml metadata into bin/runtime/<tier>/, with build-origin provenance (suite-built vs third-party) labeled on every artifact, consent keyed to server trust rather than artifact origin, and the G-122 chain completed from server artifact to a bakeable mapping entry.
Acceptance: against a fixture artifact server (httpfixture precedent) carrying a non-native tier, punk-runtime supports an explicit target-tier argument on list/use (and the fetch path) in BOTH payloads - ps1 driven as the committed bin/punk-runtime.cmd under cmd.exe, bash driven under msys bash - with row/verdict parity and the established single-line row vocabulary scoped to the named tier; 'use' materializes the tier's working copy under bin/runtime/<tier>/ with sha1 verification and beside-toml metadata, and freshness verdicts compare against that tier's server default; artifact-toml schema v2 provenance distinguishes suite-built from third-party (class/builder/source_url/upstream_ref/retrieved), with third-party rows carrying a compact origin tag in list -remote's sparse notes column and full fields in the per-name detail view while suite-built rows are unchanged; existing punkbin third-party artifacts (e.g win32-x86_64/tclsfe-x64.exe, tclkit86bi.exe) gain retroactive schema-v2 sidecars additively (artifact bytes, bare pre-family names and sha1sums.txt authority unchanged; toml-less entries continue to degrade as no-basis rows); consent is keyed to SERVER TRUST - fetches from the trusted set (default: the canonical punkbin origin) prompt nothing, leaving existing unattended flows unchanged, while the first fetch from an untrusted/alternative server gates on an explicit acknowledgement (G-006's mechanism when landed, else an interim documented flag) that surfaces the artifact's provenance class; an end-to-end fixture demonstration wires a fetched fixture-tier runtime through a G-122 non-default-target mapping entry to a bakelist row showing the runtime present; the punkbin layout docs record the per-tier + schema-v2 extension and existing native-tier clients are unaffected (existing binscripts tests pass unchanged).
## Context
Direction (user, 2026-07-26, recorded at G-122's drafting): runtime tiers may be
third-party built - e.g an msys2-packaged tclsh - and served from punkbin or a
punkbin-alternative artifact server, even if such platforms are never zig
buildsuite targets. G-122 owns REPRESENTATION (stable canon tag, store tier,
non-default-target mapping entry); this goal owns SOURCING (publish, list,
fetch, materialize).
Current punkbin state (surveyed 2026-07-26): the suite-built family artifacts
carry rich G-117 schema-v1 sidecars ([artifact] facts including packager - with
minisign sidecars already anticipated in its comment - plus [provenance]
suite/toolchain/checkout hashes and [tests] gate results), while the
third-party binaries beside them (win32-x86_64: tclsfe-x64.exe =
apnadkarni/tcl-sfe release, tclkit86bi.exe, tclkit902.exe, tclsh901k/t.exe,
tclsh902z*.exe - the last looking locally built pre-suite) have NO sidecars at
all, only sha1sums.txt coverage. Nothing machine-readable distinguishes build
origin; this goal fills that gap with the same schema rather than a parallel
mechanism.
Consent decision (user, 2026-07-26): keyed to SERVER TRUST, not artifact
origin - curating an artifact onto one's own punkbin is the consent act, so
labels are transparency and prompts appear only for servers outside the
trusted set. No new prompts may appear in existing flows, e.g the unattended
constraint fetch of tclsfe-x64 for the static-runtime suite (G-058, achieved -
see goals/archive/G-058-static-runtime-packages.md).
## Approach
- Extend, don't fork, the archived machinery: the -r<N> immutable artifact
tier + working-copy materialization + platforms/defaults server manifests
(G-103, achieved - see goals/archive/G-103-runtime-kit-family.md) and the
freshness verdict, httpfixture test harness and row vocabulary (G-119,
achieved - see goals/archive/G-119-runtime-active-freshness.md).
- Explicit target-tier argument on list/use/fetch in both payloads; ps1+bash
single-line row parity and restrained width per the established listing
conventions; third-party rows get a compact origin tag in the sparse notes
column of list -remote, full provenance in per-name detail; suite-built
rows stay quiet (expected state).
- Artifact-toml schema v2 (successor to the G-117 schema v1, achieved - see
goals/archive/G-117-self-describing-runtimes.md; the schema field exists
for this): [provenance] class = suite-built|third-party|local, builder,
source_url, upstream_ref, retrieved. The builder-vs-packager split is
deliberate: builder compiled the binary, packager (existing field) curated
it onto the server. Emission: family_artifacts.tcl for suite artifacts;
authored sidecars for third-party ingests; retroactive backfill for the
existing third-party items is ADDITIVE ONLY (artifact bytes, bare
pre-family names and the sha1sums.txt integrity authority untouched).
- Trusted-server set: the canonical punkbin origin trusted by default; the
configuration surface (list membership, acknowledgement recording) is
settled in the work, coordinated with G-006's consent pattern.
## Alternatives considered
- Artifact-origin-keyed consent - rejected (user decision 2026-07-26): it
adds prompts to already-curated flows; server-set membership is the real
trust boundary today.
- Signing/attestation in this goal - deferred deliberately (user: "signing
for down the track"): minisign sidecars are already anticipated by the
schema's packager comment and become the natural successor goal when
third-party tiers or alternative servers are routine. Until then sha1sums
remain transport-integrity only, and the docs say so plainly.
- A separate server namespace for third-party artifacts - rejected:
provenance belongs on the artifact record, not the URL layout; mirrors
would lose it.
## Notes
- Depends on: G-122 (canon tags, target-keyed stores, non-default-target
mapping entries - the end-to-end acceptance item consumes its fixture-tier
support).
- Related: G-006 - consent-gated binary downloads; the untrusted-server gate
here is a concrete consumer (interim documented flag if this lands first).
- Related: G-067 - artifact-channel kinship (module tier vs runtime tier);
G-116 already points punkbin publication at the G-067 library class.
- Related: G-105 - first-party cross-target tiers arrive via zig -target and
ride the same server surfaces this goal extends.
- Related: G-101 - a served 8.6-container tier would ride this mechanism.
- Signing deferred by user decision 2026-07-26 ("server trust ... with
signing for down the track") - flag a dedicated signing/attestation goal
when the first genuinely external tier or server appears.

61
goals/G-121-bakelist-selective-bake.md → goals/archive/G-121-bakelist-selective-bake.md

@ -1,6 +1,6 @@
# G-121 make.tcl bakelist + selective bake: list configured kits, bake by name
Status: proposed
Status: achieved 2026-07-26
Scope: src/make.tcl (bake @values + new bakelist subcommand; SUBOPTS/SUMMARIES/HELPTEXTS/SUBGROUPS, help/workflow text); src/runtime/mapvfs.config (as read; todo-comment retirement); src/tests/shell/testsuites/punkexe/ (bakelist characterization if feasible); layout make.tcl copies via established sync channels
Goal: make.tcl can report and selectively execute the configured kit matrix: `bakelist` lists the configured kit outputs (name, kit type, runtime with presence, vfs folder, deployed state) with optional per-kit name filtering, and `bake <kitname ...>` bakes and deploys exactly the named kits - bare `bake` unchanged (all configured kits). Both consume the PARSED mapping model, not the file format, so the G-024 toml conversion slides underneath without changing the surface.
Acceptance: `make.tcl bakelist` under a plain tclsh and under a punk kit executable lists every kit output configured in the runtime mapping with name, kit type, runtime (present/missing), vfs folder and a deployed-state indication (bin copy absent/current/stale vs the build product), and `bakelist <kitname>` filters to the named entries; `make.tcl bake <kitname ...>` builds and deploys only the named kit(s) - a recorded verification shows the named kit's artifacts refreshing while other kits' _build/bin artifacts and punkcheck records are untouched - and an unknown name errors without building, listing the configured names; bare `make.tcl bake` processes the same kit set as before the change; help/workflow text documents both per the update contract and the mapvfs.config todo comment is retired to point here; a piped bakelist characterization test lands under punkexe if feasible (else the infeasibility is recorded here); project version minor-bumped with a CHANGELOG entry.
@ -57,6 +57,65 @@ kit-state divergence.
already rejected 'kits' as colliding with kit runtimes; `bakelist` is
self-locating next to `bake`.
## Progress
- 2026-07-26 ALL ACCEPTANCE ITEMS VERIFIED - achieved flip. Shipped in punkshell
0.24.0 (src/make.tcl; git commit "G-121: make.tcl bakelist + selective bake").
Both surfaces (and the argdoc kit-name choices) consume the shared parsed
mapping model ::punkboot::lib::mapvfs_parse / mapvfs_kit_outputs /
mapvfs_match_outputs - the bake kit machinery's inline parse was replaced by
the same helper, so the G-024 toml conversion swaps the reader underneath.
- Verification evidence (native Tcl 9.0.3 tclsh90 driving the working tree on
windows win32-x86_64; punk902z kit exe via the punkexe suite):
- bakelist under plain tclsh AND under punk902z: all 10 configured entries
listed with name, type (kit|zip|zipcat), runtime + presence, vfs folder,
deployed state; runtime=missing rows for tclsh90magic and the linux
tclkit; piped output ESC-free (G-113). `bakelist punk91` filters to the
named row and adds the per-kit detail block (resolved paths/sizes/mtimes).
Deployed-state content-compare path proven live: equal bytes with
differing mtimes classified current.
- selective refresh: after deleting src/_build/punk91.exe, `bake -confirm 0
punk91` rebuilt and deployed punk91 only. Recorded stat diff over ALL of
src/_build and bin: only punk91.exe, punk91.exe.vfs, raw_tclsfe-x64.exe
and bin/punk91.exe changed. punkcheck diffs (src/_build, bin, src/vfs
.punkcheck) mention only punk91's records; negative grep for every other
kit/vfs name (punk905, punk9_beta, punk9bi, punkbi, punksys, punk86,
punkmagic, punk902z, punkshell902, their vfs folders) clean. vfslibs phase
narrowed with printed skip notes for unselected vfs folders.
- unknown name: `bake nosuchkit` and `bakelist nosuchkit` exit 1 before any
build, listing the configured names; a flag-looking unknown adds the
flags-before-names hint (punk::args consumes options before values, so
`bake -confirm 0 punk91` is the canonical order).
- bare `bake -confirm 0`: same kit set as pre-change (punkbi, punksys,
punk86, punk91, punk905, punk9_beta, punk902z, punk9bi_beta processed;
the 2 historical missing-runtime warnings; no selective narrowing).
- characterization test landed: src/tests/shell/testsuites/punkexe/
maketclbakelist.test - 4 PASS under punk902z (full-report row/vocabulary +
ESC-free, filtered detail, unknown-name errors on both surfaces with the
no-build guarantee); maketclcolour.test still 3 PASS.
- help/workflow text updated per the update contract (workflow verified
normal + PUNKBOOT_PLAIN=1, width <= 100); mapvfs.config todo retired to
point here; project version minor-bumped 0.23.3 -> 0.24.0 with CHANGELOG
entry; layout make.tcl copies synced via make.tcl packages; degraded-mode
(PUNKBOOT_PLAIN) dispatch carries trailing kit names for bake/bakelist.
- Design outcomes worth keeping:
- Discoverability nicety implemented: configured kit names render as
punk::args choices on bake/bakelist (-choicerestricted 0 - informational
only; handler validation stays authoritative and works when the mapping
cannot be parsed at define time).
- The enumeration helper replicates the kit loop's naming including the
duplicate-appname `<appname>_<runtime>` disambiguation; parity assumes the
config keeps appnames unique (broken-config edge documented in the helper
comment).
- Dash-runtime ('-') entries list (runtime "-", no runtime file) but the kit
loop's historical existence-filter still never builds them - pre-existing
dead path, deliberately unchanged here.
- Selective bake fail-fasts on a selected kit whose runtime/vfs is missing;
bare bake keeps the historical warn-and-continue for the full set.
- runtime_materialization_warning's toml-revision core moved to
::punkboot::lib::runtime_materialization_check, shared with bakelist's
rtrev=r<cur><r<max> notes tag (the reporting complement noted below).
## Notes
- 2026-07-25 (landed separately, same day as drafting): make.tcl bake now

2
punkproject.toml

@ -1,4 +1,4 @@
[project]
name = "punkshell"
version = "0.23.1"
version = "0.24.1"
license = "BSD-2-Clause"

3
src/AGENTS.md

@ -63,7 +63,7 @@ Recovery after a wrong path guess:
- Use `tclsh src/make.tcl modules` to build just the module packages.
- Use `tclsh src/make.tcl libs` to build just the library packages.
- Use `tclsh src/make.tcl packages` to build both modules and libraries.
- make.tcl colour is terminal-aware (G-113): piped/redirected runs (agent harnesses, CI, log capture) automatically produce fully ESC-free output with no caller action required - an ansistrip channel transform on stdout+stderr guarantees zero ESC bytes for every emitter, including module-side ones (punkcheck summaries, punk::args tables). Interactive terminal runs keep colour (stdout tty probe via the `-winsize` channel option, Tcl 8.7+/9). Precedence: `NO_COLOR` (any value) always suppresses colour; `PUNK_FORCE_COLOR`/`FORCE_COLOR` (value other than 0/false/no/off) re-enables ANSI on piped output; otherwise the probe decides. Tcl 8.6 has no boot-phase probe - colour fails OFF there (use the force vars for 8.6 interactive colour). stderr follows the stdout decision; `make.tcl shell` pops the transform (repl colour is the shell's own concern). `tclsh src/make.tcl check` reports the active policy (`colour policy (G-113): mode=...` line; modes tty|forced|nocolor|piped-plain|tcl86-plain). Pinned by `src/tests/shell/testsuites/punkexe/maketclcolour.test`. Agents no longer need to set NO_COLOR for captured runs (setting it remains harmless).
- make.tcl colour is terminal-aware (G-113): piped/redirected runs (agent harnesses, CI, log capture) automatically produce fully ESC-free output with no caller action required - an ansistrip channel transform on stdout+stderr guarantees zero ESC bytes for every emitter, including module-side ones (punkcheck summaries, punk::args tables). Interactive terminal runs keep colour (stdout tty probe via the `-winsize` channel option, Tcl 8.7+/9). Precedence: `NO_COLOR` (any value) always suppresses colour; `PUNK_FORCE_COLOR`/`FORCE_COLOR` (value other than 0/false/no/off) re-enables ANSI on piped output; otherwise the probe decides. Tcl 8.6 terminals are auto-detected without dependencies: windows consoles via the console channel's utf-16 encoding signature (`-encoding unicode` - only 8.6 console channels report it; the byte-level strip transform is never pushed onto a utf-16-class channel, which it would corrupt - per-channel push, so `> file` from an 8.6 console wraps stdout only), and unix-class hosts (linux/WSL/mac, plus msys2/cygwin-runtime tclsh builds that report platform unix on windows) via the tty channel signature (real ttys expose `-mode` etc; pipes/files lack them). The force vars remain for exotic environments. stderr follows the stdout decision; `make.tcl shell` pops the transform (repl colour is the shell's own concern). `tclsh src/make.tcl check` reports the active policy (`colour policy (G-113): mode=...` line; modes tty|forced|nocolor|piped-plain|tcl86-plain). Pinned by `src/tests/shell/testsuites/punkexe/maketclcolour.test`. Agents no longer need to set NO_COLOR for captured runs (setting it remains harmless).
- Use `tclsh src/make.tcl vendorupdate` to refresh vendormodules from config. It warns (non-fatal) for each source project whose fossil/git checkout is dirty - vendored artifacts built from a dirty tree have no committed provenance; commit in the source project to clear the warning (enforcement policy tracked by goal G-026).
- All build/promotion commands (`bakehouse`, `packages`, `modules`, `libs`, `bake`, `vfslibs`, `bin`, `bootsupport`, `vfscommonupdate`) warn similarly when this project's own `src/` has uncommitted fossil/git changes (dirt outside `src/` is ignored). Warn-only by default EXCEPT `bakehouse` (aborts by default; `-dirty-abort 0` overrides); pass `-dirty-abort` elsewhere to make the check aborting. For evaluating uncommitted source without a build, prefer `<builtexe> src` / `<builtexe> src shell`. The check is `punkboot::utils::vcs_dirty_warnings` (optional scope argument) loaded guardedly from bootsupport - if the snapshot is stale/missing the check degrades to a skip notice (but `-dirty-abort` then aborts rather than silently losing the requested strictness).
- Provenance warnings (dirty-src gate + vendorupdate source-project check) print with a plain column-0 `PROVENANCE-WARNING:` token (grep for it in captured build output) and are recapped at the end of the run via make.tcl's wrapped `::exit`. Interactive terminal runs get a 3-second ctrl-c grace countdown before a dirty build proceeds; piped/agent runs skip it. `tclsh src/make.tcl check` reports the current src provenance status and what the build commands would do.
@ -72,6 +72,7 @@ Recovery after a wrong path guess:
- make.tcl subcommands and flags are punk::args-declared (G-030): `tclsh src/make.tcl help ?subcommand?` or `<subcommand> -help` shows tabled usage; invalid arguments produce punk::args usage errors. The dispatch degrades to plain scan/help when the bootsupport punk::args (or its rendering stack) is stale or unavailable; `PUNKBOOT_PLAIN=1` forces the degraded mode for troubleshooting. The `::punkboot::argdoc` definitions are constructed (string-built) and use the G-045 authoring mechanisms — block-form help values + `@normalize`, explicit `@form -synopsis` — see src/modules/AGENTS.md "punk::args definition authoring ergonomics" before editing them or authoring similar definitions.
- Uncommitted `make.tcl`-generated outputs may be batched into one commit, regardless of how many `make.tcl` invocations produced them. This covers punkcheck-managed build outputs that are VCS-tracked: `src/bootsupport/`, `_vfscommon.vfs/modules` + `/lib`, declared per-kit `*.vfs/lib_tcl<N>/<pkg>` subfolders, the thin-layout sync copies (`src/project_layouts/vendor/punk/<layout>/src/{make.tcl,build.tcl}` + bootsupport manifest copies), and the module-shipped layout payload under `src/modules/punk/mix/#modpod-templates-*/templates/project_layouts/` (G-087: synced from `src/project_layouts` - never hand-edit the modpod copy). (Root `modules/`, `lib/`, `modules_tcl<N>/`, `lib_tcl<N>/` are gitignored and never committed.) Each module's old→new version rename is atomic; a regeneration reflects one build-output refresh, not independent hand-edits. This is a carve-out from generic "split aggressively" commit guidance for punkcheck-managed build outputs only — it does not cover user-curated VFS content (`_config/`, per-kit `*.vfs/` outside declared `lib_tcl<N>` subfolders, `_aside/`, `mkzipfix.vfs`, `_vfscommon.vfs/doc`), which commits separately per its own concerns.
- Kit bakes consume the punk-runtime WORKING COPIES under `bin/runtime/<platform>/`: `make.tcl bake`/`bakehouse` emit a `BUILD-WARNING:` (recapped at end of run) when a wrapped runtime's beside-toml revision is older than an `-r<N>` artifact present in the same folder - the forgot-to-switch-back guard for deliberate `punk-runtime use <old-rN>` testing excursions. Heed it before trusting freshly baked family kits; `bin/punk-runtime.cmd use <artifact-r<N>>` (`.exe` suffix optional) re-materializes the working copy.
- `tclsh src/make.tcl bakelist ?kitname ...?` (G-121) reports the kit outputs configured in `src/runtime/mapvfs.config`: kit name, kit type, runtime (with presence in the runtime store), vfs folder, and the deployed state of `bin/<kit>` vs the `src/_build` build product (`current|stale|absent|nobuild`), with anomalies (`runtime=missing`, `vfs=missing`, `rtrev=r<cur><r<max>` materialization staleness) in a trailing notes column. A nonexistent per-platform store folder is flagged loudly (header `(FOLDER MISSING)` + stderr canon derivation; bake's no-runtimes exit self-diagnoses the same way) - the classic trigger is an msys/cygwin-runtime tclsh whose platform canon (e.g `mingw64-x86_64`) is not the windows store the kits use. Name arguments filter the report and add a per-kit detail block. `make.tcl bake ?kitname ...?` bakes and deploys only the named kits - other kits' `_build`/`bin` artifacts and punkcheck records are untouched and the vfslibs phase narrows to the named kits' vfs folders; an unknown name errors before any build, listing the configured names; flags go before kit names (`make.tcl bake -confirm 0 punk91`). Bare `bake` processes all configured kits as before. Both surfaces consume the shared parsed-mapping helpers (`::punkboot::lib::mapvfs_*`) rather than the file format, so the G-024 config-format work swaps the reader underneath. Piped characterization: `src/tests/shell/testsuites/punkexe/maketclbakelist.test`.
- Use `punk make.tcl bakehouse` or `punk902z make.tcl bakehouse` inside Punk shell when building binaries through Punk (the deprecated `project` alias still maps). Driving make.tcl from a built punk executable is supported for informational/update subcommands and for kit builds of *other* kits — the kit whose deployed executable is running the build is skipped with a warning (it cannot be replaced while running, and the pre-deploy process sweep must not kill the build itself; the sweep also excludes the build's own pid in all cases). Rebuild that kit from tclsh or a different kit.
- **Punk-exe-hosted make.tcl runs in a pre-loaded interp, not a virgin one** — the kit's script-mode boot has already loaded much of the punk stack (punk, punk::lib, punk::repl, punk::console, punk::du, flagfilter, struct::set ...) and set process state (app-punkscript forces `::tcl_interactive 0` for script semantics; libunknown/packagepreference are active). Consequences make.tcl must (and now does) handle explicitly: `package require` of an already-provided package is a no-op, so anything make.tcl breaks in the interp (the accelerator-reload block forgets+destroys sha1/md5/struct::* — it re-requires what was loaded), and anything computed at package-load time (punk::repl's `::tcl_interactive` probe — the shell branch recomputes it before `repl::start`), must be restored deliberately rather than relying on later loads to re-fire. Also note the copies that run in this mode are the *kit's* pre-loaded modules, not the bootsupport snapshots make.tcl's paths would otherwise prefer — silent provenance mixing when versions diverge. When adding interp-surgery or load-time-state assumptions to make.tcl, test under both `tclsh src/make.tcl ...` and `<punkexe> src/make.tcl ...`.
- Binary images are platform-specific; build on each target platform rather than expecting a cross-platform flag.

1
src/bootsupport/modules/include_modules.config

@ -104,6 +104,7 @@ set bootsupport_modules [list\
modules punk::ns\
modules punk::overlay\
modules punk::path\
modules punk::platform\
modules punk::packagepreference\
modules punk::repl\
modules punk::repl::codethread\

BIN
src/bootsupport/modules/punk/mix/templates-0.2.0.tm

Binary file not shown.

237
src/bootsupport/modules/punk/platform-0.1.0.tm

@ -0,0 +1,237 @@
# -*- tcl -*-
# Maintenance Instruction: leave the 999999.xxx.x as is and use 'pmix make' or src/make.tcl to update from <pkg>-buildversion.txt
#
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem.
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository.
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
# (C) 2026
#
# @@ Meta Begin
# Application punk::platform 0.1.0
# Meta platform tcl
# Meta license BSD
# @@ Meta End
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Requirements
package require platform ;#Tcl's own platform package (vendored 1.0.19 in kits; 1.1.x in tcl9 core) - the RAW identifier underneath this module's normalization
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
namespace eval punk::platform {
#punk::platform - the CANONICAL punkshell platform naming layer (2026-07-22
#platform-folder synchronization; groundwork for the G-105 cross-target arc).
#
#punkshell artifact trees are organized by PLATFORM-DIR names of the form
#<os>-<cpu> (e.g win32-x86_64), used by: the punkbin artifact repo's top-level
#folders, bin/runtime/<platform>/ (the runtime store punk-runtime manages),
#lib_tclX/<platform> + vendorlib_tclX/<platform> auto_path dirs (wired by the
#boot machinery), and the G-103 runtime artifact metadata 'target' field.
#
#Tcl's platform package remains the raw identifier (platform::generic /
#platform::identify) but its outputs vary by version and pass some machine
#names through unmapped - the differences this module's normalize absorbs:
# - cpu 'amd64' (BSD machine names) -> x86_64
# - cpu 'aarch64' (linux arm64) -> arm64
# - os 'macos' (platform 1.1.x on osVersion>19; older versions and the
# boot's snipped copy say 'macosx') -> macosx
# - macosx cpu 'arm' -> arm64 (platform::generic's 'arm*' glob collapses
# the darwin 'arm64' machine name to 'arm'; Apple silicon is 64-bit only,
# so the arm token on macosx always means arm64. Elsewhere 'arm' is a
# genuine 32-bit token and is preserved.)
#The special name 'macosx' (no cpu suffix) is the RUNTIME-tier convention for
#universal (multi-arch) macOS binaries - punkbin and bin/runtime keep one
#universal folder; the per-arch macosx-x86_64/macosx-arm64 names serve the
#lib-tree tier where per-arch payloads are the norm.
#
#The boot machinery (punkboot::lib in src/vfs/_config/punk_main.tcl /
#project_main.tcl and src/make.tcl) cannot 'package require' at its stage and
#carries a snipped platform::generic plus an inline copy of THIS module's
#normalization mapping - if the mapping here changes, those copies must
#change with it (each carries a pointer comment to this module).
namespace eval argdoc {
variable PUNKARGS
}
#canonical platform records. status:
# supported - a punkshell target: artifact trees exist or are planned and
# tooling/build arcs (G-103/G-105) treat it as a real target
# dormant - recognized and folders may exist, but utility is under review
# recognized - a name the naming scheme reserves; no artifacts or tooling yet
#tiers: which artifact-tree tiers use the name -
# runtime = punkbin platform folders + bin/runtime/<platform>/
# lib = lib_tclX/<platform> + vendorlib_tclX/<platform> auto_path dirs
#buildsuite: whether OUR zig buildsuite system (src/buildsuites; G-103 kit
#family, G-105 cross-target) produces runtimes for the platform. A SEPARATE
#AXIS from tiers deliberately: punkbin (or a third-party repo using the same
#structure) can host runtimes built by any mechanism - a platform can be
#runtime-tier hosted while buildsuite=none (we may never build it ourselves).
# supported - a tracked suite produces family artifacts today
# planned - named in an active/proposed goal (e.g G-105 linux-first)
# candidate - plausible zig target, no committed goal
# none - no build intention; hosted/third-party runtimes only
variable platforms {
win32-x86_64 {status supported tiers {runtime lib} buildsuite supported notes "primary development platform (suite_tcl90)"}
win32-ix86 {status supported tiers {runtime lib} buildsuite candidate notes "32-bit x86; hosting for available third-party runtimes/libs - zig can target it but a buildsuite is undetermined"}
linux-x86_64 {status supported tiers {runtime lib} buildsuite planned notes "G-105 first cross-target, WSL-verified"}
linux-arm64 {status supported tiers {runtime lib} buildsuite candidate notes "aarch64; punkbin's existing arm kit predates the arm64 name and sits in linux-arm"}
linux-arm {status supported tiers {runtime lib} buildsuite candidate notes "32-bit arm"}
macosx {status supported tiers {runtime} buildsuite candidate notes "universal (multi-arch) macOS binaries - runtime tier keeps one folder; zig darwin cross needs SDK work"}
macosx-x86_64 {status supported tiers {lib} buildsuite none notes "would follow a macosx runtime arc"}
macosx-arm64 {status supported tiers {lib} buildsuite none notes "Apple silicon; would follow a macosx runtime arc"}
freebsd-x86_64 {status supported tiers {runtime lib} buildsuite candidate notes ""}
freebsd-arm64 {status supported tiers {runtime lib} buildsuite candidate notes "no artifacts yet"}
msys-x86_64 {status dormant tiers {lib} buildsuite none notes "msys/cygwin-built tclsh runtimes; utility under review"}
openbsd-x86_64 {status recognized tiers {runtime} buildsuite none notes "hosted third-party runtimes possible"}
netbsd-x86_64 {status recognized tiers {runtime} buildsuite none notes "hosted third-party runtimes possible"}
dragonflybsd-x86_64 {status recognized tiers {runtime} buildsuite none notes "hosted third-party runtimes possible"}
}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::platform::platforms
@cmd -name punk::platform::platforms\
-summary\
"Canonical punkshell platform records."\
-help\
"Return the canonical punkshell platform-name records as a dict
keyed by platform-dir name (e.g win32-x86_64). Each value is a
dict with keys:
status supported|dormant|recognized
tiers which artifact-tree tiers use the name
(runtime = punkbin + bin/runtime folders,
lib = lib_tclX/vendorlib_tclX auto_path dirs)
buildsuite supported|planned|candidate|none - whether the
punkshell zig buildsuite system produces
runtimes for the platform. Deliberately a
separate axis from tiers: punkbin-structured
repos can host runtimes built by any mechanism,
so a platform can be runtime-tier hosted while
buildsuite=none.
notes free-text qualifiers
These names are the contract for punkbin platform folders,
bin/runtime/<platform>/, lib_tclX/vendorlib_tclX platform
dirs and the G-103 runtime-artifact metadata target field.
See also 'help platforms' in the punk shell."
@values -min 0 -max 0
}]
}
proc platforms {} {
variable platforms
return $platforms
}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::platform::normalize
@cmd -name punk::platform::normalize\
-summary\
"Normalize a platform identifier to the canonical punkshell name."\
-help\
"Normalize a platform::generic-style <os>-<cpu> identifier (or an
already-canonical punkshell name) to the canonical punkshell
platform-dir name:
cpu amd64 -> x86_64, aarch64 -> arm64
os macos -> macosx (Tcl platform 1.1.x renamed modern macOS)
macosx cpu arm -> arm64 (platform::generic's arm* glob folds
the darwin arm64 machine name to arm; Apple silicon is
64-bit only - non-macosx 'arm' stays 32-bit arm)
Unrecognized os/cpu tokens pass through unchanged - normalize
never invents names, it only folds known aliases."
@values -min 1 -max 1
platform -type string -help\
"Platform identifier, e.g the result of platform::generic."
}]
}
proc normalize {platform} {
set parts [split $platform -]
if {[llength $parts] < 2} {
#single-token names (e.g the universal 'macosx') - fold macos only
if {$platform eq "macos"} {
return macosx
}
return $platform
}
#os may itself contain a dash in principle - treat last token as cpu
set cpu [lindex $parts end]
set os [join [lrange $parts 0 end-1] -]
switch -- $os {
macos {set os macosx}
}
switch -- $cpu {
amd64 {set cpu x86_64}
aarch64 {set cpu arm64}
i386 - i486 - i586 - i686 {set cpu ix86}
arm {
if {$os eq "macosx"} {
set cpu arm64
}
}
}
return "${os}-${cpu}"
}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::platform::local
@cmd -name punk::platform::local\
-summary\
"Canonical punkshell platform name of the running interpreter."\
-help\
"Return the canonical punkshell platform-dir name for the running
interpreter: platform::generic normalized per
punk::platform::normalize.
With -tier runtime, macosx per-arch names collapse to the
universal 'macosx' (the runtime-store convention: punkbin and
bin/runtime keep one universal macOS folder; the same collapse
src/make.tcl applies when locating bin/runtime/<platform>).
-tier lib (the default) returns the per-arch name used by the
lib_tclX/vendorlib_tclX auto_path dirs."
@opts
-tier -type string -default lib -choices {lib runtime} -help\
"Artifact-tree tier the name is for."
@values -min 0 -max 0
}]
}
proc local {args} {
set tier lib
foreach {k v} $args {
switch -- $k {
-tier {
if {$v ni {lib runtime}} {
error "punk::platform::local - invalid -tier '$v' (expected lib|runtime)"
}
set tier $v
}
default {
error "punk::platform::local - unknown option '$k' (known: -tier)"
}
}
}
set p [normalize [::platform::generic]]
if {$tier eq "runtime" && [string match macosx-* $p]} {
return macosx
}
return $p
}
}
namespace eval ::punk::args::register {
#use fully qualified so 8.6 doesn't find existing var in global namespace
lappend ::punk::args::register::NAMESPACES ::punk::platform
}
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
## Ready
package provide punk::platform [namespace eval punk::platform {
variable version
set version 0.1.0
}]
return

915
src/make.tcl

File diff suppressed because it is too large Load Diff

890
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl vendored

File diff suppressed because it is too large Load Diff

890
src/project_layouts/vendor/punk/basic/src/make.tcl vendored

File diff suppressed because it is too large Load Diff

890
src/project_layouts/vendor/punk/project-0.1/src/make.tcl vendored

File diff suppressed because it is too large Load Diff

6
src/runtime/mapvfs.config

@ -65,9 +65,9 @@ tclsfe-x64.exe {punk9wintk903.vfs punk91 zip}
#_beta convention: wrap freshly suite-built runtimes as *_beta kits even BEFORE they have passed the
# punk test suites - so new-runtime behaviour can be trialled interactively. Drop _beta on acceptance.
# (punk905_beta trialled and PROMOTED to punk905 2026-07-21.)
#todo: 'make.tcl bake' enhancement - build a single configured kit by name + list configured kits
# (sibling-ergonomics wish recorded in G-104, achieved 2026-07-25 WITHOUT this item -
# see goals/archive/G-104-maketcl-buildsuite-surface.md; still open)
#'make.tcl bakelist ?kitname ...?' lists the kit outputs configured here (runtime/vfs presence +
# deployed state); 'make.tcl bake ?kitname ...?' bakes/deploys only the named kits (G-121, achieved -
# see goals/archive/G-121-bakelist-selective-bake.md; wish originally recorded in archived G-104).
#5
tclsh905.exe {punk9wintk905.vfs punk905 zip}

150
src/tests/shell/testsuites/punkexe/maketclbakelist.test

@ -0,0 +1,150 @@
package require tcltest
#Piped characterization of the make.tcl bakelist report and the bake-side selective
#kit-name validation (goal G-121). Runs the WORKING TREE's src/make.tcl under the
#built punk executable's 'script' subcommand with output captured through a pipe,
#and pins:
# - 'bakelist' exits 0, output is ESC-free (G-113 piped policy), the stable header
# row is present and the punk91 row carries the expected type/runtime/vfs columns
# with a deployed state from the documented vocabulary
# - 'bakelist <kitname>' filters to the named entry and appends the per-kit detail
# block; unfiltered rows do not appear
# - 'bakelist <unknown>' exits 1 naming the unknown and listing the configured names
# - 'bake <unknown>' exits 1 BEFORE any build ("nothing built"), listing the
# configured names - the no-build guarantee of selective bake
#The row pins are characterization of the current src/runtime/mapvfs.config kit
#matrix (punk91 = tclsfe-x64 + punk9wintk903.vfs, zip) - a deliberate config change
#legitimately updates them.
#
#Target executable resolved from env(PUNK_SHELL_TEST_EXE), else <projectroot>/bin/punk902z.exe
#then <projectroot>/bin/punkshell902. Skipped (constraint punkexeavailable) if none found.
namespace eval ::testspace {
namespace import ::tcltest::*
variable testdir [file dirname [file normalize [info script]]]
#<projectroot>/src/tests/shell/testsuites/punkexe -> 5 levels up to <projectroot>
variable projectroot [file normalize [file join $testdir .. .. .. .. ..]]
variable maketcl [file join $projectroot src make.tcl]
variable punkexe ""
if {[info exists ::env(PUNK_SHELL_TEST_EXE)] && $::env(PUNK_SHELL_TEST_EXE) ne ""} {
set punkexe [file normalize $::env(PUNK_SHELL_TEST_EXE)]
} else {
foreach candidate [list [file join $projectroot bin punk902z.exe] [file join $projectroot bin punkshell902]] {
if {[file exists $candidate]} {
set punkexe $candidate
break
}
}
}
testConstraint punkexeavailable [expr {$punkexe ne "" && [file exists $punkexe]}]
variable maketcl_run_timeout_ms 60000
variable runstate
array set runstate {}
proc maketcl_run_read {chan} {
variable runstate
append runstate(output) [read $chan]
if {[chan eof $chan]} {
chan event $chan readable {}
set runstate(done) eof
}
}
#Run <punkexe> script src/make.tcl <subcommand...> with output captured through a
#pipe (stdin half-closed for immediate EOF - make.tcl must never wait on stdin for
#these subcommands). Returns dict: timedout 0|1, exitcode <int|"">, output
#<combined stdout+stderr>.
proc maketcl_run {cmdargs} {
variable runstate
variable maketcl_run_timeout_ms
variable punkexe
variable maketcl
array unset runstate
set runstate(output) ""
set runstate(done) ""
set chan [open |[list $punkexe script $maketcl {*}$cmdargs 2>@1] r+]
chan configure $chan -blocking 0 -translation binary
catch {chan close $chan write} ;#no stdin for the child - immediate EOF
set timerid [after $maketcl_run_timeout_ms [list set [namespace current]::runstate(done) timeout]]
chan event $chan readable [list [namespace current]::maketcl_run_read $chan]
while {$runstate(done) eq ""} {
vwait [namespace current]::runstate(done)
}
after cancel $timerid
set timedout [expr {$runstate(done) eq "timeout"}]
set exitcode ""
if {$timedout} {
catch {exec {*}[auto_execok taskkill] /F /T /PID [lindex [pid $chan] 0]}
catch {chan close $chan}
} else {
chan configure $chan -blocking 1
if {[catch {chan close $chan} errdata errdict]} {
set exitcode [lindex [dict get $errdict -errorcode] end]
} else {
set exitcode 0
}
}
return [dict create timedout $timedout exitcode $exitcode output $runstate(output)]
}
proc esc_count {text} {
return [regexp -all {\x1b} $text]
}
#added 2026-07-26 (agent, G-121)
test maketcl_bakelist_full_report {piped make.tcl bakelist: exit 0, ESC-free, header row + punk91 row with documented deployed vocabulary} -constraints {punkexeavailable} -body {
set r [maketcl_run {bakelist}]
set out [dict get $r output]
set result [list]
lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode]
lappend result esc [esc_count $out]
lappend result header [regexp {(?n)^kit\s+type\s+runtime\s+vfs\s+deployed\s*$} $out]
lappend result punk91row [regexp {(?n)^punk91\s+zip\s+tclsfe-x64\s+punk9wintk903\.vfs\s+(current|stale|absent|nobuild)\y} $out]
set result
} -result {timedout 0 exitcode 0 esc 0 header 1 punk91row 1}
#added 2026-07-26 (agent, G-121)
test maketcl_bakelist_filtered_detail {bakelist punk91 filters to the named entry and appends the per-kit detail block} -constraints {punkexeavailable} -body {
set r [maketcl_run {bakelist punk91}]
set out [dict get $r output]
set result [list]
lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode]
lappend result punk91row [regexp {(?n)^punk91\s+zip\s+} $out]
lappend result otherrows [regexp {(?n)^(punk905|punkbi|punksys|punk86)\s} $out]
lappend result detailblock [regexp {(?n)^detail: punk91\s*$} $out]
lappend result detailpaths [regexp {(?n)^\s+build product: src/_build/punk91} $out]
set result
} -result {timedout 0 exitcode 0 punk91row 1 otherrows 0 detailblock 1 detailpaths 1}
#added 2026-07-26 (agent, G-121)
test maketcl_bakelist_unknown_name {bakelist with an unknown kit name exits 1 naming it and listing the configured names} -constraints {punkexeavailable} -body {
set r [maketcl_run {bakelist nosuchkitxyz}]
set out [dict get $r output]
set result [list]
lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode]
lappend result named [regexp {bakelist: unknown kit name\(s\): nosuchkitxyz} $out]
lappend result confignames [regexp {configured kit outputs: .*punk91} $out]
set result
} -result {timedout 0 exitcode 1 named 1 confignames 1}
#added 2026-07-26 (agent, G-121)
test maketcl_bake_unknown_name_no_build {bake with an unknown kit name exits 1 before any build, listing the configured names} -constraints {punkexeavailable} -body {
set r [maketcl_run {bake nosuchkitxyz}]
set out [dict get $r output]
set result [list]
lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode]
lappend result nothingbuilt [regexp {make\.tcl bake: unknown kit name\(s\): nosuchkitxyz - nothing built} $out]
lappend result confignames [regexp {configured kit outputs: .*punk91} $out]
#the run must never reach the kit machinery (no vfs scan / kit processing output)
lappend result nomachinery [expr {![regexp {processing targetkit:} $out]}]
set result
} -result {timedout 0 exitcode 1 nothingbuilt 1 confignames 1 nomachinery 1}
cleanupTests
}
namespace delete ::testspace

4
src/tests/shell/testsuites/punkexe/maketclcolour.test

@ -120,7 +120,7 @@ namespace eval ::testspace {
set result [list]
lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode]
lappend result esc [esc_count [dict get $r output]]
lappend result policyline [regexp {colour policy \(G-113\): mode=piped-plain colour_disabled=1 ansistrip=1} [dict get $r output]]
lappend result policyline [regexp {colour policy \(G-113\): mode=piped-plain colour_disabled=1 ansistrip=stdout\+stderr} [dict get $r output]]
set result
} -result {timedout 0 exitcode 0 esc 0 policyline 1}
@ -141,7 +141,7 @@ namespace eval ::testspace {
set result [list]
lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode]
lappend result esc [esc_count [dict get $r output]]
lappend result policyline [regexp {colour policy \(G-113\): mode=nocolor colour_disabled=1 ansistrip=1} [dict get $r output]]
lappend result policyline [regexp {colour policy \(G-113\): mode=nocolor colour_disabled=1 ansistrip=stdout\+stderr} [dict get $r output]]
set result
} -result {timedout 0 exitcode 0 esc 0 policyline 1}

Loading…
Cancel
Save