Browse Source

G-087 stage 3: thin layouts + generation-time bootsupport injection (sources)

punk::cap::handlers::templates 0.3.0: module-pathtype layout refs resolve
against the providing module's own templates/project_layouts store (sibling of
its layout_refs folder) instead of the never-existing <modulebase>/src tree -
module-carried thin layouts become usable.

punk::mix::templates 0.2.0: ships the thin punk.project layout payload inside
the modpod (payload synced from src/project_layouts by make.tcl - see below);
shellproject_multivendor declaration fixed decktemplates -> src/decktemplates
(shell-project layouts/templates were invisible when cwd was outside any
project).

punk::mix::commandset::project 0.3.0: project.new injects bootsupport into the
generated project at generation time via new lib::bootsupport_inject - manifest
(include_modules.config) driven module resolution from the generating shell's
project bootsupport tree, or a standalone kit's tcl:™️:list paths (999999.*
dev versions never injected; latest-version non-glob semantics matching
make.tcl bootsupport; adjacent non-.tm support files such as struct sets_*.tcl
and textutil .tex data carried along; bootsupport lib* trees copied from a
shell project, notice emitted for manifest-less standalone kits); punkcheck-
recorded with plain-copy degradation. -layout now prefix-matches the live
layout set instead of the package-load-time snapshot.

punkcheck 0.4.0: start_installer_event no longer writes literal '$eventid'/
'$rel_source'/'$rel_target'/'$config' into event headers (braced-literal
expansion bug, latent since inception - collided as duplicate ids when install
and installtrack writers shared one db); sources with no common root vs the
punkcheck folder (e.g //zipfs:/ module-mounted layouts) are recorded as
independent absolute paths instead of erroring.

punk::mix::base 0.1.1 -> 0.1.2 (manually versioned rename): cksum_path no
longer hangs forever on vfs-mounted files - tcllib -file digest modes read via
fileevent+vwait and zipfs channels never deliver fileevents (empty
ZipChannelWatchChannel in core); non-native files are slurped and digested in
data mode (new cksum_data_command per algorithm + cksum_crc_data/
cksum_adler32_data helpers; exec-based sha3 errors cleanly for vfs paths).

make.tcl: custom/_project layout bootsupport-sync retired (project layouts
store no snapshots); sync_layouts retargeted to thin vendor layouts (make.tcl,
build.tcl + bootsupport manifest copies; punkcheck db at
src/project_layouts/.punkcheck) plus store->modpod punk.project payload sync
(excludes bin/sdx.kit and src/PROJECT_LAYOUTS_* marker; fossil settings kept
via core-exclude override); bootsupport copy onto a modpod the running
make.tcl has mounted records OK when target content is already identical
(FAILED-record churn fix); workflow text updated (DIAGRAM 1b) and help/argdoc
texts adjusted.

bootsupport include_modules.config: added previously-recordless-but-required
entries cmdline (fileutil dep), struct::set (make.tcl accelerators), textutil,
textutil::expander, textutil::patch - generated projects' make.tcl now boots
under a plain tclsh.

Docs: root/src/bootsupport/punk AGENTS.md updated for the retired sync and
build-managed modpod payload; goal detail G-087 stage-3 progress record;
CHANGELOG 0.16.0 + punkproject.toml bump.

Claude-Session: https://claude.ai/code/session_01WS3VakZo4sgbFzcQFW3twZ
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 week ago
parent
commit
411df7482b
  1. 2
      AGENTS.md
  2. 8
      CHANGELOG.md
  3. 111
      goals/G-087-thin-project-layouts.md
  4. 2
      punkproject.toml
  5. 4
      src/AGENTS.md
  6. 5
      src/bootsupport/AGENTS.md
  7. 5
      src/bootsupport/modules/include_modules.config
  8. 216
      src/make.tcl
  9. 1
      src/modules/punk/AGENTS.md
  10. 23
      src/modules/punk/cap/handlers/templates-999999.0a1.0.tm
  11. 3
      src/modules/punk/cap/handlers/templates-buildversion.txt
  12. 2
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates-999999.0a1.0.tm
  13. 32
      src/modules/punk/mix/base-0.1.2.tm
  14. 329
      src/modules/punk/mix/commandset/project-999999.0a1.0.tm
  15. 6
      src/modules/punk/mix/commandset/project-buildversion.txt
  16. 3
      src/modules/punk/mix/templates-buildversion.txt
  17. 19
      src/modules/punkcheck-999999.0a1.0.tm
  18. 4
      src/modules/punkcheck-buildversion.txt

2
AGENTS.md

@ -205,7 +205,7 @@ The project version is fully independent of module versions. A module bump (even
- `lib/` (root) — Build output target for `tclsh src/make.tcl libs`
- `modules_tcl8/`, `modules_tcl9/` — Tcl version-specific build output targets
- `lib_tcl8/`, `lib_tcl9/` — Tcl version-specific build output targets
- `src/project_layouts/`Project layout templates; partly updated by `make.tcl bootsupport` but full synchronisation requires manual work or future `make.tcl` improvements. Agents must not directly edit files under `src/project_layouts/` unless the user explicitly names a path there or specifically requests layout changes. When a `src/make.tcl` change affects layout make.tcl files, note it in the completion report for the user to handle — do not attempt manual synchronisation.
- `src/project_layouts/`Thin project layout payloads (structure, scripts, docs, manifests - no stored bootsupport/vfs module snapshots; G-087). The build's thin-layout sync step (`make.tcl modules`/`libs`/`packages`/`project`) keeps each layout's `src/make.tcl`, `src/build.tcl` and bootsupport `include_modules.config` copies current, and syncs the punk.project payload into the `punk::mix::templates` modpod source tree; `dev project.new` injects bootsupport modules/libs into generated projects from the generating shell at generation time. Agents must not directly edit files under `src/project_layouts/` unless the user explicitly names a path there or specifically requests layout changes; never hand-edit the synced copies (they are punkcheck-managed build outputs).
## Repo-wide Notes

8
CHANGELOG.md

@ -5,6 +5,14 @@ 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.16.0] - 2026-07-19
- G-087 stage 3 - thin project layouts with generation-time injection: `dev project.new` now injects bootsupport into the generated project from the generating shell's current trees (modules per the layout's `include_modules.config` manifests plus adjacent support files; bootsupport lib trees from a shell project; standalone kits resolve modules from their own module paths and emit a notice for the manifest-less lib set). The ~40MB of stored bootsupport/vfs module snapshots left `src/project_layouts` (fat `custom/_project` fragments and their `_overrides` refs retired; vendor layouts thinned to structure/scripts/docs/manifests).
- A bare punk kit outside any project can now list and generate the `punk.project` layout: `punk::mix::templates` 0.2.0 ships the thin payload inside the modpod (build-synced from the store), `punk::cap::handlers::templates` 0.3.0 resolves module-pathtype layout refs against the module's own store, and the `shellproject_multivendor` declaration was fixed to `src/decktemplates` (shell-project layouts/templates were invisible when cwd was outside any project). `project.new -layout` is now prefix-matched against the live layout set instead of a load-time snapshot.
- make.tcl: former custom/_project layout bootsupport-sync retired; new thin-layout sync keeps layout `make.tcl`/`build.tcl`/manifest copies and the modpod payload current (workflow DIAGRAM 1b); bootsupport copy onto a self-mounted modpod now converges when content is already identical.
- punkcheck 0.4.0: `punkcheck::install` event headers no longer record literal `$eventid`/`$rel_source`/`$rel_target`/`$config` strings (latent since inception; collided when mixing install + installtrack writers in one db), and sources sharing no common root with the punkcheck folder (e.g `//zipfs:/` module-mounted layouts) are recorded as independent absolute paths instead of erroring. punk::mix::base 0.1.2: `cksum_path` no longer hangs forever on vfs-mounted files (tcllib `-file` digest modes rely on fileevents, which zipfs channels never deliver - data-mode digests are used for non-native filesystems).
- bootsupport `include_modules.config` completed with previously-recordless-but-required entries: `cmdline`, `struct::set`, `textutil`, `textutil::expander`, `textutil::patch` (generated projects' make.tcl now boots under a plain tclsh).
## [0.15.1] - 2026-07-19
- punk::ns 0.9.1: `get_ns_dicts` per-namespace `packagetails`/`packageprefixes` fix - the two lists were initialized once before the matched-namespaces loop, so with multiple matched namespaces (path globs, trailing `**`) every dict after the first carried the union of all earlier namespaces' entries, mis-marking non-package children in `nslist`/`n//` underline styling and in the returned data; a child with several deeper packages below it was also recorded once per matching package (now once). Single-namespace queries (the nslist.test tier A pins) were unaffected; the multi-namespace reset is now pinned in nslist.test. Documentation-only PUNKARGS added for `get_ns_dicts` (documents the returned per-namespace dict keys and `-allbelow`/`-nspathcommands`) and `nslist_dict`.

111
goals/G-087-thin-project-layouts.md

@ -261,11 +261,116 @@ Notes from the work:
denied when the running make.tcl process itself has that modpod mounted
(self-lock); the stale copy must be deleted manually afterwards. Candidate
small fix: unmount or defer-prune modpods the process mounted.
- 2026-07-19 punkcheck lifecycle/locking assessment (three parallel record
lifecycle implementations with drift; unguarded read-modify-write windows)
spun out as G-094 (single OO lifecycle) and G-095 (concurrent-writer
safety); the MAX_PATH normalization candidate above is flagged in G-094's
notes as a natural rider on its load/save consolidation.
Remaining for acceptance (pivot stage - Approach stages 3-5):
### 2026-07-19 stage 3 complete: thin layouts + generation-time injection (increment 3)
Landed (punk::cap::handlers::templates 0.3.0, punk::mix::templates 0.2.0,
punk::mix::commandset::project 0.3.0, punkcheck 0.4.0, punk::mix::base 0.1.2
(manual rename), make.tcl reworked, punkshell 0.16.0):
- Store thinned: vendor/punk/project-0.1 lost its src/bootsupport (10.6MB/254
files) and src/vfs/project.vfs lib+modules snapshots (7.7MB/230 files);
vendor/punk/basic lost its 2023-era bootsupport snapshot; the fat
custom/_project fragments (punk.project-0.1, punk.shell-0.1, punk.basic -
pure bootsupport+make/build overlay fragments, never standalone-coherent)
were deleted with their two _overrides refs. Thin layouts carry structure,
scripts, docs, fossil/git settings and the three bootsupport
include_modules.config manifests. New README markers document injection.
Stale pre-modpod residue cleaned: unpacked punk/mix/templates trees (with an
old-grammar .ref) in src/bootsupport + _vfscommon.vfs + root modules, and a
superseded templates-0.1.0.tm in _vfscommon.vfs.
- Injection: project.new calls new lib::bootsupport_inject after the layout
copies - resolves each manifest entry (make.tcl bootsupport semantics: latest
non-glob version, glob keeps all; 999999.* never injected) from the shell
project's src/bootsupport tree, or a standalone kit's tcl::tm::list paths;
carries adjacent non-.tm support files (struct sets_*.tcl, textutil .tex);
tree-copies bootsupport lib* (fileutil etc.) from a shell project (kit mode:
notice - lib set has no manifest); punkcheck-recorded into the generated
project's src/bootsupport/.punkcheck with plain-copy degradation.
-layout now prefix-matches the live layout set (load-time LAYOUTNAMES
snapshot could reject layouts appearing after package load).
- Module-carried layouts: handler resolves module-pathtype refs against the
module's own templates/project_layouts (sibling of layout_refs); the
templates modpod ships the thin punk.project payload (synced, excludes
bin/sdx.kit per no-new-binaries and the src/PROJECT_LAYOUTS_* marker;
.fossil-custom/.fossil-settings included via core-exclude override);
shellproject_multivendor decl fixed decktemplates -> src/decktemplates.
- make.tcl: bootsupport-command layout loop retired; sync_layouts retargeted to
vendor layouts carrying src/make.tcl (scripts + manifests, punkcheck db at
src/project_layouts/.punkcheck) + store->modpod payload sync + sourcevanished
prune; workflow text updated (DIAGRAM 1b); bootsupport copy onto a
self-mounted modpod records OK when content already identical (FAILED-record
churn fix - the pod is mounted by the running make.tcl itself).
- Latent bugs found and fixed en route:
- punkcheck start_installer_event wrote LITERAL '$eventid'/'$rel_source'/
'$rel_target'/'$config' into every install event header (braced-literal
{*}{} misuse, since inception; invisible until installtrack + install
shared one db - duplicate '$eventid' keys crashed the constructor).
- punkcheck cross-root sources: get_relativecksum was called with the
punkcheck folder as base for //zipfs:/ sources -> 'don't share a common
root' error; now uses the helpers' independent-absolute (empty base) mode.
- punk::mix::base cksum_path hung FOREVER on vfs files: tcllib -file digest
modes read via fileevent+vwait, and zipfs channels never deliver
fileevents (core: ZipChannelWatchChannel is an empty stub, no OS handle
either; native Windows FileWatchProc fakes readiness via
Tcl_SetMaxBlockTime2(0)). Non-native files are now slurped and digested in
data mode (0.1.2). Upstream ticket candidate.
- bootsupport manifest was INCOMPLETE vs the working tree: cmdline (fileutil
dep), struct::set (make.tcl accelerators), textutil umbrella, expander,
patch existed only as recordless legacy files - invisible to injection.
Entries added; generated projects' make.tcl now boots under plain tclsh.
Verification (2026-07-19, native tclsh 9.0.3):
- runtests: full suite 974 pass / 15 skip / 1 fail = the documented exec-14.3
baseline; punkcheck install+installtrack 64/64; prune/fossilmove/multishell/
libsearch suites pass.
- In-checkout (punk902z src, piped, FOSSIL_HOME+HOME diverted): layouts list
punk.project (store) + punk.project#2 (modpod, now with a REAL resolvable
//zipfs:/ path) + basic/minimal/sample; no _overrides ghosts; generated
project's bootsupport marker punk::mix::cli-0.5.0 matches the checkout
src/bootsupport exactly; handler 0.3.0/templates 0.2.0/punkcheck 0.4.0
injected; no 999999 versions; fossil-settings present; plain-tclsh
'src/make.tcl info' in the generated project succeeds.
- Bare kit (fresh punk902z.exe copied outside the repo, cwd outside any
project): lists exactly punk.project resolving into the mounted modpod
payload; generates a full project through zipfs-source punkcheck::install
(102-107 modules injected from kit module paths, current versions, adjacent
files included); kit-hosted '<kit> src/make.tcl info' in the generated
project succeeds (plain-tclsh path needs the lib-set notice honoured).
- make.tcl bootsupport converges to 0 copies on the second pass again.
Notes / residue from stage 3:
- Generated-project trailing fossil commit still aborts on binary data
(bin/sdx.kit from checkout-generation; injected zip-based modpod .tm files
in all cases) - G-012 territory: thin layouts ship no binary-glob.
- Standalone-kit generation cannot inject bootsupport lib packages (fileutil
etc.) - no manifest exists for the lib set (bootsupport libs are governed by
tclpackages.toml at the checkout level only). Candidate: an include_libs
manifest, or fold into G-027 pull. Kit-hosted make.tcl runs are unaffected.
- The repodb-folder prompt in punk::repo::fossil_get_repository_folder_for_project
ignores -confirm 0 when more than one .fossils candidate exists (or any
conflict) - unattended kit-mode generation needs a single unambiguous
candidate (e.g FOSSIL_HOME with a .fossils subdir). Candidate improvement:
honour a non-interactive flag by auto-selecting the first non-conflicted
choice.
- modpod packing is byte-unstable across packs (zip timestamps), so a repack
without payload change churns downstream punkcheck copies once; and a
make.tcl process cannot overwrite a modpod it has mounted (the convergence
fix records OK on identical content; a real content change to a same-named
bootsupport pod requires a second run or manual copy while no make.tcl is
running). Candidate: deterministic pack timestamps + unmount-before-copy.
- sdx.kit remains in the store layout (grandfathered committed binary,
excluded from the modpod) - G-004/G-006 decide its future.
Remaining for acceptance (Approach stages 4-5):
- thin layouts + generation-time bootsupport injection, retire make.tcl layout
bootsupport-sync, module-shipped thin punk.project layout for bare kits (stage 3).
- staging-directory overlay merge primitive exercising the sample-0.1/othersample
fixtures (stage 4).
- child-side PROJECT_LAYOUTS_CREATE_FROM_... marker retirement and the

2
punkproject.toml

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

4
src/AGENTS.md

@ -69,14 +69,14 @@ Recovery after a wrong path guess:
- Use `tclsh src/make.tcl vfscommonupdate` to rebuild `_vfscommon.vfs`. The REPLACE confirmation prompts on an interactive terminal; unattended/agent runs must pass `-confirm 0` (with the default `-confirm 1` and a piped/closed stdin, make.tcl aborts fast with guidance instead of reading stdin - do not pipe `y`).
- `tclsh src/make.tcl workflow` prints an embedded ASCII data-flow overview of the build/release workflow (release-ready command sequence, source→outputs folder flow, kit assembly, keyed notes). The text lives in `::punkboot::workflow_text` in `src/make.tcl` - embedded rather than a data file so it travels with the make.tcl copies seeded into generated projects. **Update contract (agents):** whenever build data flow changes - a make.tcl subcommand added/removed/repurposed, a source or output folder added or rerouted, a propagation target added, a gate (staleness/provenance/confirm) or deploy behaviour changed - update the workflow text in the same change-set. Keep it plain ASCII, max line width 100, and preserve the RELEASE SEQUENCE / DIAGRAM / KEY-NOTES / MAINTENANCE structure; verify by running `tclsh src/make.tcl workflow` (and once under `PUNKBOOT_PLAIN=1`). Adding/changing a subcommand also means: SUMMARIES + HELPTEXTS + SUBOPTS (+ SUBGROUPS) in `::punkboot::argdoc`, `known_commands`, the plain `punkboot_gethelp` text, and a project-version bump (make.tcl interface is product surface per root AGENTS.md). Layout make.tcl copies pick the change up via `make.tcl bootsupport` - do not hand-sync them.
- 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`, and declared per-kit `*.vfs/lib_tcl<N>/<pkg>` subfolders. (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.
- 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.
- Use `punk make.tcl project` or `punk902z make.tcl project` inside Punk shell when building binaries through Punk. 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.
- Remove `_build/` artifacts only when a clean/resync is needed, then rerun the relevant `make.tcl` command. Avoid partial cleans that break boot modules.
- Superseded module intermediates are pruned automatically so payload wrapping inherits clean trees:
- `punk::mix::cli::lib::prune_superseded_target_modules` runs inside `build_modules_from_source_to_base` after each module install/skip (covers root `modules/`, `modules_tcl8/`, `modules_tcl9/`), and from `make.tcl bootsupport` for non-glob `include_modules.config` entries (glob entries may intentionally track multiple versions and are never pruned).
- `punk::mix::cli::lib::prune_sourcevanished_targets` mirror-prunes recorded `.tm` targets whose recorded source files no longer exist: project-layout bootsupport copies (installer `punkboot-bootsupport`) and root `modules*/` vendormodule copies (installer `make.tcl`).
- `punk::mix::cli::lib::prune_sourcevanished_targets` mirror-prunes recorded targets whose recorded source files no longer exist: root `modules*/` vendormodule copies (installer `make.tcl`) and the thin-layout/modpod sync copies recorded in `src/project_layouts/.punkcheck` (installer `make.tcl`, all file types).
- Every deletion is recorded as a punkcheck DELETE event in the owning `.punkcheck`, so change detection stays consistent. Files without a qualifying punkcheck install record (e.g placed manually) are never deleted — they are reported to stderr and must be removed by hand if unwanted.
- Install records carry virtual `module_name`/`module_version` SOURCE entries (punkcheck `targetset_addsource_virtual`, punkcheck >= 0.3.0) identifying which product of a source fileset a target is — needed because successive versions are typically built from the same physical source files. Prune trusts this identity; for legacy records it falls back to requiring the candidate's recorded sources to share the keep-version's source folder(s) and module-name prefix.
- The make.tcl call sites are guarded with `info commands` checks: after changing this tooling itself, the first `make.tcl modules` + `make.tcl bootsupport` pass builds/propagates the new tooling (prune skipped with a stderr note) and the next pass prunes.

5
src/bootsupport/AGENTS.md

@ -8,8 +8,9 @@ Modules and libraries required during the build/bootstrap/make process before th
- Agents should not directly modify files in this tree unless the task specifically targets boot behaviour.
- Bootsupport modules are snapshots that may lag behind or diverge from the corresponding `src/modules/` versions intentionally.
- The systematic update workflow is `modules/include_modules.config` (entries are `<base> <module>` pairs; base `src/vendormodules` for vendored modules, base `modules` for the project's *built* root modules - build with `make.tcl modules` first) followed by `tclsh src/make.tcl bootsupport`, which also propagates snapshots into the project-layout bootsupport trees.
- For non-glob config entries only the latest version is tracked: `make.tcl bootsupport` prunes punkcheck-recorded superseded older `.tm` versions from the bootsupport folders (recorded as DELETE events in `src/bootsupport/.punkcheck`), and mirror-prunes project-layout copies whose recorded sources vanished. Use a glob entry to intentionally keep multiple versions. Files without punkcheck install records are never pruned automatically.
- The systematic update workflow is `modules/include_modules.config` (entries are `<base> <module>` pairs; base `src/vendormodules` for vendored modules, base `modules` for the project's *built* root modules - build with `make.tcl modules` first) followed by `tclsh src/make.tcl bootsupport`. Project layouts store no bootsupport snapshots (G-087): `dev project.new` injects bootsupport into generated projects from the generating shell at generation time, and the layouts carry only manifest copies of `include_modules.config` (synced by the thin-layout sync step of `make.tcl modules`/`libs`/`packages`/`project` runs - a manifest edit reaches layouts and the templates modpod on the next such build).
- For non-glob config entries only the latest version is tracked: `make.tcl bootsupport` prunes punkcheck-recorded superseded older `.tm` versions from the bootsupport folders (recorded as DELETE events in `src/bootsupport/.punkcheck`). Use a glob entry to intentionally keep multiple versions. Files without punkcheck install records are never pruned automatically.
- The manifest is expected to be complete: a module physically present in bootsupport but missing from `include_modules.config` (a recordless legacy file) works for this checkout but is invisible to generation-time injection - generated projects then lack it. cmdline and struct::set were re-added to the manifest this way (2026-07-19). `make.tcl bootsupport` itself copies only manifest-listed `.tm` files; injection additionally carries adjacent non-.tm support files (e.g struct's `sets_*.tcl`, textutil's `.tex` data) that some modules load from their own directory.
- `dev lib.copyasmodule` is a manual convenience for copying a module (or a single-file pkgIndex.tcl library, converting it to .tm form) from a running punk shell - see `modules/README.md`; it is not the primary update mechanism.
## Local Contracts

5
src/bootsupport/modules/include_modules.config

@ -20,6 +20,7 @@
set bootsupport_modules [list\
src/vendormodules commandstack\
src/vendormodules cksum\
src/vendormodules cmdline\
src/vendormodules debug\
src/vendormodules dictutils\
src/vendormodules fauxlink\
@ -36,11 +37,15 @@ set bootsupport_modules [list\
src/vendormodules platform\
src/vendormodules promise\
src/vendormodules sha1\
src/vendormodules struct::set\
src/vendormodules tcltest\
src/vendormodules tomlish\
src/vendormodules test::tomlish\
src/vendormodules dictn\
src/vendormodules textutil\
src/vendormodules textutil::adjust\
src/vendormodules textutil::expander\
src/vendormodules textutil::patch\
src/vendormodules textutil::repeat\
src/vendormodules textutil::split\
src/vendormodules textutil::string\

216
src/make.tcl

@ -1372,8 +1372,9 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " - build (or copy if build not required) both .tm and pkgIndex.tcl based packages from src to their corresponding locations under <projectdir>" \n
append h " This does not scan src/runtime and src/vfs folders to build kit/zipkit/cookfs executables" \n \n
append h " $scriptname bootsupport" \n
append h " - update the src/bootsupport modules as well as the src/project_layouts/custom/_project/<layoutname>/src/bootsupport modules if the folder exists" \n
append h " - update the src/bootsupport modules" \n
append h " - bootsupport modules are pulled from locations specified in include_modules.config files within each src/bootsupport subdirectory" \n
append h " - project layouts store no bootsupport module snapshots: generated projects get bootsupport injected at generation time by 'dev project.new'" \n
append h " - This should usually be from modules that have been built and tested in <projectdir>/modules <projectdir>/lib etc." \n
append h " - bootsupport modules are available to make.tcl" \n \n
append h " $scriptname vendorupdate" \n
@ -1442,8 +1443,10 @@ RELEASE SEQUENCE (the order that satisfies the staleness + provenance gates)
(6) build tclsh src/make.tcl bootsupport -confirm 0 run twice: 2nd pass must copy 0 files [K4]
(7) build tclsh src/make.tcl vfscommonupdate -confirm 0
(8) build tclsh src/make.tcl project -confirm 0 kits; close running kit shells first [K5]
(9) commit tracked build outputs src/bootsupport, src/project_layouts
copies, src/vfs/_vfscommon.vfs
(9) commit tracked build outputs src/bootsupport, thin-layout script/
manifest copies (src/project_layouts +
templates modpod payload),
src/vfs/_vfscommon.vfs
DIAGRAM 1 - DATA FLOW: SOURCE -> BUILT PACKAGES -> PROPAGATION TARGETS
@ -1468,14 +1471,28 @@ DIAGRAM 1 - DATA FLOW: SOURCE -> BUILT PACKAGES -> PROPAGATION TARGETS
v | are overwritten with built set)
src/bootsupport/modules/ v
(the module set make.tcl itself src/vfs/_vfscommon.vfs/modules/ + libs
boots from; staleness-gated) (VCS-tracked kit payload source)
| |
| same run, second hop | make.tcl project (or: make.tcl vfs)
v v
src/project_layouts/custom/_project/ src/_build/<kit>.exe --deploy--> bin/<kit>.exe
<layoutname>/src/bootsupport/ (see DIAGRAM 2) [K5]
(template copies seeded into projects
generated by 'dev project.new')
boots from; staleness-gated. (VCS-tracked kit payload source)
Project layouts store NO module |
snapshots - 'dev project.new' | make.tcl project (or: make.tcl vfs)
injects bootsupport into generated v
projects from the generating src/_build/<kit>.exe --deploy--> bin/<kit>.exe
shell at generation time - G-087) (see DIAGRAM 2) [K5]
DIAGRAM 1b - THIN-LAYOUT SYNC (part of modules/libs/packages/project runs)
----------------------------------------------------------------------------
src/make.tcl src/build.tcl src/bootsupport/modules*/include_modules.config
|
| punkcheck-recorded copies into each vendor layout carrying src/make.tcl
v
src/project_layouts/vendor/punk/<layout>/src/... (thin layouts: scripts, docs,
| manifests - no module snapshots)
| store -> modpod payload sync (punk.project layout only; excludes bin/sdx.kit)
v
src/modules/punk/mix/#modpod-templates-*/templates/project_layouts/
(packed into the punk::mix::templates module by the modules build, so a bare
kit outside any project can list and generate the punk.project layout)
DIAGRAM 2 - KIT ASSEMBLY DETAIL (inside 'make.tcl project' / 'make.tcl vfs')
@ -1526,7 +1543,7 @@ KEY / NOTES
src/bootsupport against built sources; a stale bootsupport makes other
subcommands warn/prompt/abort (per bump level) unless -confirm 0. After a
tracked module bump, refresh with 'make.tcl bootsupport' and rerun it until
a pass copies 0 files (layout copies have needed a second pass historically).
a pass copies 0 files (a converged second pass also proves prune tooling ran).
[K5] The deploy step cannot replace a kit exe that is currently executing. Close
running kit shells before step 8, or rerun 'make.tcl vfs -confirm 0'
@ -1651,7 +1668,7 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
vfslibs "Propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees"
bin "Install executables from src/bin into <projectdir>/bin, then build kits as for vfs"
vendorupdate "Update src/vendormodules based on src/vendormodules/include_modules.config"
bootsupport "Update src/bootsupport modules (and project-layout copies) from built project modules"
bootsupport "Update src/bootsupport modules from built project modules"
vfscommonupdate "Replace src/vfs/_vfscommon.vfs modules and libs with the project's built modules and libs"
info "Show the name and base folder of the project to be built"
check "Show module/library paths and any potentially problematic packages for running this script"
@ -1717,13 +1734,15 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
Update the src/vendorlib based on src/vendorlib/config.toml (todo)."
bootsupport
"
Update the src/bootsupport modules as well as the project-layout
src/bootsupport modules if those folders exist.
Update the src/bootsupport modules.
Bootsupport modules are pulled from locations specified in
include_modules.config files within each src/bootsupport subdirectory.
This should usually be from modules that have been built and tested in
<projectdir>/modules, <projectdir>/lib etc.
Bootsupport modules are available to make.tcl."
Bootsupport modules are available to make.tcl.
Project layouts store no bootsupport module snapshots - generated
projects get bootsupport injected at generation time by 'dev
project.new' (G-087)."
vfscommonupdate
"
Update the src/vfs/_vfscommon.vfs from compiled src/modules and src/lib etc.
@ -2911,7 +2930,26 @@ if {$::punkboot::command eq "bootsupport"} {
if {[catch {
file copy -force $srcfile $tgtfile
} errM]} {
$boot_event targetset_end FAILED
#a copy onto a modpod this make.tcl process has mounted (e.g punk::mix::templates)
#fails on windows even when the target content is already current (a prior failed
#record forces a retry). If content is already identical, record OK so the record
#converges instead of churning FAILED forever.
set already_current 0
if {[file exists $tgtfile] && [file size $tgtfile] == [file size $srcfile]} {
catch {
set src_ck [dict get [punk::mix::base::lib::cksum_path $srcfile] cksum]
set tgt_ck [dict get [punk::mix::base::lib::cksum_path $tgtfile] cksum]
if {$src_ck ne "" && $src_ck eq $tgt_ck} {
set already_current 1
}
}
}
if {$already_current} {
$boot_event targetset_end OK -note "copy failed ($errM) but target content already identical (self-mounted modpod?)"
} else {
puts stderr "BOOTSUPPORT module$which update FAILED: $tgtfile ($errM)"
$boot_event targetset_end FAILED
}
} else {
$boot_event targetset_end OK
}
@ -2962,81 +3000,12 @@ if {$::punkboot::command eq "bootsupport"} {
bootsupport_localupdate $projectroot
#if this project has custom project layouts, and there is a bootsupport folder - update their bootsupport
set layout_bases [list {*}{
} $sourcefolder/project_layouts/custom/_project {*}{
}
]
foreach project_layout_base $layout_bases {
if {[file exists $project_layout_base]} {
set project_layouts [glob -nocomplain -dir $project_layout_base -type d -tail *]
foreach layoutname $project_layouts {
puts stdout "Processing layout $project_layout_base/$layoutname"
#don't auto-create src/bootsupport - just update it if it exists
if {[file exists [file join $project_layout_base $layoutname/src/bootsupport]]} {
set antipaths [list {*}{
README.md
AGENTS.md
**/AGENTS.md
}]
#set boot_module_folders [list modules {*}[glob -nocomplain -dir $projectroot/src/bootsupport/modules_tcl*]]
set bootsupport_module_folders "modules"
foreach bm $bootsupport_module_folders {
if {[file exists $projectroot/src/bootsupport/$bm]} {
lassign [split $bm _] _bm tclx
if {$tclx ne ""} {
set which _$tclx
} else {
set which ""
}
set sourcemodules $projectroot/src/bootsupport/modules$which
set targetroot [file join $project_layout_base $layoutname/src/bootsupport/modules$which]
file mkdir $targetroot
puts stdout "BOOTSUPPORT$which layouts/$layoutname: copying from $sourcemodules to $targetroot (if source file changed)"
#G-087 stage 3: the former custom/_project layout bootsupport sync is retired.
#Project layouts store no bootsupport module snapshots - 'dev project.new' injects bootsupport
#into generated projects from the generating shell at generation time. Layout-carried scripts
#and bootsupport manifests are kept current by the thin-layout sync step (see DIAGRAM 1b in
#::punkboot::workflow_text), which runs during modules/libs/packages/project builds.
set resultdict [punkcheck::install $sourcemodules $targetroot\
-overwrite installedsourcechanged-targets\
-exclude-paths $antipaths\
-installer "punkboot-bootsupport"\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict]
flush stdout
if {![catch {package require punk::mix::cli}]
&& [llength [info commands ::punk::mix::cli::lib::prune_sourcevanished_targets]]
} {
#remove layout copies whose recorded bootsupport source files no longer exist (e.g pruned as superseded)
::punk::mix::cli::lib::prune_sourcevanished_targets $targetroot punkboot-bootsupport -glob *.tm
} else {
puts stderr "bootsupport: prune helper punk::mix::cli::lib::prune_sourcevanished_targets unavailable (older punk::mix::cli snapshot) - skipping sourcevanished prune for $targetroot"
}
}
}
}
#make.tcl (to be boot.tcl?) is part of bootsupport
set source_bootscript [file join $projectroot src/make.tcl]
set targetroot_bootscript $project_layout_base/$layoutname/src
if {[file exists $source_bootscript]} {
puts stdout "BOOTSUPPORT layouts/$layoutname: copying from $source_bootscript to $targetroot_bootscript (if source file changed)"
set resultdict [punkcheck::install [file dirname $source_bootscript] $targetroot_bootscript\
-glob make.tcl\
-max-depth 1\
-createempty 0\
-overwrite installedsourcechanged-targets\
-installer "punkboot-bootsupport"\
-progresschannel stdout\
]
puts stdout [punkcheck::summarize_install_resultdict $resultdict]
flush stdout
}
}
} else {
puts stderr "No layout base at $project_layout_base"
}
}
puts stdout " bootsupport done "
flush stderr
flush stdout
@ -3335,10 +3304,14 @@ if {$::punkboot::command in {project packages libs}} {
if {$::punkboot::command in {project packages modules libs}} {
########################################################
#templates
#sync scripts developed under the main src (build.tcl, make.tcl) into the project_layouts custom/_project layout copies
#templates - thin-layout sync (G-087 stage 3; see DIAGRAM 1b in ::punkboot::workflow_text)
#sync boot scripts (build.tcl, make.tcl) and bootsupport include_modules.config manifests into the
#thin vendor layouts (those that carry src/make.tcl), then sync the punk.project layout payload from
#the store into the punk::mix::templates modpod source tree so the built module ships it.
#Layouts store no bootsupport/vfs module snapshots - 'dev project.new' injects bootsupport into
#generated projects from the generating shell at generation time.
set layout_bases [list {*}{
} $sourcefolder/project_layouts/custom/_project {*}{
} $sourcefolder/project_layouts/vendor/punk {*}{
}
]
@ -3348,20 +3321,30 @@ if {$::punkboot::command in {project packages modules libs}} {
}
set project_layouts [glob -nocomplain -dir $layoutbase -type d -tail *]
foreach layoutname $project_layouts {
if {![file exists $layoutbase/$layoutname/src/make.tcl]} {
#layout doesn't carry boot scripts (e.g minimal, sample-0.1) - nothing to sync
continue
}
set config [dict create {*}{
-make-step sync_layouts
}]
#----------
set tpl_installer [punkcheck::installtrack new make.tcl $layoutbase/.punkcheck]
$tpl_installer set_source_target $sourcefolder $layoutbase
set tpl_installer [punkcheck::installtrack new make.tcl $sourcefolder/project_layouts/.punkcheck]
$tpl_installer set_source_target $sourcefolder $layoutbase/$layoutname
set tpl_event [$tpl_installer start_event $config]
#----------
set pairs [list]
set pairs [list {*}{
} [list $sourcefolder/build.tcl $layoutbase/$layoutname/src/build.tcl] {*}{
} [list $sourcefolder/make.tcl $layoutbase/$layoutname/src/make.tcl] {*}{
}
]
#bootsupport manifests: sync only into bootsupport modules folders the layout already carries
foreach bm [glob -nocomplain -dir $layoutbase/$layoutname/src/bootsupport -type d -tails modules modules_tcl*] {
set src_manifest $sourcefolder/bootsupport/$bm/include_modules.config
if {[file exists $src_manifest]} {
lappend pairs [list $src_manifest $layoutbase/$layoutname/src/bootsupport/$bm/include_modules.config]
}
}
foreach filepair $pairs {
lassign $filepair srcfile tgtfile
@ -3371,10 +3354,9 @@ if {$::punkboot::command in {project packages modules libs}} {
$tpl_event targetset_init INSTALL $tgtfile
$tpl_event targetset_addsource $srcfile
#----------
if {\
[llength [dict get [$tpl_event targetset_source_changes] changed]]\
|| [llength [$tpl_event get_targets_exist]] < [llength [$tpl_event get_targets]]\
} {
if { [llength [dict get [$tpl_event targetset_source_changes] changed]]
|| [llength [$tpl_event get_targets_exist]] < [llength [$tpl_event get_targets]]
} {
$tpl_event targetset_started
# -- --- --- --- --- ---
puts stdout "PROJECT LAYOUT update - layoutname: $layoutname Copying from $srcfile to $tgtfile"
@ -3397,8 +3379,38 @@ if {$::punkboot::command in {project packages modules libs}} {
$tpl_installer destroy
}
}
}
#store -> modpod payload sync: the punk::mix::templates module ships the thin punk.project layout
#so module-pathtype layout refs resolve and a bare kit outside any project can generate from it.
#The modpod copy is build-managed - do not hand-edit it. Excludes: bin/sdx.kit (no new binaries
#inside zip-based .tm modules) and the unimplemented child-side src/PROJECT_LAYOUTS_* marker.
set store_layout $sourcefolder/project_layouts/vendor/punk/project-0.1
set modpod_layout "$sourcefolder/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1"
if {[file isdirectory $store_layout]} {
file mkdir $modpod_layout
puts stdout "LAYOUT->MODPOD: syncing thin punk.project layout payload into punk::mix::templates modpod source (if source file changed)"
#override the default core excludes: .fossil-custom/.fossil-settings are layout content the
#generated project needs (project.new installs them into projects with the same override)
set resultdict [punkcheck::install $store_layout $modpod_layout {*}{
-punkcheck-folder
} $sourcefolder/project_layouts {*}{
-installer make.tcl
-overwrite installedsourcechanged-targets
-createempty 1
-exclude-paths-core {#* **/#* _aside **/_aside _build **/_build .git **/.git}
-exclude-paths {bin/sdx.kit src/PROJECT_LAYOUTS_*}
}]
puts stdout [punkcheck::summarize_install_resultdict $resultdict]
if {![catch {package require punk::mix::cli}]
&& [llength [info commands ::punk::mix::cli::lib::prune_sourcevanished_targets]]
} {
#remove synced layout/modpod copies whose recorded source files no longer exist
::punk::mix::cli::lib::prune_sourcevanished_targets $sourcefolder/project_layouts make.tcl -glob *
} else {
puts stderr "sync_layouts: prune helper punk::mix::cli::lib::prune_sourcevanished_targets unavailable (older punk::mix::cli snapshot) - skipping sourcevanished prune for $modpod_layout"
}
}
}
if {$::punkboot::command in {project packages modules}} {
#consolidated /modules /modules_tclX folder used for target where X is tcl major version

1
src/modules/punk/AGENTS.md

@ -39,6 +39,7 @@ Source of truth for all modules under the `punk::*` namespace. This is the prima
- Private helpers go in `namespace eval private { ... }` blocks.
- Keep `namespace export` lists alphabetized.
- The `#modpod-*` folders contain files that get packed into `.tm` module archives during build — do not flatten them.
- `mix/#modpod-templates-*/templates/project_layouts/` is a build-managed copy of the thin punk.project layout, synced from `src/project_layouts/vendor/punk/project-0.1` by make.tcl's thin-layout sync step (G-087) - edit the store layout, never this copy. It ships inside the built `punk::mix::templates` module so a bare kit can list and generate the punk.project layout (resolution via punk::cap::handlers::templates >= 0.3.0).
- Follow the parent `src/modules/AGENTS.md` "Versioning And Releases" rules: bump `<modulename>-buildversion.txt` on any API or behavior change and append a changelog comment line documenting the change.
## Verification

23
src/modules/punk/cap/handlers/templates-999999.0a1.0.tm

@ -518,15 +518,26 @@ namespace eval punk::cap::handlers::templates {
#an @ within a target segment is literal fauxlink content (derived-layout folder names such as othersample@sample-0.1)
#unresolvable refs were already skipped (with a warning) by get_itemdict_projectlayoutrefs
set targetpath [dict get [fauxlink::resolve $reftail] targetpath]
if {[dict exists $refinfo sourceinfo base]} {
#some template pathtypes refer to the projectroot from the template - not the cwd
set ref_projectroot [dict get $refinfo sourceinfo base]
if {$templatepathtype eq "module"} {
#module-carried layout store (G-087): the providing module ships thin layout payloads
#in a project_layouts folder within its own templates tree - sibling of the layout_refs
#folder this ref was found in. A module cannot contain a src/project_layouts projectroot path.
set layoutroot [file join [file dirname [dict get $refinfo basefolder]] project_layouts]
} else {
set ref_projectroot $projectroot
if {[dict exists $refinfo sourceinfo base]} {
#some template pathtypes refer to the projectroot from the template - not the cwd
set ref_projectroot [dict get $refinfo sourceinfo base]
} else {
set ref_projectroot $projectroot
}
if {$ref_projectroot ne ""} {
set layoutroot [file join $ref_projectroot src/project_layouts]
} else {
set layoutroot ""
}
}
if {$ref_projectroot ne ""} {
set layoutroot [file join $ref_projectroot src/project_layouts]
if {$layoutroot ne ""} {
set layoutfolder [file join $layoutroot $targetpath]
if {[file isdirectory $layoutfolder]} {
#todo - check if layoutname already in layoutdict append .ref path to list of refs that linked to this layout?

3
src/modules/punk/cap/handlers/templates-buildversion.txt

@ -1,6 +1,7 @@
0.2.0
0.3.0
#First line must be a semantic version number
#all other lines are ignored.
#0.3.0 - G-087 stage 3: module-pathtype layout refs now resolve - the layout root for a module-provided ref is the project_layouts folder within the providing module's own templates tree (sibling of its layout_refs folder), instead of the never-existing <modulebase>/src/project_layouts. Enables module-carried thin layout payloads (bare kit can list and generate the module-shipped punk.project layout).
#0.2.0 - G-087 stage 2: layout refs are now fauxlink files (<alias>#<encodedtarget>.fauxlink|.fxlnk, resolved via the fauxlink module; target relative to <projectroot>/src/project_layouts). Bespoke <alias>@<target>.ref grammar removed - .ref files are no longer recognised. Alias-less (leading-#) refs and unresolvable refs are skipped with a warning (punkcheck excludes leading-# filenames). New dependency: fauxlink. Also fixes the ignore* skip (previously matched against the full glob path, so it never applied).
#0.1.1 - fix: pkg_register 'absolute' pathtype branch had a non-braced expr 'if {!file exists $normpath}' which raised an expr syntax error on any absolute-pathtype provider registration; now correctly tests ![file exists $normpath]
#0.1.1 - fix: pkg_register duplicate-registration guard did 'dict get $provider_info $pkg' without a dict exists check, erroring for any second provider package registering the same capability

2
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates-999999.0a1.0.tm

@ -34,7 +34,7 @@ namespace eval punk::mix::templates {
#only punk::templates is allowed to register a _multivendor path - review
#other punk.template providers should use module, absolute, currentproject and shellproject pathtypes only
lappend decls [list punk.templates {path src/decktemplates pathtype currentproject_multivendor vendor punk}]
lappend decls [list punk.templates {path decktemplates pathtype shellproject_multivendor vendor punk}]
lappend decls [list punk.templates {path src/decktemplates pathtype shellproject_multivendor vendor punk}]
#we need a way to ensure we don't pull updates from a remote repo into a local project that is actually the same project ? review!

32
src/modules/punk/mix/base-0.1.1.tm → src/modules/punk/mix/base-0.1.2.tm

@ -1,8 +1,9 @@
package provide punk::mix::base [namespace eval punk::mix::base {
variable version
set version 0.1.1
set version 0.1.2
}]
#version history (manually versioned module - real version is the filename/provide value)
#0.1.2 - fix: cksum_path hung forever on files on non-native (vfs-mounted e.g //zipfs:/) filesystems - the tcllib -file digest modes read via fileevent+vwait and vfs channels never deliver fileevents. Files on non-native filesystems are now slurped and digested in data mode (new cksum_data_command per algorithm incl new cksum_adler32_data/cksum_crc_data helpers; exec-based sha3 returns an unsupported_algorithm_for_vfs_path error for vfs paths). Needed for punkcheck::install from module-carried //zipfs:/ layout payloads (G-087 stage 3).
#0.1.1 - fix: get_template_basefolders no-handler warning used 'put' instead of 'puts'; missing-handler path now warns and returns an empty dict instead of erroring on an unset variable
package require punk::path
@ -503,6 +504,12 @@ namespace eval punk::mix::base {
set data [punk::mix::util::fcat -translation binary -encoding iso8859-1 $filename]
cksum_md5_data $data
}
proc cksum_adler32_data {data} {
zlib adler32 $data
}
proc cksum_crc_data {data} {
zlib crc32 $data
}
#required to be able to accept relative paths
@ -636,15 +643,23 @@ namespace eval punk::mix::base {
puts stderr "cksum_path doesn't yet support cksum of entire volume. (todo)"
return [list error unsupported_path opts $opts]
}
#cksum_data_command is the string/data-mode equivalent of cksum_command, used for files on
#non-native (vfs-mounted e.g //zipfs:/) filesystems: the tcllib -file digest modes read via
#fileevent + vwait, and vfs channels don't deliver fileevents - the vwait never returns.
#Empty means no data-mode equivalent is available (e.g exec-based sha3 - the external process
#can't see this process's vfs mounts either).
set cksum_data_command ""
switch -- $opt_cksum_algorithm {
sha1 {
package require sha1
#review - any utf8 issues in tcl9?
set cksum_command [list sha1::sha1 -hex -file]
set cksum_data_command [list sha1::sha1 -hex --]
}
sha2 - sha256 {
package require sha256
set cksum_command [list sha2::sha256 -hex -file]
set cksum_data_command [list sha2::sha256 -hex --]
}
md5 {
package require md5
@ -653,16 +668,20 @@ namespace eval punk::mix::base {
} else {
set cksum_comand [list cksum_md5_file]
}
set cksum_data_command [list cksum_md5_data]
}
cksum {
package require cksum ;#tcllib
set cksum_command [list crc::cksum -format 0x%X -file]
set cksum_data_command [list crc::cksum -format 0x%X --]
}
crc {
set cksum_command [list cksum_crc_file]
set cksum_data_command [list cksum_crc_data]
}
adler32 {
set cksum_command [list cksum_adler32_file]
set cksum_data_command [list cksum_adler32_data]
}
sha3 - sha3-256 {
#todo - replace with something that doesn't call another process - only if tcllibc not available!
@ -752,7 +771,16 @@ namespace eval punk::mix::base {
if {$opt_cksum_meta} {
return [list error unsupported_opts_combo cksum "<ERR>" opts $opts]
} else {
set cksum [{*}$cksum_command $path]
if {[lindex [file system $path] 0] eq "native"} {
set cksum [{*}$cksum_command $path]
} else {
#vfs-mounted path (e.g //zipfs:/ module-carried content) - see cksum_data_command comment
if {![llength $cksum_data_command]} {
return [list error unsupported_algorithm_for_vfs_path cksum "<ERR>" opts $opts]
}
set data [punk::mix::util::fcat -translation binary -encoding iso8859-1 $path]
set cksum [{*}$cksum_data_command $data]
}
}
} else {
error "cksum_path unsupported $opts for path type [file type $path]"

329
src/modules/punk/mix/commandset/project-999999.0a1.0.tm

@ -168,8 +168,8 @@ namespace eval punk::mix::commandset::project {
}
set opts [dict merge $defaults $args]
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
#validate -layout against documented choices with prefix matching (punk::args -choiceprefix defaults to true)
dict set opts -layout [tcl::prefix::match -message "project.new -layout" $::punk::mix::commandset::project::argdoc::LAYOUTNAMES [dict get $opts -layout]]
#-layout is prefix-matched later against the live layout set (not the load-time argdoc
#LAYOUTNAMES snapshot, which can be stale - e.g layouts only visible now that cwd is inside a project)
# -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
set opt_type [dict get $opts -type]
if {$opt_type ni [punk::mix::cli::lib::module_types]} {
@ -255,6 +255,15 @@ namespace eval punk::mix::commandset::project {
puts stderr "commandset::project::new WARNING - no handler available for the 'punk.templates' capability - template providers will be unable to provide layout locations"
return
}
#validate -layout against the live layout names with prefix matching (exact name wins, unique prefix accepted)
if {[catch {tcl::prefix::match -message "project.new -layout" [dict keys $layout_dict] $opt_layout} matched_layout]} {
puts stderr "commandset::project::new - unable to match specified layout-name '$opt_layout'"
puts stderr $matched_layout
puts stderr "layout names found: [dict keys $layout_dict]"
return
}
set opt_layout $matched_layout
dict set opts -layout $matched_layout
if {[dict exists $layout_dict $opt_layout]} {
set layout_name $opt_layout
set layout_info [dict get $layout_dict $layout_name]
@ -434,6 +443,13 @@ namespace eval punk::mix::commandset::project {
puts stdout "no .fossil-settings in source template - update not required"
}
#G-087: thin layouts ship include_modules.config manifests but no bootsupport module snapshot.
#Inject current modules from the generating shell's own trees.
set inject_counts [lib::bootsupport_inject $projectdir]
if {[dict get $inject_counts manifests] > 0} {
puts stdout "bootsupport module injection from running shell: $inject_counts"
}
#scan all files in template
#
#TODO - deck command to substitute templates?
@ -1095,6 +1111,315 @@ namespace eval punk::mix::commandset::project {
#consider using punk::cap to enable multiple template-substitution providers with their own set of tagnames and/or tag wrappers, where substitution providers are all run
return [string cat % $tagname %]
}
punk::args::define {
@id -id ::punk::mix::commandset::project::lib::_tm_version_sort
@cmd -name punk::mix::commandset::project::lib::_tm_version_sort\
-summary\
"lsort -command helper comparing versioned .tm file tails."\
-help\
"Compare two .tm module file tails (e.g name-1.2.3.tm) by their version
segments using package vcompare, falling back to string comparison for
versions package vcompare cannot interpret."
@values -min 2 -max 2
tail_a -type string
tail_b -type string
}
proc _tm_version_sort {tail_a tail_b} {
lassign [punk::mix::util::split_modulename_version $tail_a] _na va
lassign [punk::mix::util::split_modulename_version $tail_b] _nb vb
if {[catch {package vcompare $va $vb} cmp]} {
return [string compare $va $vb]
}
return $cmp
}
punk::args::define {
@id -id ::punk::mix::commandset::project::lib::_bootsupport_inject_copyfile
@cmd -name punk::mix::commandset::project::lib::_bootsupport_inject_copyfile\
-summary\
"Copy one bootsupport file with punkcheck recording and plain-copy degradation."\
-help\
"Internal helper for bootsupport_inject. Records the copy as a punkcheck
install event via the caller's open installtrack event (variable named by
eventvar; skips unchanged recorded targets), or falls back to a plain
file copy - degrading the caller's event variable to empty on the first
punkcheck failure. Updates the caller's counters dict (variable named by
countsvar: installed/skipped/failed)."
@values -min 4 -max 4
eventvar -type string -help\
"Caller's variable name holding the open installtrack event object (may be empty)."
countsvar -type string -help\
"Caller's variable name holding the counters dict."
srcfile -type string
tgtfile -type string
}
proc _bootsupport_inject_copyfile {eventvar countsvar srcfile tgtfile} {
upvar 1 $eventvar boot_event
upvar 1 $countsvar counts
set recorded 0
if {$boot_event ne ""} {
if {[catch {
$boot_event targetset_init INSTALL $tgtfile
$boot_event targetset_addsource $srcfile
catch {
#virtual module identity/version sources so records identify the product
lassign [punk::mix::util::split_modulename_version [file tail $tgtfile]] bs_module_name bs_module_version
if {$bs_module_version ne ""} {
$boot_event targetset_addsource_virtual module_name $bs_module_name
$boot_event targetset_addsource_virtual module_version $bs_module_version
}
}
if { [llength [dict get [$boot_event targetset_source_changes] changed]]
|| [llength [$boot_event get_targets_exist]] < [llength [$boot_event get_targets]]
} {
file mkdir [file dirname $tgtfile]
$boot_event targetset_started
if {[catch {file copy -force $srcfile $tgtfile} cerr]} {
$boot_event targetset_end FAILED -note "bootsupport_inject copy failed: $cerr"
dict incr counts failed
} else {
$boot_event targetset_end OK
dict incr counts installed
}
} else {
$boot_event targetset_end SKIPPED
dict incr counts skipped
}
} perr]} {
puts stderr "bootsupport_inject: punkcheck record failed for $tgtfile ($perr) - continuing with plain copies"
set boot_event ""
} else {
set recorded 1
}
}
if {!$recorded} {
file mkdir [file dirname $tgtfile]
if {[catch {file copy -force $srcfile $tgtfile} cerr]} {
puts stderr "bootsupport_inject: copy failed $srcfile -> $tgtfile : $cerr"
dict incr counts failed
} else {
dict incr counts installed
}
}
return
}
punk::args::define {
@id -id ::punk::mix::commandset::project::lib::bootsupport_inject
@cmd -name punk::mix::commandset::project::lib::bootsupport_inject\
-summary\
"Inject bootsupport modules into a generated project from the running shell."\
-help\
"Populate <projectdir>/src/bootsupport/modules* with the .tm modules listed
in each modules*/include_modules.config manifest, sourced from the
generating shell's own current trees (G-087: thin layouts ship only the
manifests - no module snapshot).
Source preference per modules folder:
- if the running shell was launched from within a punkshell-style project
that has src/bootsupport/<modulesfolder>, modules are copied from that
tree (the shell project's punkcheck-managed bootsupport snapshot).
- otherwise (e.g a standalone punk kit) each manifest entry is resolved
against the shell's module paths (tcl::tm::list), picking the highest
version. Tcl-major-specific manifests (modules_tcl8/modules_tcl9) are
only resolved this way when the running shell's Tcl major version
matches.
Modules with the development magic version (999999.*) are never injected.
Adjacent non-.tm support files in a matched module's directory (e.g
struct's sets_tcl.tcl loader companion, textutil's .tex hyphenation
data) are carried along with the modules they support.
Bootsupport lib trees (pkgIndex.tcl-based packages such as fileutil,
needed by the generated project's make.tcl under a plain tclsh) are
tree-copied from the shell project's src/bootsupport/lib* folders when
available; a standalone kit emits a notice instead (no manifest exists
for the lib set - run the generated make.tcl via the kit executable, or
populate src/bootsupport/lib manually).
Copies are recorded as punkcheck install events in
<projectdir>/src/bootsupport/.punkcheck when possible (degrades to plain
copies with a notice if punkcheck tracking fails).
Returns a dict of counters: manifests installed skipped missing failed
libfolders."
@values -min 1 -max 1
projectdir -type string -help\
"Root folder of the generated project. Injection occurs only if
src/bootsupport exists there and carries include_modules.config
manifest(s) with entries."
}
proc bootsupport_inject {args} {
set argd [punk::args::parse $args withid ::punk::mix::commandset::project::lib::bootsupport_inject]
set projectdir [dict get $argd values projectdir]
package require punk::repo
package require punkcheck
package require punk::mix::util
set counts [dict create manifests 0 installed 0 skipped 0 missing 0 failed 0 libfolders 0]
set bootbase [file join $projectdir src/bootsupport]
if {![file isdirectory $bootbase]} {
return $counts ;#layout carries no bootsupport - nothing to inject
}
#the shell's own project (if any) is the preferred source - its src/bootsupport is the
#canonical punkcheck-managed snapshot of current real-versioned modules
set shellbase [file dirname [file dirname [file normalize [info nameofexecutable]/___]]]
set shell_projectroot [punk::repo::find_project $shellbase]
set running_tclx tcl[lindex [split [info tclversion] .] 0]
set boot_event ""
set boot_installer ""
if {[catch {
set boot_installer [punkcheck::installtrack new project.new $bootbase/.punkcheck]
$boot_installer set_source_target $projectdir $bootbase
set boot_event [$boot_installer start_event {-install_step bootsupport_inject}]
} errM]} {
puts stderr "bootsupport_inject: punkcheck tracking unavailable ($errM) - copying without install records"
set boot_event ""
}
set seen_adjacent_dirs [dict create]
set bootmodulefolders [glob -nocomplain -dir $bootbase -type d -tails modules modules_tcl*]
foreach bm $bootmodulefolders {
set configfile [file join $bootbase $bm include_modules.config]
if {![file exists $configfile]} {
continue
}
set bootsupport_modules [list] ;#populated by sourcing the config
set bootsupport_module_folders [list] ;#historical config variable - not used here
if {[catch {source $configfile} errM]} {
puts stderr "bootsupport_inject WARNING - failed to read $configfile: $errM"
continue
}
if {![llength $bootsupport_modules]} {
continue
}
dict incr counts manifests
#determine source roots for this modules folder
set sourceroots [list]
if {$shell_projectroot ne "" && [file isdirectory [file join $shell_projectroot src/bootsupport $bm]]} {
lappend sourceroots [file join $shell_projectroot src/bootsupport $bm]
puts stdout "bootsupport_inject: $bm source = [lindex $sourceroots 0] (generating shell's project bootsupport)"
} else {
lassign [split $bm _] _bm tclx
if {$tclx ne "" && $tclx ne $running_tclx} {
puts stderr "bootsupport_inject NOTICE - $bm manifest has entries but no shell project bootsupport tree is available and the running shell is $running_tclx - skipping (populate $bm manually or regenerate from a matching shell)"
continue
}
foreach tmdir [tcl::tm::list] {
if {[file isdirectory $tmdir]} {
lappend sourceroots $tmdir
}
}
puts stdout "bootsupport_inject: $bm resolving manifest entries from the running shell's module paths ([llength $sourceroots] paths)"
}
if {![llength $sourceroots]} {
puts stderr "bootsupport_inject WARNING - no usable module source paths for $bm"
continue
}
foreach {relpath modulematch} $bootsupport_modules {
set modulematch [string trim $modulematch :]
set module_subpath [string map {:: /} [namespace qualifiers $modulematch]]
set mtail [namespace tail $modulematch]
#always glob with the dash for un-versioned names - or we match modules whose names are extensions of others
if {[string first - $modulematch] != -1} {
set searchglob ${mtail}*.tm
} else {
set searchglob ${mtail}-*.tm
}
set modulematch_is_glob [regexp {[*?\[\]]} $modulematch]
#note: the manifest's base path (relpath - e.g src/vendormodules, modules) describes source
#folders of a punkshell-style source tree for make.tcl bootsupport refreshes. For injection the
#sources are the shell's already-assembled trees, so relpath is deliberately not used.
set found [dict create] ;#tail -> full source path (first source root providing a tail wins)
foreach root $sourceroots {
set dir [file join $root $module_subpath]
foreach f [glob -nocomplain -dir $dir -tail -type f -- $searchglob] {
if {[string match *-999999* $f]} {
continue ;#development magic version - never inject
}
if {![dict exists $found $f]} {
dict set found $f [file join $dir $f]
}
}
}
if {![dict size $found]} {
puts stderr "bootsupport_inject: missing source for bootsupport module $modulematch - no matches in [llength $sourceroots] source path(s)"
dict incr counts missing
continue
}
if {$modulematch_is_glob} {
#glob entries intentionally keep all matches
set copy_tails [lsort -command ::punk::mix::commandset::project::lib::_tm_version_sort [dict keys $found]]
} else {
#non-glob entries track only the latest version
set copy_tails [list [lindex [lsort -command ::punk::mix::commandset::project::lib::_tm_version_sort [dict keys $found]] end]]
}
foreach ctail $copy_tails {
set srcfile [dict get $found $ctail]
set tgtfile [file join $bootbase $bm $module_subpath $ctail]
_bootsupport_inject_copyfile boot_event counts $srcfile $tgtfile
}
#adjacent non-.tm support files: some vendored modules load companion files from
#their own directory at runtime (e.g struct's set-x.y.tm sources sets_tcl.tcl/sets_c.tcl,
#textutil's hyphenation .tex data) - carry them along with the modules they support.
set adjacent_src_dir [file dirname [dict get $found [lindex $copy_tails 0]]]
if {![dict exists $seen_adjacent_dirs $adjacent_src_dir]} {
dict set seen_adjacent_dirs $adjacent_src_dir 1
foreach af [glob -nocomplain -dir $adjacent_src_dir -type f -tails *] {
if {[string match -nocase *.tm $af]} {
continue
}
if {$af in {include_modules.config README.md AGENTS.md .punkcheck}} {
continue
}
_bootsupport_inject_copyfile boot_event counts [file join $adjacent_src_dir $af] [file join $bootbase $bm $module_subpath $af]
}
}
}
}
if {$boot_event ne ""} {
catch {$boot_event end}
}
if {$boot_installer ne ""} {
catch {$boot_event destroy}
catch {$boot_installer destroy}
}
#bootsupport lib trees (pkgIndex.tcl-based packages e.g fileutil - required by the generated
#project's make.tcl under a plain tclsh). There is no manifest for the lib set (the
#include_modules.config manifests cover .tm modules only) so these are tree-copied from the
#generating shell's project bootsupport when available.
#(run after the installtrack event above is closed - it holds its record list in memory and a
# concurrent punkcheck::install writing the same db would be clobbered by its save)
set libfolders_available [list]
if {$shell_projectroot ne ""} {
set libfolders_available [glob -nocomplain -dir [file join $shell_projectroot src/bootsupport] -type d -tails lib lib_tcl*]
}
if {[llength $libfolders_available]} {
foreach lf $libfolders_available {
set src_lib [file join $shell_projectroot src/bootsupport $lf]
set tgt_lib [file join $bootbase $lf]
file mkdir $tgt_lib
puts stdout "bootsupport_inject: $lf source = $src_lib (generating shell's project bootsupport)"
if {[catch {
set resultdict [punkcheck::install $src_lib $tgt_lib -punkcheck-folder $bootbase -installer project.new -overwrite installedsourcechanged-targets -createempty 1 -exclude-paths {README.md AGENTS.md **/AGENTS.md}]
puts stdout [punkcheck::summarize_install_resultdict $resultdict]
dict incr counts libfolders
} errM]} {
puts stderr "bootsupport_inject WARNING - lib tree copy failed for $lf: $errM"
}
}
} else {
if {[file isdirectory [file join $bootbase lib]]} {
puts stderr "bootsupport_inject NOTICE - bootsupport lib packages (pkgIndex.tcl-based, e.g fileutil) were not injected: no shell project bootsupport lib tree is available from this shell (standalone kit; the lib set has no manifest). The generated project's make.tcl requires them under a plain tclsh - run it via this shell's executable instead (<punkexe> src/make.tcl ...), or populate src/bootsupport/lib manually."
}
}
return $counts
}
punk::args::define {
@id -id ::punk::mix::commandset::project::lib::get_projects
@cmd -name punk::mix::commandset::project::lib::get_projects\

6
src/modules/punk/mix/commandset/project-buildversion.txt

@ -1,6 +1,10 @@
0.2.2
0.3.0
#First line must be a semantic version number
#all other lines are ignored.
#0.3.0 - G-087 stage 3: new lib::bootsupport_inject (+ lib::_tm_version_sort) - project.new now injects bootsupport modules into the generated project from the generating shell's current trees, driven by the thin layout's include_modules.config manifests (shell-project bootsupport tree preferred; standalone kits resolve via tcl::tm::list; 999999.* dev versions never injected; punkcheck-recorded with plain-copy degradation)
#0.3.0 - bootsupport_inject also tree-copies the shell project's src/bootsupport/lib* pkgIndex-based lib trees (e.g fileutil - required by the generated make.tcl under plain tclsh); standalone kits emit a notice (lib set has no manifest) - generated make.tcl remains runnable via the kit executable
#0.3.0 - bootsupport_inject carries adjacent non-.tm support files beside matched modules (struct sets_*.tcl loader companions, textutil .tex hyphenation data) via new private helper _bootsupport_inject_copyfile; bootsupport include_modules.config gained the previously-missing cmdline and struct::set entries (files existed recordless in bootsupport; fileutil requires cmdline, make.tcl uses struct::set)
#0.3.0 - new: -layout now prefix-matched against the live layout set at call time instead of the package-load-time LAYOUTNAMES snapshot (stale snapshot could reject layouts that appeared after load, e.g when cwd moved into a project)
#0.2.2 - fix: new's no-handler warning path used 'put' instead of 'puts' (raised invalid command name error instead of warning)
#0.2.1 - fix: -layout now validated and normalized via tcl::prefix::match against documented layout choices, matching the PUNKARGS -choiceprefix default (true); previously the manual parser accepted arbitrary values with no validation
#0.2.0 - updated punkcheck::install call sites to use -exclude-paths / -exclude-paths-core (replaced -antiglob-paths + -exclude-dirsegments) — call-site API change warrants minor bump

3
src/modules/punk/mix/templates-buildversion.txt

@ -1,5 +1,6 @@
0.1.5
0.2.0
#First line must be a semantic version number
#all other lines are ignored.
#0.2.0 - G-087 stage 3: ships the thin punk.project layout payload at templates/project_layouts/vendor/punk/project-0.1 (synced from src/project_layouts by make.tcl - do not hand-edit the modpod copy) so the module-shipped ref resolves via punk::cap::handlers::templates 0.3.0 and a bare kit can generate projects. shellproject_multivendor declaration fixed to src/decktemplates (was decktemplates, which never existed at a project root - layouts/templates from the shell's own project were invisible when cwd was outside any project).
#0.1.5 - G-087 stage 2: module-shipped layout ref migrated to fauxlink grammar (project#vendor+punk+project-0.1.fauxlink)
#0.1.4 - removed dead templates/layouts/project payload subtree (design-generation residue: no reader; layout payloads live in src/project_layouts, located via layout_refs - G-087 hygiene)

19
src/modules/punkcheck-999999.0a1.0.tm

@ -770,12 +770,7 @@ namespace eval punkcheck {
set this_installer_record [dict get $resultinfo record]
}
set event_record [punkcheck::recordlist::new_installer_event_record install {*}{
-id $eventid
-source $rel_source
-targets $rel_target
-config $config
}]
set event_record [punkcheck::recordlist::new_installer_event_record install -id $eventid -source $rel_source -targets $rel_target -config $config]
set this_installer_record [punkcheck::recordlist::installer_record_add_event $this_installer_record $event_record]
set this_installer_record [punkcheck::recordlist::installer_record_pruneevents $this_installer_record $record_list]
@ -1001,10 +996,18 @@ namespace eval punkcheck {
# }
#}
#get_relativecksum_from_base and fill_relativecksums_from_base_and_relativepathdict will set cksum to <PATHNOTFOUND> if fpath doesn't exist
if {[file pathtype $source_relpath] eq "absolute"} {
#source shares no common root with the punkcheck folder, so path_relative left it absolute
#(e.g a //zipfs:/ module-mounted layout installing to the filesystem - G-087 module-carried layouts).
#Use the helpers' independent-absolute mode (empty base) - they error on unrelatable roots otherwise.
set ck_base ""
} else {
set ck_base $punkcheck_folder
}
if {$use_cache} {
set source_cksum_info [punk::mix::base::lib::fill_relativecksums_from_base_and_relativepathdict $punkcheck_folder [dict create $source_relpath $use_cache_record]]
set source_cksum_info [punk::mix::base::lib::fill_relativecksums_from_base_and_relativepathdict $ck_base [dict create $source_relpath $use_cache_record]]
} else {
set source_cksum_info [punk::mix::base::lib::get_relativecksum_from_base $punkcheck_folder $source_relpath {*}$cksum_opts]
set source_cksum_info [punk::mix::base::lib::get_relativecksum_from_base $ck_base $source_relpath {*}$cksum_opts]
}

4
src/modules/punkcheck-buildversion.txt

@ -1,6 +1,8 @@
0.3.1
0.4.0
#First line must be a semantic version number
#all other lines are ignored.
#0.4.0 - cross-root sources: installfile_add_source_and_fetch_metadata now records sources that share no common root with the punkcheck folder (e.g //zipfs:/ module-mounted sources installing to the filesystem) as independent absolute paths via the cksum helpers' empty-base mode, instead of erroring 'don't share a common root'. Enables punkcheck::install from module-carried (vfs-mounted) layout payloads - G-087 stage 3 bare-kit project generation.
#0.3.2 - fix: start_installer_event built its EVENT header record from a braced literal, writing the literal strings '$eventid' '$rel_source' '$rel_target' '$config' into every punkcheck::install event header instead of the values. Readers matching on the returned eventid masked it, but any two such events in one file collide on the duplicate literal id when punkcheck::installtrack loads the file (first hit: G-087 stage 3 sharing src/project_layouts/.punkcheck between install and installtrack writers), and event -source/-targets/-config provenance metadata was unrecorded.
#0.3.1 - PUNKARGS fix: install -overwrite, -source-checksum and -punkcheck-folder now declare -choiceprefix 0 so documented behaviour matches the manual parser (exact match required, no prefix matching)
#0.3.0 - added installsource_add_virtual proc + installevent targetset_addsource_virtual method: 'virtual' named-value SOURCE records (-type virtual -path virtual:<id> -value <v>) compared by value, for e.g recording resolved module build versions on install/delete records
#0.2.0 - merged -exclude-dirsegments + -antiglob_paths into unified -exclude-paths (backward-compat aliases retained)

Loading…
Cancel
Save