Browse Source

G-012 achieved: inert VCS-config template payloads + layout gitignore refresh

Layout .gitignore payloads are now stored inert as gitignore.in and
materialized to .gitignore at project generation, ending the
self-censoring-template hazard (live nested .gitignore files silently
untracked template content in git while fossil managed it fine):

- punk::mix::commandset::layout 0.3.0: layout_materialize_renames map;
  layout_stage_chain renames inert payloads after composing all overlay
  layers (.anti markers keep targeting store names); layout_materialize
  withholds the in-place fast path from inert-payload folders so the
  store is never renamed; inert+live name conflict errors loudly
- make.tcl thin-layout sync refreshes every vendor/punk layout's
  gitignore.in from the canonical repo-root .gitignore (punkcheck-tracked
  sync_layouts events); workflow text updated
- the four vintage payload variants eliminated as drift: all payloads
  now byte-identical mirrors of root; modpod copy carried and its stale
  .gitignore pruned
- materialize.test +5 G-012 characterization tests (25/25; mix subtree
  71 pass / 1 known skip)

Verified end-to-end: generation from each affected layout yields a
byte-identical .gitignore (othersample .anti case yields none); root
edits propagate to payloads via make.tcl libs in both directions; the
fc1c474c force-added template files are back to ordinary git tracking
with no ignore rule matching them; modpod payload README trees are now
visible as ordinary trackable files.

Goal flipped achieved 2026-08-01 (user-confirmed; the custom/_project
acceptance clause is stale - that store level was retired by G-087
stage 5 after drafting; recorded in the archive record and detail
evidence). Reference sweep + archive moves per the flip protocol.
Project version 0.39.5.

Assisted-by: harness=opencode; primary-model=opencode/kimi-k3; api-location=unknown
master
Julian Noble 1 week ago
parent
commit
24bc272c93
  1. 2
      .fossil-settings/AGENTS.md
  2. 2
      AGENTS.md
  3. 2
      ARCHITECTURE.md
  4. 12
      CHANGELOG.md
  5. 4
      GOALS-archive.md
  6. 4
      GOALS.md
  7. 8
      goals/G-027-derived-project-pull-updates.md
  8. 5
      goals/G-031-componentized-kit-boot.md
  9. 2
      goals/G-043-subshell-definition-plugins.md
  10. 3
      goals/G-047-declared-primary-vcs.md
  11. 2
      goals/G-086-light-project-layout.md
  12. 2
      goals/G-136-generated-project-icon-seeding.md
  13. 83
      goals/archive/G-012-template-payload-safety.md
  14. 2
      punkproject.toml
  15. 2
      src/AGENTS.md
  16. 46
      src/make.tcl
  17. 2
      src/modules/punk/AGENTS.md
  18. 53
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/.gitignore
  19. 3
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/bin/README.md
  20. 26
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/bin/runtime/README.md
  21. 108
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/gitignore.in
  22. 6
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/lib/README.md
  23. 9
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/modules/README.md
  24. 46
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl
  25. 150
      src/modules/punk/mix/commandset/layout-999999.0a1.0.tm
  26. 3
      src/modules/punk/mix/commandset/layout-buildversion.txt
  27. 35
      src/project_layouts/README.md
  28. 9
      src/project_layouts/custom/punk/othersample@sample-0.1/README.md
  29. 0
      src/project_layouts/custom/punk/othersample@sample-0.1/gitignore.in.anti
  30. 16
      src/project_layouts/custom/punk/sample-0.1/extra.txt
  31. 47
      src/project_layouts/vendor/punk/basic/.gitignore
  32. 108
      src/project_layouts/vendor/punk/basic/gitignore.in
  33. 46
      src/project_layouts/vendor/punk/basic/src/make.tcl
  34. 39
      src/project_layouts/vendor/punk/minimal/.gitignore
  35. 108
      src/project_layouts/vendor/punk/minimal/gitignore.in
  36. 53
      src/project_layouts/vendor/punk/project-0.1/.gitignore
  37. 108
      src/project_layouts/vendor/punk/project-0.1/gitignore.in
  38. 46
      src/project_layouts/vendor/punk/project-0.1/src/make.tcl
  39. 47
      src/project_layouts/vendor/punk/sample-0.1/.gitignore
  40. 108
      src/project_layouts/vendor/punk/sample-0.1/gitignore.in
  41. 86
      src/tests/modules/punk/mix/testsuites/layout/materialize.test

2
.fossil-settings/AGENTS.md

@ -28,7 +28,7 @@ Versioned fossil settings (one setting value or glob list per file) for this dua
- **No negation exists in fossil globs.** Git's `/bin/*` + `!exception` pattern becomes: ignore `bin/*` wholesale and explicitly `fossil add` the tracked exceptions (ignore-glob never affects already-managed files, so they stay tracked). The exception set is whatever git tracks under an ignored tree - at the time of writing: `bin/AGENTS.md`, `bin/*.cmd`, `bin/*.kit`, `bin/*.tcl`, `bin/*.sh`, `bin/*.bash`, `.claude/settings.json` (shared claude harness hooks/permissions; session-local `.claude` files stay ignored), `.claude/skills/**` (project agent skills - synced copies of `.agents/skills/**`, which is not git-ignored and needs no exception), plus force-tracked one-offs (`bin/libssp-0.dll`, `bin/runtime/README.md` - guidance, not build output; a `!` negation cannot re-include it because git does not descend into the excluded `bin/runtime` directory, so `git add -f` is the only mechanism, `src/vfs/punk9magicsplat.vfs/lib/nmake/x86_64-w64-mingw32-nmakehlp.exe`). Derive the current set with the verification comparison below rather than trusting this list.
- A bare directory name prunes that whole tree; `*` crosses `/`; git patterns intended to match at any depth need an additional `*/` variant; `#` comment lines are honoured.
- Do not add the fossil-generated names (`manifest`, `manifest.uuid`, `manifest.tags`) to `ignore-glob` - fossil handles them itself.
- **Nested `.gitignore` files are git-only**: ignore files inside subtrees (e.g. the project-layout templates under `src/project_layouts/`) are honoured by git as nested ignores of the outer repo but invisible to fossil - template content they match is untracked in git yet managed by fossil. Treat such divergence as a signal to review the git side (usually `git add -f` of the affected template files).
- **Nested `.gitignore` files are git-only**: ignore files inside subtrees are honoured by git as nested ignores of the outer repo but invisible to fossil - content they match is untracked in git yet managed by fossil. Treat such divergence as a signal to review the git side. (The former instance of this class - the project-layout templates under `src/project_layouts/` carrying live `.gitignore` payloads - is resolved structurally: payloads are stored inert as `gitignore.in` and materialized at project generation, G-012. Any NEW nested `.gitignore` appearing under a layout store is the hazard recurring and should be converted to the inert form, not force-added.)
- **Case sensitivity differs**: git ignore matching is case-insensitive on Windows (`core.ignorecase`), fossil glob matching is case-sensitive - e.g. a `todo.txt` pattern hits vendored `TODO.txt` in git but not fossil.
### Safe sync procedure (mandatory for any ignore-rule change, not only on request)

2
AGENTS.md

@ -211,7 +211,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/` — Thin project layout payloads (structure, scripts, docs, manifests - no stored bootsupport/vfs module snapshots; G-087, achieved - see goals/archive/G-087-thin-project-layouts.md). The build's thin-layout sync step (`make.tcl modules`/`libs`/`packages`/`bakehouse`) 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` composes each layout's overlay chain (vendor then custom tier, `<name>@<base>` derived folders, `.anti` deletion markers - see the store README) via `layout_materialize`, injects bootsupport modules/libs from the generating shell at generation time, and seeds generated-project identity metadata (punkproject.toml, CHANGELOG.md, punkorigin.toml provenance baseline) programmatically. 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).
- `src/project_layouts/` — Thin project layout payloads (structure, scripts, docs, manifests - no stored bootsupport/vfs module snapshots; G-087, achieved - see goals/archive/G-087-thin-project-layouts.md). The build's thin-layout sync step (`make.tcl modules`/`libs`/`packages`/`bakehouse`) keeps each layout's `src/make.tcl`, `src/build.tcl` and bootsupport `include_modules.config` copies current, refreshes every vendor/punk layout's inert `gitignore.in` payload from the canonical root `.gitignore` (G-012 - VCS-config payloads are stored inert and materialized to their live name at project generation; never store a literal `.gitignore` in a layout), and syncs the punk.project payload into the `punk::mix::templates` modpod source tree; `dev project.new` composes each layout's overlay chain (vendor then custom tier, `<name>@<base>` derived folders, `.anti` deletion markers - see the store README) via `layout_materialize`, injects bootsupport modules/libs from the generating shell at generation time, and seeds generated-project identity metadata (punkproject.toml, CHANGELOG.md, punkorigin.toml provenance baseline) programmatically. 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

2
ARCHITECTURE.md

@ -95,7 +95,7 @@ Two layers with a deliberate dependency direction (the class never depends on th
- **Payload/target consistency checks (G-133 + G-134), all advisory.** At the same post-merge seam, a bake classifies each binary library in the merged tree by header (`punkboot::utils::binary_arch_classify` - PE/ELF/Mach-O, honest unknowns) against the kit's target platform; wrong-arch libraries outside platform-discriminated subdirs (canonical `<os>-<cpu>` names, vendor spellings like `win-x64`) earn recapped `BUILD-WARNING`s. After assembly, a kit declaring smoke-require packages (`mapvfs.toml` `smokerequire` key) has each one plain-`package require`d inside the freshly built artifact via its tclsh subcommand - the only check that sees resolution-order defects (wrong-arch version shadowing); cross-target kits skip with a stated reason. The assembled image is also probed for its zip offset convention (G-134, `punkboot::utils::kit_offsetstyle_report` over `punk::zip::archive_info`): a FILE-relative attached payload warns (the pipeline emits archive-relative; the G-128 stamper refuses file-relative by default), no-zip/plain results stay silent. Same guarded-require degradation; `make.tcl check` reports all three. None guarantees statics, pure-tcl packages with binary deps, or version preference beyond the declared smoke set. Sources: `src/AGENTS.md`, `src/runtime/AGENTS.md`, `src/modules/punkboot/utils-999999.0a1.0.tm`.
- **Kit icon step (G-057 + G-128).** Every kit a bake builds gets a `<kitname>.resources.toml` sidecar (deployed beside `bin/<kit>`) recording the build-time icon choice - default `src/runtime/punkshell.ico`, overridable by a root `punkshell.ico` in the kit's own custom `.vfs` folder (pre-merge) - with sha256 identity and provenance lifted from the icon's G-135 assetorigin record. win32-target kits get the icon embedded as PE `RT_ICON`/`RT_GROUP_ICON` behind a single seam (`::punkboot::kit_icon_process`): the vendored punkres portable stamper (`src/tools/punkres`, built to `bin/punkres(.exe)` by the make.tcl tool step) is selected when present and works from any build host; otherwise the twapi arm (tcl-sfe mechanism) serves windows hosts with nothing built. Both arms share one semantic (delete all icon/group entries, write ids 1..N; the group is NAMED from the icon file's uppercased rootname - `PUNKSHELL` - with language adopted from the replaced group) and stamp a per-kit copy of the payload-free raw runtime prefix BEFORE payload attach; punkres can also stamp a FINISHED kit post-hoc, preserving an appended zip payload (archive-relative moved verbatim; file-relative refused by default, shifted with a consent flag). Non-PE targets skip as not applicable; a host where neither mechanism serves skips with a combined notice naming the punkres build remedy; the sidecar is written in every case. Sources: `src/AGENTS.md`, `bin/AGENTS.md`, `src/vfs/AGENTS.md`, `src/tools/AGENTS.md`.
- **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. Both suites also emit the punkbin LIBRARY tier (G-138, achieved): verified tcllib/tcllibc installs as generation-tagged immutable zips (`lib/allplatforms` + `lib/<target>`, embedded schema-v2 class=library records, punkzip-deterministic) - the `library-artifacts` step in each recipe. The tier's `<target>` axis includes cross-built lanes: suite_tcl90's `tcllibc-linux` step (G-140, achieved) cross-builds the critcl accelerators for linux-x86_64 via critcl driving `zig cc -target` (stubs linkage - no linux host involved), structurally ELF-gated on the build host and consumed by the punk9linux kit payload. Consumption (G-139, achieved): `make.tcl libfetch` materializes declared artifacts into the untracked `bin/packages/<target>` tier with server-trust consent, and the PACKAGES_tcl<N> libs phase + per-.vfs payload declarations with `source_root = "packages"` (G-115) feed the deployed `lib_tcl<N>` trees and kit vfs payloads - the vendored tcllib trees are retired, and embedded records ride into baked kits. `src/buildsuites/suite_tcl86/` is the 8.6 sibling (G-099 + G-100, both achieved): a Tcl 8.6 windows runtime (static + dynamic shells, `tcl86t.dll`, on-disk lib tree - no zipfs, so no self-contained kit) with thread 2.8 + tclvfs + Tk 8.6 + tklib + tcllib(+tcllibc critcl accelerators) companions, gated against the 8.6 core/thread/tclvfs testsuites with tcllib/tklib on a record tier and an opt-in `test-tk`; punkshell's own runtests is censused on that runtime at parity with a same-day native-8.6 baseline. Suite child shells scrub `TCL<major>_<minor>_TM_PATH` as well as TCLLIBPATH/TCL_LIBRARY/TK_LIBRARY - without it a census measures the machine's module trees. The 8.6 kit container strategy remains in-flux - see "In-flux areas".
- **Project generation.** `dev project.new` composes thin layouts from `src/project_layouts/` (overlay chain with `.anti` deletion markers and `name@base` derivation - G-087, achieved) and injects bootsupport modules from the generating shell at generation time.
- **Project generation.** `dev project.new` composes thin layouts from `src/project_layouts/` (overlay chain with `.anti` deletion markers and `name@base` derivation - G-087, achieved; VCS-config payloads stored inert as `gitignore.in` and materialized to `.gitignore` at composition - G-012) and injects bootsupport modules from the generating shell at generation time.
- **Workflow overview.** `tclsh src/make.tcl workflow` prints the embedded ASCII data-flow overview of the build/release pipeline, with its own update contract in `src/AGENTS.md`. This section deliberately summarises rather than copies it.
## Test harness

12
CHANGELOG.md

@ -5,6 +5,18 @@ 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.39.5] - 2026-08-01
- Project-layout template payloads no longer self-censor in git (G-012): layout
`.gitignore` payloads are stored inert as `gitignore.in` and materialized to
`.gitignore` at project generation (punk::mix::commandset::layout 0.3.0 -
rename map in `layout_materialize_renames`, staging-time rename after overlay
composition, in-place fast path withheld from inert-payload folders).
make.tcl's thin-layout sync now refreshes every vendor/punk layout's
`gitignore.in` from the canonical root `.gitignore` (punkcheck-tracked), so
generated projects receive current ignore rules and template files can no
longer silently drop out of git tracking.
## [0.39.4] - 2026-08-01
- make.tcl piped output no longer leaks orphan ANSI fragments ('0;1m', '[0m', a lone

4
GOALS-archive.md

@ -28,6 +28,10 @@ Acceptance: a subshell started with an ssh-channel-backed and a tk-widget-backed
Scope: src/modules/punk/console-999999.0a1.0.tm; src/modules/punk/repl-999999.0a1.0.tm; src/modules/punk/repl/codethread-999999.0a1.0.tm
Acceptance: from a running punk session's code interp, without `repl eval`: `console_fact_get` returns the same values the parent sees and a fact set in the parent is immediately visible; a terminal query (e.g. `get_cursor_pos` or `dec_get_mode`) against the default console succeeds and cooperates with the repl reader (no lost or garbled input); a console constructed and owned by code-interp code is operated on locally (no round-trip to the parent); the existing console test suites pass and single-interp (non-repl) usage is unchanged.
### G-012 [achieved 2026-08-01] Template system: inert VCS-config payloads and explicit layout refresh → detail: goals/archive/G-012-template-payload-safety.md
Scope: src/project_layouts/; src/make.tcl; src/modules/punk/mix/ (layout instantiation); fauxlink module (bootsupport 0.2.0 - promoted if chosen as mechanism)
Acceptance: a scan of src/project_layouts finds no file named .gitignore, and git check-ignore --no-index over every layout file matches only root-.gitignore rules (nested rules provably inert); a project generated from each affected layout receives a working .gitignore whose content matches the canonical payload/target; after editing a canonical source (e.g. root .gitignore), the make.tcl template-refresh step updates the derived layout payloads (punkcheck-tracked), covering vendor/punk layouts as well as custom/_project; the previously hidden template files (layout READMEs, vendored TODO-class files) remain git-tracked without per-file force-add exceptions. (Achieved with the custom/_project clause stale - that store level was retired by G-087 stage 5 after drafting; the refresh covers every vendor/punk layout. Evidence in the detail file.)
### G-015 [achieved 2026-07-07] Punk executable `script` subcommand: reliable non-interactive piped/script execution → detail: goals/archive/G-015-script-subcommand-piped-stdin.md
Scope: src/vfs/_config/punk_main.tcl; src/lib/app-punkshell/punkshell.tcl (script path or a leaner dedicated app package)
Acceptance: piping commands to `<punkexe> script` runs them and terminates at stdin EOF with no trailing `exit` required, exit code 0 on success; a failing piped command terminates the process with a nonzero exit code and the error on stderr, never landing in an interactive shell regardless of console availability or PUNK_PIPE_EOF; `<punkexe> script <file> [<args>...]` executes the file with conventional ::argv0/::argv and propagates its error status the same way; the script path installs none of the `shell` subcommand's shellfilter stacks/transforms and the launch plumbing itself emits nothing on stdout/stderr (the current stub's stderr diagnostics removed) so exec-style callers see only the script's own output; the motivating example works with no package require boilerplate: piping `dev projects.work *<name>*` to `<punkexe> script` emits the matching-project table and exits 0, because the script interp carries the default punk shell module/alias environment.

4
GOALS.md

@ -113,10 +113,6 @@ Detail: goals/G-010-subshell-tree-navigation.md
Scope: src/modules/punk/console-999999.0a1.0.tm; src/modules/opunk/console-999999.0a1.0.tm; src/modules/punk/repl-999999.0a1.0.tm
Detail: goals/G-011-console-stderr-semantics.md
### G-012 [proposed] Template system: inert VCS-config payloads and explicit layout refresh
Scope: src/project_layouts/; src/make.tcl; src/modules/punk/mix/ (layout instantiation); fauxlink module (bootsupport 0.2.0 - promoted if chosen as mechanism)
Detail: goals/G-012-template-payload-safety.md
### G-013 [proposed] Raw mode as the repl's default input mode
Scope: src/modules/punk/repl-999999.0a1.0.tm; src/modules/punk/console-999999.0a1.0.tm
Detail: goals/G-013-raw-mode-default.md

8
goals/G-027-derived-project-pull-updates.md

@ -117,8 +117,12 @@ What a pull may touch splits by ownership, not by file type:
## Notes
- Related: G-012 (inert template payloads and the make.tcl template-refresh
step feed what a pull delivers), G-016/G-017 (project discovery for
- Related: G-012 (achieved 2026-08-01 - see goals/archive/G-012-template-payload-safety.md;
its inert template payloads and the make.tcl template-refresh step feed what a pull
delivers. G-012 also recorded that AGENTS.md layout payloads - when they arrive per this
goal's documentation-ownership design - are the same live-config hazard class and need
the same inert-storage + materialize-at-generation treatment as the .gitignore payloads),
G-016/G-017 (project discovery for
re-locating a moved origin), G-026 (clean-checkout policy on the source),
G-005/G-006 (remote artifact channels if remote pull proceeds).
- The 2025-04 tomlish checkin "update bootsupport and make.tcl from punkshell"

5
goals/G-031-componentized-kit-boot.md

@ -55,7 +55,8 @@ permanently own - a thousand lines of boot code.
- Boot core carries its own version/identity so a kit can report the boot
vintage it embeds (G-025 stamping is the natural vehicle).
- Layout skeletons follow through the established sync channels (custom/
_project sync step now; G-012/G-027 for the rest) - noted in completion
_project sync step now; G-012 (achieved 2026-08-01 - see
goals/archive/G-012-template-payload-safety.md)/G-027 for the rest) - noted in completion
reports, never hand-synced.
## Alternatives considered
@ -78,7 +79,7 @@ permanently own - a thousand lines of boot code.
script subcommand's behaviour.
- Related: G-027 (ownership + pull channel this is designed for), G-025
(boot-core vintage reporting), G-032 (punk::args-defined dispatch built on
the registration model), G-012 (layout payload hygiene), G-018 (plain tclsh
the registration model), G-012 (layout payload hygiene - achieved 2026-08-01), G-018 (plain tclsh
kits deliberately carry NO punk boot layer - out of scope here).
- G-033 (archived) recorded a fork-drift datapoint for this goal: the proj:
scope prefix and its `::punkboot::proj_root_find` walk-up landed in

2
goals/G-043-subshell-definition-plugins.md

@ -106,7 +106,7 @@ Phases:
punk::cap pkg_unregister "destroy api objects?" review; silent unhandled-capability
declarations (punk.isbogus); hardcoded _multivendor whitelist in the templates handler
(not this goal's to fix - noted for the templates/layout update-story work under
G-012/G-027).
G-012 (achieved 2026-08-01 - see goals/archive/G-012-template-payload-safety.md)/G-027).
- The templates handler's registration-time vs call-time path resolution split (and its
documented staleness edge for long-running shells) is the precedent for deciding when
a punk.subshell declaration's resources are resolved.

3
goals/G-047-declared-primary-vcs.md

@ -37,7 +37,8 @@ The original G-047 draft (2026-07-08) declared a single team-wide primary with t
- Layout templates seed `vcs = "fossil"` as the generated-project default plus the punkproject.local.toml ignore rules in both ignore systems.
- Validation of the declared field against the VCS systems actually present (make.tcl / project.new) is follow-on work, deliberately out of scope.
- G-012 relationship (recorded 2026-07-24 after overlap review; neither file previously
referenced the other): this goal seeds template payload (the vcs default plus the
referenced the other; G-012 achieved 2026-08-01 - see goals/archive/G-012-template-payload-safety.md):
this goal seeds template payload (the vcs default plus the
ignore rules) - G-012's inert-storage plus explicit-refresh machinery is how such
layout payload propagates; seeding rides its refresh step, never hand-syncs.
- G-065 relationship: its vendor manifest schema shares punkproject.toml-or-sibling

2
goals/G-086-light-project-layout.md

@ -13,4 +13,4 @@ serversecretsync (c:/repo/jn/serversecretsync) was seeded 2026-07-14 by hand rat
- Default mechanism (user decision 2026-07-14): vendor punk::libunknown + punk::packagepreference into the light layout as the bootstrap minimum, so generated projects get punkshell's package-resolution control rather than relying on bare path ordering alone. The seeded preamble stays small: project path setup plus the bootstrap requires, before anything else runs.
- Pure-Tcl path ordering remains the inner layer the bootstrap itself depends on - the preamble must be correct before any package require, including the requires that load the bootstrap modules themselves.
- Related goals: G-012 (template payload safety - inert VCS-config payloads apply to this layout too), G-027 (derived-project pull updates - how light projects receive infrastructure refreshes), G-035 (mixed .tm/pkgIndex provision characterization backing libunknown/packagepreference behaviour), G-029 (packaged test modules - the light harness should stay compatible with that direction).
- Related goals: G-012 (achieved 2026-08-01 - see goals/archive/G-012-template-payload-safety.md; template payload safety - its inert VCS-config payload treatment applies to this layout too), G-027 (derived-project pull updates - how light projects receive infrastructure refreshes), G-035 (mixed .tm/pkgIndex provision characterization backing libunknown/packagepreference behaviour), G-029 (packaged test modules - the light harness should stay compatible with that direction).

2
goals/G-136-generated-project-icon-seeding.md

@ -89,7 +89,7 @@ unbranded so it reads "replace me" rather than as a product identity.
- Related: G-086 - interaction. Whether the light layout declares an icon slot is
decided there; this goal's acceptance is written so a layout may decline
without that counting as a failure.
- Related: G-012 - sibling surface. The overwrite-on-regeneration behaviour is
- Related: G-012 (achieved 2026-08-01 - see goals/archive/G-012-template-payload-safety.md) - sibling surface. The overwrite-on-regeneration behaviour is
the same question as its explicit layout-refresh semantics.
- The placeholder's geometry carries two constraints that are easy to undo by
accident, recorded in `src/assets/logo/AGENTS.md`: bezel and crest are ONE

83
goals/G-012-template-payload-safety.md → goals/archive/G-012-template-payload-safety.md

@ -1,6 +1,6 @@
# G-012 Template system: inert VCS-config payloads and explicit layout refresh
Status: proposed
Status: achieved 2026-08-01
Scope: src/project_layouts/; src/make.tcl; src/modules/punk/mix/ (layout instantiation); fauxlink module (bootsupport 0.2.0 - promoted if chosen as mechanism)
Goal: project layouts carry no live nested VCS-config files - template .gitignore payloads are stored inert (renamed, or fauxlink-encoded) and materialized at project generation - and src/make.tcl has an explicit punkcheck-tracked step that refreshes layout payloads from their canonical sources.
Acceptance: a scan of src/project_layouts finds no file named .gitignore, and git check-ignore --no-index over every layout file matches only root-.gitignore rules (nested rules provably inert); a project generated from each affected layout receives a working .gitignore whose content matches the canonical payload/target; after editing a canonical source (e.g. root .gitignore), the make.tcl template-refresh step updates the derived layout payloads (punkcheck-tracked), covering vendor/punk layouts as well as custom/_project; the previously hidden template files (layout READMEs, vendored TODO-class files) remain git-tracked without per-file force-add exceptions.
@ -32,6 +32,20 @@ payload drift is unmanaged.
## Approach
**Mechanism decision (recorded 2026-08-01, implementation start): neutral rename.**
Payloads are stored under an inert name (`gitignore.in`) and materialized to their live
name (`.gitignore`) at generation. Chosen over fauxlink because: (a) fauxlink's model in
this codebase is zero-byte filename-as-pointer (layout refs) - real authored payload
content does not fit it; (b) a fauxlink target flattens every layout's payload to one
canonical file, removing the possibility of per-layout canonical payloads, and pointing
at the out-of-store repo-root `.gitignore` would need a new resolution convention;
(c) rename is dependency-free and the layout system already has a single materialization
funnel (`layout_materialize`) where a small explicit rename map (extensible to future
live-config payloads such as `.gitattributes`) hooks in cleanly. The four vendor/punk
`.gitignore` payloads are treated as drifted mirrors of the canonical root `.gitignore`
(the goal context's "essentially a copy") - the make.tcl template-refresh step rederives
them from root, so the vintage variants are eliminated rather than preserved.
1. **Store VCS-config payloads inert.** Two candidate mechanisms; the choice is an
implementation decision to be recorded here when made:
- *Neutral rename*: payload stored as e.g. `_gitignore` or `gitignore.in`; layout
@ -67,6 +81,69 @@ payload drift is unmanaged.
- **Do nothing on the git side (fossil sees the files)** - rejected: git is the day-to-day VCS;
a mirror having the truer view is a symptom, not a solution.
## Progress
Landed 2026-08-01 (activated and implemented in one session at user request):
- **Inert storage** - the four vendor/punk `.gitignore` payloads renamed to `gitignore.in`;
the `custom/punk/othersample@sample-0.1/.gitignore.anti` marker renamed to
`gitignore.in.anti` (.anti markers target store names). No file named `.gitignore`
remains under src/project_layouts or in the modpod payload source folder.
- **Materialization** - punk::mix::commandset::layout 0.3.0: new
`lib::layout_materialize_renames` map ({gitignore.in .gitignore}) with private helpers
`_find_inert_payload_relpaths`/`_apply_materialize_renames`; `layout_stage_chain` renames
inert payloads to live names after composing all layers (new `materialized` return key);
`layout_materialize` withholds the in-place fast path from inert-payload folders so the
rename only ever happens in staging. Both store-name and materialized-name presence at
one location errors. Characterization: src/tests/modules/punk/mix/testsuites/layout/
materialize.test (+5 tests, 25/25; full punk/mix subtree 71 pass / 1 known skip).
- **Template refresh** - make.tcl's thin-layout sync (the `sync_layouts` make-step recorded
in src/project_layouts/.punkcheck) now syncs the canonical REPO ROOT .gitignore into every
vendor/punk layout as `gitignore.in` alongside the existing boot-script/manifest pairs
(which remain gated on the layout carrying src/make.tcl); workflow_text DIAGRAM 1b +
release step 9 updated. The four payloads are byte-identical mirrors of root - the vintage
variants are eliminated as drift per this goal's "essentially a copy" framing.
- **Modpod path** - the store->modpod sync carries `gitignore.in` and its prune removed the
stale modpod `.gitignore`; the rebuilt templates-0.2.0.tm was verified to carry the
byte-identical payload. (The kit-baked copy inside punk91.exe etc is stale until the
routine vfscommonupdate + bake refresh - noted, not a source defect.)
Verification evidence (acceptance clause by clause):
1. "scan of src/project_layouts finds no file named .gitignore" - VERIFIED (recursive force
listing; also none in the modpod payload source folder).
2. "git check-ignore --no-index over every layout file matches only root-.gitignore rules" -
VERIFIED: full sweep matched exactly one file, src/project_layouts/.punkcheck, by the
ROOT rule `*.punkcheck`; no nested rule exists to fire.
3. "project generated from each affected layout receives a working .gitignore whose content
matches the canonical payload" - VERIFIED end-to-end (punk91 src script driver, FOSSIL_HOME
diverted): generated projects from punk.basic, punk.minimal, punk.project and
punk.sample-0.1 each received `.gitignore` byte-identical to root; punk.othersample (the
.anti deletion fixture) received none, by design. Generation from the module-carried
layout (punk.project#2) returns old-variant content only from the stale kit-baked
templates module; the freshly built templates-0.2.0.tm carries the byte-identical payload
(verified by direct member extraction).
4. "editing a canonical source updates the derived payloads (punkcheck-tracked), covering
vendor/punk" - VERIFIED both directions: a transient marker line appended to root
.gitignore propagated to all four payloads on `make.tcl libs` (punkcheck-recorded
"PROJECT LAYOUT update" events) and was removed again after reverting root. **Stale
clause:** "...as well as custom/_project" - the _project pseudo-vendor store level was
retired for layouts by G-087 stage 5 (2026-07-19, after this goal was drafted 2026-07-06):
no custom/_project layout exists to cover, and custom-tier payloads are authored
overrides (not derived mirrors) by design. Flagged for the user at completion - the
refresh covers every layout under vendor/punk, which is the live reading of the clause.
5. "previously hidden template files remain git-tracked without per-file force-add
exceptions" - VERIFIED: the fc1c474c README files (project-0.1/{bin,lib,modules}/
README.md, bin/runtime/README.md) are tracked and match no ignore rule; the previously
invisible modpod-copy README trees now appear as ordinary (trackable) files. The
accidental-ignore class is structurally impossible: no nested .gitignore exists to fire.
Remaining manual items: none for the goal itself. The stale-kit refresh (vfscommonupdate +
bake) is routine build maintenance. The binary-glob/fossil-commit note from G-087 (generated
projects' trailing fossil commit aborts on binary payload - layouts ship no binary-glob) is
a different problem class (missing fossil-side setting, not a live-config hazard) and stays
out of this goal's acceptance - candidate follow-on goal.
## Notes
- Affected today: `src/project_layouts/vendor/punk/{basic,minimal,project-0.1,sample-0.1}/.gitignore`.
@ -97,6 +174,10 @@ payload drift is unmanaged.
.tm modules) because thin layouts ship no binary-glob settings - this goal's
inert VCS-config payload treatment is the designated fix. Layouts without
binaries (sample/derived fixtures) commit cleanly.
- Related (activation survey 2026-08-01, goals_xref score G-012): G-086 (light layout's
VCS-config payloads get the same inert treatment), G-031 (thin-main skeleton propagates
via this goal's refresh step), G-136 (icon seeding's overwrite-on-regeneration rides the
same refresh semantics) - all three already point here; back-pointers recorded at activation.
- G-047 relationship (recorded 2026-07-24 after overlap review): G-047 seeds
punkproject.toml workflow defaults plus ignore rules into layout payload - a new
payload class for this goal's inert-storage plus refresh machinery to carry; the

2
punkproject.toml

@ -1,6 +1,6 @@
[project]
name = "punkshell"
version = "0.39.4"
version = "0.39.5"
license = "BSD-2-Clause"
url = "https://www.gitea1.intx.com.au/jn/punkshell"
#packager: declared identity for published artifacts (declarative, not proof -

2
src/AGENTS.md

@ -70,7 +70,7 @@ 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 + its braced `punk::args::define` block (+ 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 the thin-layout sync step in any `make.tcl modules`/`libs`/`packages`/`bakehouse` run - 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 braced file-style blocks using the G-045 authoring mechanisms — `-&` record continuations, tstr placeholders pulling SUMMARIES/HELPTEXTS and the shared `OPT_*` option fragments, explicit `@form -synopsis` — with `-help` bodies expanding as deferred display fields so the HELPTEXTS block indentation deliberately renders as a centred Description (no `@normalize` re-basing); 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.
- 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 + the inert `gitignore.in` payload copies refreshed from the root `.gitignore` - G-012), 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.
- **Boot-precondition gate (G-125).** A bake refuses any kit whose merged `.vfs` supplies no tcl library: the kit is listed under `FAILED KITS` with a reason naming the cause, and NOTHING is written - no `src/_build/<kit>`, no deploy, and the previously deployed `bin/<kit>` is left byte-identical. This is deliberately a failure rather than a warning: the deploy step deletes the old kit before copying the new one, so the pre-gate behaviour replaced a working shell with an artifact that died at startup with `application-specific initialization failed: Cannot find a usable init.tcl`. The check is structural and non-executing (so it covers cross-target kits too): `init.tcl` in `tcl_library/`, `lib/tcl<major>.<minor>/` or `tcl<major>.<minor>/` - the third for runtimes whose archive mounts at the executable's own path rather than `//zipfs:/app`, so `[info library]` is `<exe>/tcl8.6` (the androwish/undroidwish zipfs backport for 8.6 does this) - plus one companion file a real Tcl library carries beside it (`tm.tcl`, `package.tcl`, `auto.tcl`, `clock.tcl`, `history.tcl`, `word.tcl`) or an `encoding/` directory - without that second test the `lib/BWidget1.10.1/init.tcl` every punkshell kit carries would answer for a tcl library it cannot provide. It reads the MERGED tree rather than asking whether extraction ran, so a `.vfs` that legitimately supplies its own tcl library (`src/vfs/punk8_statictwapi.vfs`, `src/vfs/punk9test.vfs`) builds and deploys unchanged. **Remedy when a kit is refused:** the usual cause is that the runtime's own payload could not be extracted - read the recapped `BUILD-WARNING` naming what was tried, then either fix extraction (a driving tclsh with no zipfs needs punk::zip >= 0.2.0 in bootsupport - see the extraction bullet above), point the kit at a runtime that carries a payload, or have the kit's `.vfs` supply a tcl library itself. The predicate is `punkboot::utils::vfs_boot_library_report` called through a guarded require, exactly as the dirty-src provenance check is: a stale or missing bootsupport snapshot degrades the gate to a `NOTE` rather than failing every kit. `tclsh src/make.tcl check` reports whether the gate is `ACTIVE` or `UNAVAILABLE`.
- **Payload/target consistency checks (G-133), both ADVISORY** - warnings are recapped `BUILD-WARNING`s and the kit still builds and deploys (unlike the G-125 gate). (a) **Binary-arch scan**, at the same post-merge seam as the gate: every binary library (`*.dll`/`*.so`/`*.dylib`) in the merged tree is classified by header bytes (`punkboot::utils::binary_arch_classify` - PE Machine field, ELF e_machine, Mach-O incl. universal; unclassifiable content is honestly `unknown` and never warned about) against the kit's target platform. Structural and non-executing, so it runs for cross-target kits too (measured ~30-140ms per kit, avg ~93ms across the 12 assembled trees). Libraries under a platform-discriminated subdir - canonical `<os>-<cpu>` platform-dir names, the universal `macosx` folder, or a recognised vendor spelling (`win-x64` etc; the lists are namespace variables in `punkboot::utils`) - are exempt: multi-arch payloads are legitimate there. First real sweep (2026-07-27) found a true latent instance: `zint.dll` 2.13.0 in `src/vfs/punk8win.vfs/lib_tcl8/` is 32-bit (i386) and can never load in the x64 tcl8 kits (punk86/punkbi/punksys) that carry it - those bakes warn until the payload is fixed. (b) **Smoke-require probe**: a kit declaring packages in `src/runtime/mapvfs.toml` (`smokerequire` key - see `src/runtime/AGENTS.md`) has each one plain-`package require`d INSIDE the freshly built artifact via its tclsh subcommand, which exits cleanly even on runtimes whose full repl teardown is fragile. This is the only check that observes actual package RESOLUTION - the punkluck86 2026-07-27 incident class (a higher-versioned wrong-arch Thread shadowing the runtime's working copy on plain require) is invisible to structural checks because the working file exists too. Failures are recapped naming kit, package and the actual error; cross-target kits skip with a stated reason; undeclared kits run nothing new; the probe only runs when the kit actually rebuilds. **What the checks do NOT guarantee:** statically linked packages, pure-tcl packages whose binary dependency lives elsewhere, and version-preference outcomes are visible only to the smoke probe - and only for the declared package set; the arch scan proves nothing about loadability beyond architecture, and an `unknown` classification is silence, not a pass. Both degrade to a NOTE when the bootsupport `punkboot::utils` snapshot is stale (same guarded require as the G-125 gate); `tclsh src/make.tcl check` reports the scan's ACTIVE/UNAVAILABLE state and the currently declared smoke-require kits.
- **Kit offset-style pin (G-134), ADVISORY** - at the same seam family, every ASSEMBLED kit image is probed with `punk::zip::archive_info` (via `punkboot::utils::kit_offsetstyle_report`, same guarded-require degradation to a NOTE; `make.tcl check` reports ACTIVE/UNAVAILABLE): a FILE-relative attached zip payload earns a recapped `BUILD-WARNING` naming the kit, because the pipeline emits ARCHIVE-relative by construction and the G-128 stamper refuses file-relative by default - a warning here means an assembly path regressed the output convention. **What the pin does NOT cover:** `plain` and `none` results (a bare zip, or no attached zip at all - the metakit kit shape, and any unreadable input) are silence, never warnings - the pin asserts the offset CONVENTION of an attached zip, not the presence of one; and it asserts only what make.tcl's own assembly paths EMIT - `punk::zip::mkzip -offsettype file` (and modpod's equivalent) remain available for deliberate manual creation of file-relative artifacts, and file-relative INPUT artifacts remain tolerated by the G-124 reader and normalized by any re-bake.

46
src/make.tcl

@ -2674,7 +2674,8 @@ RELEASE SEQUENCE (the developer order that satisfies the staleness + provenance
(7) build tclsh src/make.tcl vfscommonupdate -confirm 0
(8) build tclsh src/make.tcl bake -confirm 0 kits; close running kit shells first [K5]
(9) commit tracked build outputs src/bootsupport, thin-layout script/
manifest copies (src/project_layouts +
manifest/gitignore.in payload copies
(src/project_layouts +
templates modpod payload),
src/vfs/_vfscommon.vfs
@ -2712,9 +2713,11 @@ DIAGRAM 1 - DATA FLOW: SOURCE -> BUILT PACKAGES -> PROPAGATION TARGETS
DIAGRAM 1b - THIN-LAYOUT SYNC (part of modules/libs/packages/bakehouse runs)
----------------------------------------------------------------------------
src/make.tcl src/build.tcl src/bootsupport/modules*/include_modules.config
.gitignore src/make.tcl src/build.tcl src/bootsupport/modules*/include_modules.config
|
| punkcheck-recorded copies into each vendor layout carrying src/make.tcl
| punkcheck-recorded copies: root .gitignore -> every vendor layout's inert
| gitignore.in payload (G-012; materialized to .gitignore at generation),
| scripts/manifests -> each vendor layout carrying src/make.tcl
v
src/project_layouts/vendor/punk/<layout>/src/... (thin layouts: scripts, docs,
| manifests - no module snapshots)
@ -6634,9 +6637,11 @@ if {$::punkboot::command in {bakehouse packages libs}} {
if {$::punkboot::command in {bakehouse packages modules libs}} {
########################################################
#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.
#sync the canonical root .gitignore into every vendor layout as the inert gitignore.in
#payload (G-012 - materialized to .gitignore at project generation), sync boot scripts
#(build.tcl, make.tcl) and bootsupport include_modules.config manifests into the thin
#layouts 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 {*}{
@ -6650,10 +6655,6 @@ if {$::punkboot::command in {bakehouse 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
}]
@ -6662,16 +6663,27 @@ if {$::punkboot::command in {bakehouse packages modules libs}} {
$tpl_installer set_source_target $sourcefolder $layoutbase/$layoutname
set tpl_event [$tpl_installer start_event $config]
#----------
#G-012: every layout's gitignore payload is a derived mirror of the canonical root
#.gitignore, stored inert as gitignore.in (materialized to .gitignore in the
#generated project by punk::mix::commandset::layout::lib::layout_materialize).
#Canonical source is the REPO root .gitignore ($projectroot, not $sourcefolder).
set pairs [list {*}{
} [list $sourcefolder/build.tcl $layoutbase/$layoutname/src/build.tcl] {*}{
} [list $sourcefolder/make.tcl $layoutbase/$layoutname/src/make.tcl] {*}{
} [list $projectroot/.gitignore $layoutbase/$layoutname/gitignore.in] {*}{
}
]
#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]
if {[file exists $layoutbase/$layoutname/src/make.tcl]} {
#layout carries boot scripts - sync them too (layouts that don't, e.g minimal,
#sample-0.1, receive only the gitignore payload)
lappend pairs {*}{
} [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]
}
}
}

2
src/modules/punk/AGENTS.md

@ -40,7 +40,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).
- `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). Like the store, its `.gitignore` payload is held inert as `gitignore.in` and materialized to `.gitignore` at generation (G-012).
- 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

53
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/.gitignore vendored

@ -1,53 +0,0 @@
/bin/*
!/bin/*.cmd
!/bin/*.kit
!/bin/*.tcl
!/bin/*.sh
!/bin/*.bash
/lib/
#The directory for compiled/built Tcl modules
/modules/
/vendorbuilds/
#Temporary files e.g from tests
/tmp/
/logs/
**/_aside/
**/_build/
scratch*
#Built documentation
/html/
/man/
/md/
/doc/
/test*
#Built tclkits (if any)
*.exe
#ignore fossil database files (but keep .fossil-settings and .fossil-custom in repository even if fossil not being used at your site)
_FOSSIL_
.fos
.fslckout
*.fossil
#miscellaneous editor files etc
*.swp
*.log
*.punkcheck
todo.txt
zig-cache/
zig-out/
/release/
/debug/
/build/
/build-*/
/docgen_tmp/

3
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/bin/README.md vendored

@ -0,0 +1,3 @@
Project scripts and executables
These files usually shouldn't be modified directly as they are built from the files in the src folder

26
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/bin/runtime/README.md vendored

@ -0,0 +1,26 @@
runtime Tcl Executables are installed here under the appropriate os_architecture folder.
e.g
windows 64bit binaries under win32-x86_64
linux 64 bit binaries under linux-x86_64
The bin/punk-runtime.cmd cross-platform script can be used to download prebuilt Tcl binaries.
e.g ./bin/punk-runtime.cmd fetch
This will detect the platform and architecture, creating the containing folder as necessary.
To see existing installed runtimes:
./bin/punk-runtime.cmd list
Or to compare local runtimes with those available on the artifact server:
./bin/punk-runtime.cmd list -remote
An installed runtime can then be used to call the src/make.tcl file.
e.g - initial check
./bin/punk-runtime.cmd run src/make.tcl check
e.g - to build (or just copy with latest version number from src if build not required) modules to projectdir/modules
./bin/punk-runtime.cmd run src/make.tcl modules
for libs & modules
./bin/punk-runtime.cmd run src/make.tcl packages

108
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/gitignore.in vendored

@ -0,0 +1,108 @@
#while bin is primarily an output - some items here are for bootstrapping - so while they aren't the source - we install and source-control them.
/bin/*
!/bin/AGENTS.md
!/bin/*.cmd
!/bin/*.kit
!/bin/*.tcl
!/bin/*.sh
!/bin/*.bash
#The directories for compiled/built Tcl packages (libs and modules)
#These are purely 'output' folders - no need to track
/lib/
/lib_tcl8/
/lib_tcl9/
/modules/
/modules_tcl8/
/modules_tcl9/
#Temporary files e.g from tests
/tmp/
.vscode
#claude harness config: track the shared project settings (hooks/permissions) and project skills,
#ignore everything else (session-local settings etc)
.claude/*
!.claude/settings.json
!.claude/skills/
.omo
/logs/
**/_aside/
**/_build/
scratch*
#Built documentation
/html/
/man/
/md/
/doc/
/test*
/src/testdata
/src/scriptapps/test_*
#G-115 demonstration kit payload - materialized from the src/vfs/punkdeclare.vfs.toml
#declaration (vendor tree + consent-gated bin/packages tier); reproducing it from the
#declaration is the point, so it is never committed
/src/vfs/punkdeclare.vfs/lib_tcl9/
#G-127 per-platform payload staging - materialized from %platform% entries in
#src/vfs/<name>.vfs.toml (one subtree per consuming kit target) and selected into
#each kit's merged image at bake time; regenerable staging, never committed
/src/vfs/*.vfs/_targets/
#review
/src/docgen
#local developer notes
/localdoc/
#Built tclkits (if any)
*.exe
#subfolder for temporary reference files - not source controlled
#we may use git clone to get a copy of the reference files - but we don't want to track them in the repo
TEMP_REFERENCE
#ignore fossil database files (but keep .fossil-settings and .fossil-custom in repository even if fossil not being used at your site)
_FOSSIL_
.fos
.fslckout
*.fossil
#fossil-generated checkout artifacts - regenerated on every fossil checkin/checkout per the
#versioned 'manifest' setting (value 'rut'); fossil auto-manages them and they must not be
#git-tracked. Anchored: nested manifest-named files elsewhere are real content.
#See .fossil-settings/AGENTS.md for the git+fossil coexistence contract.
/manifest
/manifest.uuid
/manifest.tags
#miscellaneous editor files etc
*.swp
*.log
*.punkcheck
todo.txt
zig-cache/
.zig-cache/
zig-out/
zig-pkg/
/release/
/debug/
/build/
/build-*/
/docgen_tmp/
/~/
#Node
/node_modules/
/package-lock.json
/package.json

6
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/lib/README.md vendored

@ -0,0 +1,6 @@
Tcl Libraries which use the pkgIndex.tcl system to load.
Each library will usually have its own folder in the lib folder.
Some groups of libraries may have a deeper structure - usually with a pkIndex.tcl at the top level,
and individual pkgIndex.tcl files further down.
These files shouldn't be modified directly as they are built or copied from the files in the src folder

9
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/modules/README.md vendored

@ -0,0 +1,9 @@
Tcl Modules
These are packaged as .tm file extension and the loadable locations are controlled by functions such as tcl::tm::path add|list|remove
Via the make process, the modules may come from from src/vendormodules - being modules not generally developed within the current project.
They are built from src/modules for those that are directly part of the project.
These files shouldn't be modified directly as they are built or copied from the files in the src folder structure.

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

@ -2674,7 +2674,8 @@ RELEASE SEQUENCE (the developer order that satisfies the staleness + provenance
(7) build tclsh src/make.tcl vfscommonupdate -confirm 0
(8) build tclsh src/make.tcl bake -confirm 0 kits; close running kit shells first [K5]
(9) commit tracked build outputs src/bootsupport, thin-layout script/
manifest copies (src/project_layouts +
manifest/gitignore.in payload copies
(src/project_layouts +
templates modpod payload),
src/vfs/_vfscommon.vfs
@ -2712,9 +2713,11 @@ DIAGRAM 1 - DATA FLOW: SOURCE -> BUILT PACKAGES -> PROPAGATION TARGETS
DIAGRAM 1b - THIN-LAYOUT SYNC (part of modules/libs/packages/bakehouse runs)
----------------------------------------------------------------------------
src/make.tcl src/build.tcl src/bootsupport/modules*/include_modules.config
.gitignore src/make.tcl src/build.tcl src/bootsupport/modules*/include_modules.config
|
| punkcheck-recorded copies into each vendor layout carrying src/make.tcl
| punkcheck-recorded copies: root .gitignore -> every vendor layout's inert
| gitignore.in payload (G-012; materialized to .gitignore at generation),
| scripts/manifests -> each vendor layout carrying src/make.tcl
v
src/project_layouts/vendor/punk/<layout>/src/... (thin layouts: scripts, docs,
| manifests - no module snapshots)
@ -6634,9 +6637,11 @@ if {$::punkboot::command in {bakehouse packages libs}} {
if {$::punkboot::command in {bakehouse packages modules libs}} {
########################################################
#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.
#sync the canonical root .gitignore into every vendor layout as the inert gitignore.in
#payload (G-012 - materialized to .gitignore at project generation), sync boot scripts
#(build.tcl, make.tcl) and bootsupport include_modules.config manifests into the thin
#layouts 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 {*}{
@ -6650,10 +6655,6 @@ if {$::punkboot::command in {bakehouse 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
}]
@ -6662,16 +6663,27 @@ if {$::punkboot::command in {bakehouse packages modules libs}} {
$tpl_installer set_source_target $sourcefolder $layoutbase/$layoutname
set tpl_event [$tpl_installer start_event $config]
#----------
#G-012: every layout's gitignore payload is a derived mirror of the canonical root
#.gitignore, stored inert as gitignore.in (materialized to .gitignore in the
#generated project by punk::mix::commandset::layout::lib::layout_materialize).
#Canonical source is the REPO root .gitignore ($projectroot, not $sourcefolder).
set pairs [list {*}{
} [list $sourcefolder/build.tcl $layoutbase/$layoutname/src/build.tcl] {*}{
} [list $sourcefolder/make.tcl $layoutbase/$layoutname/src/make.tcl] {*}{
} [list $projectroot/.gitignore $layoutbase/$layoutname/gitignore.in] {*}{
}
]
#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]
if {[file exists $layoutbase/$layoutname/src/make.tcl]} {
#layout carries boot scripts - sync them too (layouts that don't, e.g minimal,
#sample-0.1, receive only the gitignore payload)
lappend pairs {*}{
} [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]
}
}
}

150
src/modules/punk/mix/commandset/layout-999999.0a1.0.tm

@ -232,6 +232,19 @@ namespace eval punk::mix::commandset::layout {
# layout_materialize is the entry point; project.new copies from its result path.
# -- --- --- --- --- --- --- --- --- --- ---
# -- --- --- --- --- --- --- --- --- --- ---
# G-012: inert VCS-config payloads. A payload file stored under a tail name that is a
# key of materialize_renames would be LIVE configuration under its real name (git
# treats any .gitignore in the tree as live), so layouts store it under the inert key
# name and it is renamed to the materialized value name when the layout's effective
# content is composed (layout_stage_chain). layout_materialize withholds the in-place
# fast path from folders carrying inert payloads, so the rename only ever happens in
# the disposable staging dir - never in the store. .anti markers target STORE names
# (renames run after every layer is composed). The map is the extension point for
# future live-config payload classes (e.g .gitattributes).
# -- --- --- --- --- --- --- --- --- --- ---
variable materialize_renames {gitignore.in .gitignore}
punk::args::define {
@id -id ::punk::mix::commandset::layout::lib::layout_resolve_chain
@cmd -name punk::mix::commandset::layout::lib::layout_resolve_chain\
@ -334,7 +347,11 @@ namespace eval punk::mix::commandset::layout {
deleting it.
Returns a dict: path (stagingdir), chain, deletions (relative paths removed
by .anti markers), copied (number of files copied)."
by .anti markers), copied (number of files copied), materialized (list of
{from to} relative-path pairs of inert VCS-config payloads renamed to
their live names - G-012: gitignore.in stored inert in the store becomes
.gitignore in the composed folder; .anti markers always target store
names, since the rename runs after every layer is composed)."
@values -min 2 -max 2
chain -type list -help\
"Ordered list of layout source folders, lowest priority first
@ -376,7 +393,9 @@ namespace eval punk::mix::commandset::layout {
}
incr copied [_merge_copy_into $sourcefolder $stagingdir]
}
return [dict create path $stagingdir chain $chain deletions $deletions copied $copied]
#G-012: with every layer composed under store names, rename inert payloads to their live names
set materialized [_apply_materialize_renames $stagingdir]
return [dict create path $stagingdir chain $chain deletions $deletions copied $copied materialized $materialized]
}
punk::args::define {
@ -388,12 +407,15 @@ namespace eval punk::mix::commandset::layout {
"Resolves the named layout's overlay chain (layout_resolve_chain) within its
store and returns a folder holding the effective layout content.
When the chain is a single folder containing no .anti markers (the common
case - e.g the thin punk.project layout, or any module-carried layout) that
folder is returned as-is (staged 0) and no copying occurs. Otherwise the
chain is composed into a staging directory (layout_stage_chain) and the
staged folder is returned (staged 1) - the caller may delete it when done
(the default location is deterministic, so a later call recomposes it).
When the chain is a single folder containing no .anti markers AND no inert
VCS-config payloads (G-012: files named as keys of
layout_materialize_renames, e.g gitignore.in) that folder is returned
as-is (staged 0) and no copying occurs. Otherwise the chain is composed
into a staging directory (layout_stage_chain) and the staged folder is
returned (staged 1) - the caller may delete it when done (the default
location is deterministic, so a later call recomposes it). Inert payloads
always force staging: their rename to live names (.gitignore) must never
touch the store.
Layout folders whose store position does not follow the
<storeroot>/{vendor,custom}/<vendor>/<layoutfolder> shape (including any
@ -406,7 +428,9 @@ namespace eval punk::mix::commandset::layout {
0 if it is the store folder itself
chain - ordered contributing folders, lowest priority first
deletions - relative paths removed by .anti markers (staged only)
copied - number of files copied into staging (staged only)"
copied - number of files copied into staging (staged only)
materialized - {from to} relative-path pairs of inert payloads renamed
to live names (staged only; G-012)"
@leaders -min 1 -max 1
layoutname -type string -optional 0 -help\
"Visible layout name as shown by 'dev project.layouts' (e.g punk.project)"
@ -452,16 +476,21 @@ namespace eval punk::mix::commandset::layout {
}
}
}
#fast path - a single-source chain with no .anti markers is usable in place
if {[llength $chain] == 1 && ![llength [_find_anti_relpaths [lindex $chain 0]]]} {
return [dict create path [lindex $chain 0] staged 0 chain $chain deletions [list] copied 0]
#fast path - a single-source chain with no .anti markers and no inert payloads is
#usable in place. Inert VCS-config payloads (G-012) force staging: their rename to
#live names must never touch the store.
if { [llength $chain] == 1
&& ![llength [_find_anti_relpaths [lindex $chain 0]]]
&& ![llength [_find_inert_payload_relpaths [lindex $chain 0]]]
} {
return [dict create path [lindex $chain 0] staged 0 chain $chain deletions [list] copied 0 materialized [list]]
}
if {$opt_stagingdir eq ""} {
set sanitized [regsub -all {[^A-Za-z0-9_.-]} $layoutname _]
set opt_stagingdir [file join [punk::lib::tempdir] punk_layout_staging $sanitized]
}
set stageinfo [layout_stage_chain $chain $opt_stagingdir]
return [dict create path [dict get $stageinfo path] staged 1 chain $chain deletions [dict get $stageinfo deletions] copied [dict get $stageinfo copied]]
return [dict create path [dict get $stageinfo path] staged 1 chain $chain deletions [dict get $stageinfo deletions] copied [dict get $stageinfo copied] materialized [dict get $stageinfo materialized]]
}
punk::args::define {
@ -531,6 +560,101 @@ namespace eval punk::mix::commandset::layout {
return $antis
}
punk::args::define {
@id -id ::punk::mix::commandset::layout::lib::layout_materialize_renames
@cmd -name punk::mix::commandset::layout::lib::layout_materialize_renames\
-summary\
"Return the inert-payload rename map applied at layout materialization"\
-help\
"Returns a dict of {inertTail materializedTail} (G-012). Layout payload
files stored under an inert tail name - so they are not live
configuration in the layout store (git never reads gitignore.in) - are
renamed to their materialized tail name (.gitignore) when a layout's
effective content is composed by layout_stage_chain. The map is the
extension point for future live-config payload classes (e.g
.gitattributes stored as gitattributes.in)."
@values -min 0 -max 0
}
proc layout_materialize_renames {} {
variable materialize_renames
return $materialize_renames
}
punk::args::define {
@id -id ::punk::mix::commandset::layout::lib::_find_inert_payload_relpaths
@cmd -name punk::mix::commandset::layout::lib::_find_inert_payload_relpaths\
-summary\
"Recursively collect inert-payload files below a layout folder"\
-help\
"Internal helper for the overlay merge primitive. Returns the relative
paths of files whose tail is an inert-payload store name (a key of
layout_materialize_renames) anywhere below basefolder."
@values -min 1 -max 2
basefolder -type string
prefix -type string -optional 1
}
proc _find_inert_payload_relpaths {basefolder {prefix ""}} {
#manual args - see PUNKARGS ::punk::mix::commandset::layout::lib::_find_inert_payload_relpaths
variable materialize_renames
if {$prefix eq ""} {
set here $basefolder
} else {
set here [file join $basefolder $prefix]
}
set found [list]
foreach entry [_dir_entries $here] {
if {$prefix eq ""} {
set rel $entry
} else {
set rel [file join $prefix $entry]
}
if {[dict exists $materialize_renames $entry]} {
lappend found $rel
} elseif {[file isdirectory [file join $here $entry]]} {
lappend found {*}[_find_inert_payload_relpaths $basefolder $rel]
}
}
return $found
}
punk::args::define {
@id -id ::punk::mix::commandset::layout::lib::_apply_materialize_renames
@cmd -name punk::mix::commandset::layout::lib::_apply_materialize_renames\
-summary\
"Rename inert-payload files in a composed folder to their live names"\
-help\
"Internal helper for the overlay merge primitive. Renames every
inert-payload file below folder (tail matching a key of
layout_materialize_renames) to its materialized tail name in place, and
returns the list of applied {from to} relative-path pairs. Errors when
both the inert name and its materialized name exist at the same location
- a layout carrying both is contradictory (the materialized name is live
VCS config and must never be store content)."
@values -min 1 -max 1
folder -type string
}
proc _apply_materialize_renames {folder} {
#manual args - see PUNKARGS ::punk::mix::commandset::layout::lib::_apply_materialize_renames
variable materialize_renames
set applied [list]
foreach relpath [_find_inert_payload_relpaths $folder] {
set newtail [dict get $materialize_renames [file tail $relpath]]
set relbase [file dirname $relpath]
if {$relbase eq "."} {
set tgtrel $newtail
} else {
set tgtrel [file join $relbase $newtail]
}
set tgt [file join $folder $tgtrel]
if {[file exists $tgt]} {
error "layout materialize: both inert payload '$relpath' and materialized name '$tgtrel' are present - the materialized name is live VCS config and must never be layout store content"
}
file rename -- [file join $folder $relpath] $tgt
lappend applied [list $relpath $tgtrel]
}
return $applied
}
punk::args::define {
@id -id ::punk::mix::commandset::layout::lib::_merge_copy_into
@cmd -name punk::mix::commandset::layout::lib::_merge_copy_into\

3
src/modules/punk/mix/commandset/layout-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-012: inert VCS-config payload materialization. New lib::layout_materialize_renames (public map of inert->materialized tail names, initially {gitignore.in .gitignore}); layout_stage_chain renames inert payloads to live names after composing all layers (new materialized key in its return dict; .anti markers keep targeting store names); layout_materialize withholds the in-place fast path from folders carrying inert payloads so the rename only ever happens in staging. New private helpers lib::_find_inert_payload_relpaths and lib::_apply_materialize_renames.
#0.2.0 - G-087 stage 4: overlay merge primitive in lib - layout_materialize (entry point returning the effective content folder; fast path returns the store folder untouched for single-source chains with no .anti markers), layout_resolve_chain (vendor-then-custom tier chain with <name>@<base> derived-layout expansion, cycle-guarded), layout_stage_chain (staging-dir composition applying .anti deletion markers; #layout_staging marker guards against clobbering non-staging dirs), plus private helpers _dir_entries/_find_anti_relpaths/_merge_copy_into. The _project pseudo-vendor participates in no chain (retired for layouts - G-087 stage 5 decision).
#0.2.0 - new lib::layoutfolder_scan_for_template_files (path-based core of layout_scan_for_template_files); layout_scan_for_template_files, layout_all_files and templatefiles.relative now operate on the overlay-materialized content rather than the primary store folder only (the staging marker file is excluded from listings/scans).
#0.1.1 - fix: no-handler warning paths in collection::references_as_dict and lib::layouts_dict used 'put' instead of 'puts' and then errored on an unset dict variable; they now warn correctly and return an empty dict. references_as_dict warning message no longer misattributes itself to layouts_dict.

35
src/project_layouts/README.md

@ -121,11 +121,36 @@ folders by punk::mix::commandset::layout::lib::layout_materialize (used by
in src/decktemplates/custom/punk/layout_refs/) for a working example of
custom-over-vendor override, a .anti deletion and derivation; semantics
detail in custom/punk/sample-0.1/extra.txt.
- Chains of one folder with no .anti markers (e.g the thin punk.project
layout, module-carried layouts) are used in place - no copying. Anything
else is composed into a disposable staging directory (deterministic
per-layout path under the system temp dir, marked with a #layout_staging
file) which project.new removes after use.
- Chains of one folder with no .anti markers and no inert VCS-config payloads
(below) are used in place - no copying. Anything else is composed into a
disposable staging directory (deterministic per-layout path under the system
temp dir, marked with a #layout_staging file) which project.new removes after use.
Inert VCS-config payloads (G-012)
---------------------------------
A payload file that would be LIVE VCS configuration under its real name must
never be stored under that name in a layout: git treats any `.gitignore`
anywhere in the tree as live config for its subtree, so a literal `.gitignore`
payload self-censors the layout in the punkshell repo (and any file the
payload ignores silently drops out of git tracking - the G-012 hazard).
- Payloads are stored under an inert tail name and materialized to the live
name when the layout's effective content is composed: `gitignore.in` becomes
`.gitignore` in the staging dir (never in the store). The rename map lives
in `punk::mix::commandset::layout::lib::layout_materialize_renames` and is
the extension point for future live-config payload classes.
- The rename runs after every overlay layer is composed, so `.anti` markers
always target STORE names: use `gitignore.in.anti` to delete an inherited
payload, never `.gitignore.anti`.
- A layout carrying both the inert and the materialized name at the same
location is contradictory and errors at materialization.
- `.fossil-settings/` payloads need no such treatment: fossil reads them only
at a checkout root, so they are inert in the store by nature.
- The vendor/punk `gitignore.in` payloads are DERIVED mirrors of the punkshell
root `.gitignore`, refreshed (punkcheck-tracked) by the make.tcl thin-layout
sync step. Edit the root `.gitignore` and rebuild (`make.tcl
modules`/`libs`/`packages`/`bakehouse`); never hand-edit the payloads.
Store structure
---------------

9
src/project_layouts/custom/punk/othersample@sample-0.1/README.md

@ -12,10 +12,11 @@ sample-0.1 (vendor/punk/sample-0.1 merged with custom/punk/sample-0.1), with
this folder's own content applied on top.
This README overrides the one inherited from the sample-0.1 chain, and the
zero-byte .gitignore.anti marker beside it deletes the .gitignore inherited
from vendor/punk/sample-0.1 - so a project generated from punk.othersample
contains this README, extra.txt from custom/punk/sample-0.1, and no
.gitignore.
zero-byte gitignore.in.anti marker beside it deletes the gitignore.in payload
inherited from vendor/punk/sample-0.1 - so a project generated from
punk.othersample contains this README, extra.txt from custom/punk/sample-0.1,
and no .gitignore. (The .gitignore payload is stored inert as gitignore.in -
G-012 - and .anti markers target store names.)
See custom/punk/sample-0.1/extra.txt for the full overlay/merge semantics.

0
src/project_layouts/custom/punk/othersample@sample-0.1/.gitignore.anti → src/project_layouts/custom/punk/othersample@sample-0.1/gitignore.in.anti

16
src/project_layouts/custom/punk/sample-0.1/extra.txt

@ -29,5 +29,17 @@ Derived layouts:
a working example (it also demonstrates a .anti deletion).
Composition happens in a disposable staging directory at generation time - the store folders
are never modified, and a layout whose chain is a single folder with no .anti markers is used
in place without any copying.
are never modified, and a layout whose chain is a single folder with no .anti markers and no
inert VCS-config payloads (below) is used in place without any copying.
Inert VCS-config payloads (G-012):
- A payload that would be LIVE VCS configuration under its real name is stored under an
inert name and materialized at generation: gitignore.in is renamed to .gitignore in the
composed staging dir (never in the store) by layout_materialize. Never store a literal
.gitignore in a layout - git would treat it as live config for the layout tree itself in
the punkshell repo (the self-censoring-template hazard G-012 exists to fix).
- .anti markers target STORE names: name gitignore.in.anti to delete an inherited
gitignore.in payload (see ../othersample@sample-0.1), never .gitignore.anti.
- The vendor/punk layouts' gitignore.in payloads are derived mirrors of the punkshell root
.gitignore, refreshed (punkcheck-tracked) by the make.tcl thin-layout sync step - edit the
root .gitignore, not the payloads.

47
src/project_layouts/vendor/punk/basic/.gitignore vendored

@ -1,47 +0,0 @@
/bin/
/lib/
#The directory for compiled/built Tcl modules
/modules/
/vendorbuilds/
#Temporary files e.g from tests
/tmp/
/logs/
**/_aside/
**/_build/
scratch*
#Built documentation
/html/
/man/
/md/
/doc/
/test*
#Built tclkits (if any)
punk*.exe
tcl*.exe
#ignore fossil database files (but keep .fossil-settings and .fossil-custom in repository even if fossil not being used at your site)
_FOSSIL_
.fos
.fslckout
*.fossil
#miscellaneous editor files etc
*.swp
todo.txt
zig-cache/
zig-out/
/release/
/debug/
/build/
/build-*/
/docgen_tmp/

108
src/project_layouts/vendor/punk/basic/gitignore.in vendored

@ -0,0 +1,108 @@
#while bin is primarily an output - some items here are for bootstrapping - so while they aren't the source - we install and source-control them.
/bin/*
!/bin/AGENTS.md
!/bin/*.cmd
!/bin/*.kit
!/bin/*.tcl
!/bin/*.sh
!/bin/*.bash
#The directories for compiled/built Tcl packages (libs and modules)
#These are purely 'output' folders - no need to track
/lib/
/lib_tcl8/
/lib_tcl9/
/modules/
/modules_tcl8/
/modules_tcl9/
#Temporary files e.g from tests
/tmp/
.vscode
#claude harness config: track the shared project settings (hooks/permissions) and project skills,
#ignore everything else (session-local settings etc)
.claude/*
!.claude/settings.json
!.claude/skills/
.omo
/logs/
**/_aside/
**/_build/
scratch*
#Built documentation
/html/
/man/
/md/
/doc/
/test*
/src/testdata
/src/scriptapps/test_*
#G-115 demonstration kit payload - materialized from the src/vfs/punkdeclare.vfs.toml
#declaration (vendor tree + consent-gated bin/packages tier); reproducing it from the
#declaration is the point, so it is never committed
/src/vfs/punkdeclare.vfs/lib_tcl9/
#G-127 per-platform payload staging - materialized from %platform% entries in
#src/vfs/<name>.vfs.toml (one subtree per consuming kit target) and selected into
#each kit's merged image at bake time; regenerable staging, never committed
/src/vfs/*.vfs/_targets/
#review
/src/docgen
#local developer notes
/localdoc/
#Built tclkits (if any)
*.exe
#subfolder for temporary reference files - not source controlled
#we may use git clone to get a copy of the reference files - but we don't want to track them in the repo
TEMP_REFERENCE
#ignore fossil database files (but keep .fossil-settings and .fossil-custom in repository even if fossil not being used at your site)
_FOSSIL_
.fos
.fslckout
*.fossil
#fossil-generated checkout artifacts - regenerated on every fossil checkin/checkout per the
#versioned 'manifest' setting (value 'rut'); fossil auto-manages them and they must not be
#git-tracked. Anchored: nested manifest-named files elsewhere are real content.
#See .fossil-settings/AGENTS.md for the git+fossil coexistence contract.
/manifest
/manifest.uuid
/manifest.tags
#miscellaneous editor files etc
*.swp
*.log
*.punkcheck
todo.txt
zig-cache/
.zig-cache/
zig-out/
zig-pkg/
/release/
/debug/
/build/
/build-*/
/docgen_tmp/
/~/
#Node
/node_modules/
/package-lock.json
/package.json

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

@ -2674,7 +2674,8 @@ RELEASE SEQUENCE (the developer order that satisfies the staleness + provenance
(7) build tclsh src/make.tcl vfscommonupdate -confirm 0
(8) build tclsh src/make.tcl bake -confirm 0 kits; close running kit shells first [K5]
(9) commit tracked build outputs src/bootsupport, thin-layout script/
manifest copies (src/project_layouts +
manifest/gitignore.in payload copies
(src/project_layouts +
templates modpod payload),
src/vfs/_vfscommon.vfs
@ -2712,9 +2713,11 @@ DIAGRAM 1 - DATA FLOW: SOURCE -> BUILT PACKAGES -> PROPAGATION TARGETS
DIAGRAM 1b - THIN-LAYOUT SYNC (part of modules/libs/packages/bakehouse runs)
----------------------------------------------------------------------------
src/make.tcl src/build.tcl src/bootsupport/modules*/include_modules.config
.gitignore src/make.tcl src/build.tcl src/bootsupport/modules*/include_modules.config
|
| punkcheck-recorded copies into each vendor layout carrying src/make.tcl
| punkcheck-recorded copies: root .gitignore -> every vendor layout's inert
| gitignore.in payload (G-012; materialized to .gitignore at generation),
| scripts/manifests -> each vendor layout carrying src/make.tcl
v
src/project_layouts/vendor/punk/<layout>/src/... (thin layouts: scripts, docs,
| manifests - no module snapshots)
@ -6634,9 +6637,11 @@ if {$::punkboot::command in {bakehouse packages libs}} {
if {$::punkboot::command in {bakehouse packages modules libs}} {
########################################################
#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.
#sync the canonical root .gitignore into every vendor layout as the inert gitignore.in
#payload (G-012 - materialized to .gitignore at project generation), sync boot scripts
#(build.tcl, make.tcl) and bootsupport include_modules.config manifests into the thin
#layouts 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 {*}{
@ -6650,10 +6655,6 @@ if {$::punkboot::command in {bakehouse 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
}]
@ -6662,16 +6663,27 @@ if {$::punkboot::command in {bakehouse packages modules libs}} {
$tpl_installer set_source_target $sourcefolder $layoutbase/$layoutname
set tpl_event [$tpl_installer start_event $config]
#----------
#G-012: every layout's gitignore payload is a derived mirror of the canonical root
#.gitignore, stored inert as gitignore.in (materialized to .gitignore in the
#generated project by punk::mix::commandset::layout::lib::layout_materialize).
#Canonical source is the REPO root .gitignore ($projectroot, not $sourcefolder).
set pairs [list {*}{
} [list $sourcefolder/build.tcl $layoutbase/$layoutname/src/build.tcl] {*}{
} [list $sourcefolder/make.tcl $layoutbase/$layoutname/src/make.tcl] {*}{
} [list $projectroot/.gitignore $layoutbase/$layoutname/gitignore.in] {*}{
}
]
#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]
if {[file exists $layoutbase/$layoutname/src/make.tcl]} {
#layout carries boot scripts - sync them too (layouts that don't, e.g minimal,
#sample-0.1, receive only the gitignore payload)
lappend pairs {*}{
} [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]
}
}
}

39
src/project_layouts/vendor/punk/minimal/.gitignore vendored

@ -1,39 +0,0 @@
/bin/
/lib/
#The directory for compiled/built Tcl modules
/modules/
/vendorbuilds/
#Temporary files e.g from tests
/tmp/
/logs/
**/_aside/
**/_build/
scratch*
#Built documentation
/html/
/man/
/md/
/doc/
/test*
#Built tclkits (if any)
punk*.exe
tcl*.exe
#ignore fossil database files (but keep .fossil-settings and .fossil-custom in repository even if fossil not being used at your site)
_FOSSIL_
.fos
.fslckout
*.fossil
#miscellaneous editor files etc
*.swp
todo.txt

108
src/project_layouts/vendor/punk/minimal/gitignore.in vendored

@ -0,0 +1,108 @@
#while bin is primarily an output - some items here are for bootstrapping - so while they aren't the source - we install and source-control them.
/bin/*
!/bin/AGENTS.md
!/bin/*.cmd
!/bin/*.kit
!/bin/*.tcl
!/bin/*.sh
!/bin/*.bash
#The directories for compiled/built Tcl packages (libs and modules)
#These are purely 'output' folders - no need to track
/lib/
/lib_tcl8/
/lib_tcl9/
/modules/
/modules_tcl8/
/modules_tcl9/
#Temporary files e.g from tests
/tmp/
.vscode
#claude harness config: track the shared project settings (hooks/permissions) and project skills,
#ignore everything else (session-local settings etc)
.claude/*
!.claude/settings.json
!.claude/skills/
.omo
/logs/
**/_aside/
**/_build/
scratch*
#Built documentation
/html/
/man/
/md/
/doc/
/test*
/src/testdata
/src/scriptapps/test_*
#G-115 demonstration kit payload - materialized from the src/vfs/punkdeclare.vfs.toml
#declaration (vendor tree + consent-gated bin/packages tier); reproducing it from the
#declaration is the point, so it is never committed
/src/vfs/punkdeclare.vfs/lib_tcl9/
#G-127 per-platform payload staging - materialized from %platform% entries in
#src/vfs/<name>.vfs.toml (one subtree per consuming kit target) and selected into
#each kit's merged image at bake time; regenerable staging, never committed
/src/vfs/*.vfs/_targets/
#review
/src/docgen
#local developer notes
/localdoc/
#Built tclkits (if any)
*.exe
#subfolder for temporary reference files - not source controlled
#we may use git clone to get a copy of the reference files - but we don't want to track them in the repo
TEMP_REFERENCE
#ignore fossil database files (but keep .fossil-settings and .fossil-custom in repository even if fossil not being used at your site)
_FOSSIL_
.fos
.fslckout
*.fossil
#fossil-generated checkout artifacts - regenerated on every fossil checkin/checkout per the
#versioned 'manifest' setting (value 'rut'); fossil auto-manages them and they must not be
#git-tracked. Anchored: nested manifest-named files elsewhere are real content.
#See .fossil-settings/AGENTS.md for the git+fossil coexistence contract.
/manifest
/manifest.uuid
/manifest.tags
#miscellaneous editor files etc
*.swp
*.log
*.punkcheck
todo.txt
zig-cache/
.zig-cache/
zig-out/
zig-pkg/
/release/
/debug/
/build/
/build-*/
/docgen_tmp/
/~/
#Node
/node_modules/
/package-lock.json
/package.json

53
src/project_layouts/vendor/punk/project-0.1/.gitignore vendored

@ -1,53 +0,0 @@
/bin/*
!/bin/*.cmd
!/bin/*.kit
!/bin/*.tcl
!/bin/*.sh
!/bin/*.bash
/lib/
#The directory for compiled/built Tcl modules
/modules/
/vendorbuilds/
#Temporary files e.g from tests
/tmp/
/logs/
**/_aside/
**/_build/
scratch*
#Built documentation
/html/
/man/
/md/
/doc/
/test*
#Built tclkits (if any)
*.exe
#ignore fossil database files (but keep .fossil-settings and .fossil-custom in repository even if fossil not being used at your site)
_FOSSIL_
.fos
.fslckout
*.fossil
#miscellaneous editor files etc
*.swp
*.log
*.punkcheck
todo.txt
zig-cache/
zig-out/
/release/
/debug/
/build/
/build-*/
/docgen_tmp/

108
src/project_layouts/vendor/punk/project-0.1/gitignore.in vendored

@ -0,0 +1,108 @@
#while bin is primarily an output - some items here are for bootstrapping - so while they aren't the source - we install and source-control them.
/bin/*
!/bin/AGENTS.md
!/bin/*.cmd
!/bin/*.kit
!/bin/*.tcl
!/bin/*.sh
!/bin/*.bash
#The directories for compiled/built Tcl packages (libs and modules)
#These are purely 'output' folders - no need to track
/lib/
/lib_tcl8/
/lib_tcl9/
/modules/
/modules_tcl8/
/modules_tcl9/
#Temporary files e.g from tests
/tmp/
.vscode
#claude harness config: track the shared project settings (hooks/permissions) and project skills,
#ignore everything else (session-local settings etc)
.claude/*
!.claude/settings.json
!.claude/skills/
.omo
/logs/
**/_aside/
**/_build/
scratch*
#Built documentation
/html/
/man/
/md/
/doc/
/test*
/src/testdata
/src/scriptapps/test_*
#G-115 demonstration kit payload - materialized from the src/vfs/punkdeclare.vfs.toml
#declaration (vendor tree + consent-gated bin/packages tier); reproducing it from the
#declaration is the point, so it is never committed
/src/vfs/punkdeclare.vfs/lib_tcl9/
#G-127 per-platform payload staging - materialized from %platform% entries in
#src/vfs/<name>.vfs.toml (one subtree per consuming kit target) and selected into
#each kit's merged image at bake time; regenerable staging, never committed
/src/vfs/*.vfs/_targets/
#review
/src/docgen
#local developer notes
/localdoc/
#Built tclkits (if any)
*.exe
#subfolder for temporary reference files - not source controlled
#we may use git clone to get a copy of the reference files - but we don't want to track them in the repo
TEMP_REFERENCE
#ignore fossil database files (but keep .fossil-settings and .fossil-custom in repository even if fossil not being used at your site)
_FOSSIL_
.fos
.fslckout
*.fossil
#fossil-generated checkout artifacts - regenerated on every fossil checkin/checkout per the
#versioned 'manifest' setting (value 'rut'); fossil auto-manages them and they must not be
#git-tracked. Anchored: nested manifest-named files elsewhere are real content.
#See .fossil-settings/AGENTS.md for the git+fossil coexistence contract.
/manifest
/manifest.uuid
/manifest.tags
#miscellaneous editor files etc
*.swp
*.log
*.punkcheck
todo.txt
zig-cache/
.zig-cache/
zig-out/
zig-pkg/
/release/
/debug/
/build/
/build-*/
/docgen_tmp/
/~/
#Node
/node_modules/
/package-lock.json
/package.json

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

@ -2674,7 +2674,8 @@ RELEASE SEQUENCE (the developer order that satisfies the staleness + provenance
(7) build tclsh src/make.tcl vfscommonupdate -confirm 0
(8) build tclsh src/make.tcl bake -confirm 0 kits; close running kit shells first [K5]
(9) commit tracked build outputs src/bootsupport, thin-layout script/
manifest copies (src/project_layouts +
manifest/gitignore.in payload copies
(src/project_layouts +
templates modpod payload),
src/vfs/_vfscommon.vfs
@ -2712,9 +2713,11 @@ DIAGRAM 1 - DATA FLOW: SOURCE -> BUILT PACKAGES -> PROPAGATION TARGETS
DIAGRAM 1b - THIN-LAYOUT SYNC (part of modules/libs/packages/bakehouse runs)
----------------------------------------------------------------------------
src/make.tcl src/build.tcl src/bootsupport/modules*/include_modules.config
.gitignore src/make.tcl src/build.tcl src/bootsupport/modules*/include_modules.config
|
| punkcheck-recorded copies into each vendor layout carrying src/make.tcl
| punkcheck-recorded copies: root .gitignore -> every vendor layout's inert
| gitignore.in payload (G-012; materialized to .gitignore at generation),
| scripts/manifests -> each vendor layout carrying src/make.tcl
v
src/project_layouts/vendor/punk/<layout>/src/... (thin layouts: scripts, docs,
| manifests - no module snapshots)
@ -6634,9 +6637,11 @@ if {$::punkboot::command in {bakehouse packages libs}} {
if {$::punkboot::command in {bakehouse packages modules libs}} {
########################################################
#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.
#sync the canonical root .gitignore into every vendor layout as the inert gitignore.in
#payload (G-012 - materialized to .gitignore at project generation), sync boot scripts
#(build.tcl, make.tcl) and bootsupport include_modules.config manifests into the thin
#layouts 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 {*}{
@ -6650,10 +6655,6 @@ if {$::punkboot::command in {bakehouse 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
}]
@ -6662,16 +6663,27 @@ if {$::punkboot::command in {bakehouse packages modules libs}} {
$tpl_installer set_source_target $sourcefolder $layoutbase/$layoutname
set tpl_event [$tpl_installer start_event $config]
#----------
#G-012: every layout's gitignore payload is a derived mirror of the canonical root
#.gitignore, stored inert as gitignore.in (materialized to .gitignore in the
#generated project by punk::mix::commandset::layout::lib::layout_materialize).
#Canonical source is the REPO root .gitignore ($projectroot, not $sourcefolder).
set pairs [list {*}{
} [list $sourcefolder/build.tcl $layoutbase/$layoutname/src/build.tcl] {*}{
} [list $sourcefolder/make.tcl $layoutbase/$layoutname/src/make.tcl] {*}{
} [list $projectroot/.gitignore $layoutbase/$layoutname/gitignore.in] {*}{
}
]
#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]
if {[file exists $layoutbase/$layoutname/src/make.tcl]} {
#layout carries boot scripts - sync them too (layouts that don't, e.g minimal,
#sample-0.1, receive only the gitignore payload)
lappend pairs {*}{
} [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]
}
}
}

47
src/project_layouts/vendor/punk/sample-0.1/.gitignore vendored

@ -1,47 +0,0 @@
/bin/
/lib/
#The directory for compiled/built Tcl modules
/modules/
/vendorbuilds/
#Temporary files e.g from tests
/tmp/
/logs/
**/_aside/
**/_build/
scratch*
#Built documentation
/html/
/man/
/md/
/doc/
/test*
#Built tclkits (if any)
punk*.exe
tcl*.exe
#ignore fossil database files (but keep .fossil-settings and .fossil-custom in repository even if fossil not being used at your site)
_FOSSIL_
.fos
.fslckout
*.fossil
#miscellaneous editor files etc
*.swp
todo.txt
zig-cache/
zig-out/
/release/
/debug/
/build/
/build-*/
/docgen_tmp/

108
src/project_layouts/vendor/punk/sample-0.1/gitignore.in vendored

@ -0,0 +1,108 @@
#while bin is primarily an output - some items here are for bootstrapping - so while they aren't the source - we install and source-control them.
/bin/*
!/bin/AGENTS.md
!/bin/*.cmd
!/bin/*.kit
!/bin/*.tcl
!/bin/*.sh
!/bin/*.bash
#The directories for compiled/built Tcl packages (libs and modules)
#These are purely 'output' folders - no need to track
/lib/
/lib_tcl8/
/lib_tcl9/
/modules/
/modules_tcl8/
/modules_tcl9/
#Temporary files e.g from tests
/tmp/
.vscode
#claude harness config: track the shared project settings (hooks/permissions) and project skills,
#ignore everything else (session-local settings etc)
.claude/*
!.claude/settings.json
!.claude/skills/
.omo
/logs/
**/_aside/
**/_build/
scratch*
#Built documentation
/html/
/man/
/md/
/doc/
/test*
/src/testdata
/src/scriptapps/test_*
#G-115 demonstration kit payload - materialized from the src/vfs/punkdeclare.vfs.toml
#declaration (vendor tree + consent-gated bin/packages tier); reproducing it from the
#declaration is the point, so it is never committed
/src/vfs/punkdeclare.vfs/lib_tcl9/
#G-127 per-platform payload staging - materialized from %platform% entries in
#src/vfs/<name>.vfs.toml (one subtree per consuming kit target) and selected into
#each kit's merged image at bake time; regenerable staging, never committed
/src/vfs/*.vfs/_targets/
#review
/src/docgen
#local developer notes
/localdoc/
#Built tclkits (if any)
*.exe
#subfolder for temporary reference files - not source controlled
#we may use git clone to get a copy of the reference files - but we don't want to track them in the repo
TEMP_REFERENCE
#ignore fossil database files (but keep .fossil-settings and .fossil-custom in repository even if fossil not being used at your site)
_FOSSIL_
.fos
.fslckout
*.fossil
#fossil-generated checkout artifacts - regenerated on every fossil checkin/checkout per the
#versioned 'manifest' setting (value 'rut'); fossil auto-manages them and they must not be
#git-tracked. Anchored: nested manifest-named files elsewhere are real content.
#See .fossil-settings/AGENTS.md for the git+fossil coexistence contract.
/manifest
/manifest.uuid
/manifest.tags
#miscellaneous editor files etc
*.swp
*.log
*.punkcheck
todo.txt
zig-cache/
.zig-cache/
zig-out/
zig-pkg/
/release/
/debug/
/build/
/build-*/
/docgen_tmp/
/~/
#Node
/node_modules/
/package-lock.json
/package.json

86
src/tests/modules/punk/mix/testsuites/layout/materialize.test

@ -8,6 +8,9 @@ package require punk::mix::commandset::layout
#is fed -layoutinfo directly). The repo fixtures (vendor/custom punk/sample-0.1 +
#othersample@sample-0.1) are exercised end-to-end via dev project.new - see the G-087 detail
#file verification records.
#extended 2026-08-01 (agent, G-012) - inert VCS-config payloads: the gitignore.in -> .gitignore
#materialize rename (map, any-depth staging rename, store-name .anti targeting, inert+live
#conflict error, fast-path denial so the store is never renamed).
namespace eval ::testspace {
namespace import ::tcltest::*
@ -362,6 +365,89 @@ namespace eval ::testspace {
-cleanup {}\
-result [list 1 3 derived-readme extra modules-readme 0 .gitignore]
# -- --- --- G-012 inert VCS-config payloads --- --- --
#added 2026-08-01 (agent, G-012) - inert-payload rename map and staging materialization pins
test materialize_renames_map {layout_materialize_renames returns the inert->materialized tail map}\
-setup $common -body {
punk::mix::commandset::layout::lib::layout_materialize_renames
}\
-cleanup {}\
-result {gitignore.in .gitignore}
#added 2026-08-01 (agent, G-012)
test stage_inert_payload_renamed {inert gitignore.in composes to live .gitignore at any depth}\
-setup $common -body {
set store [make_store s30]
fwrite $store/vendor/punk/sample/gitignore.in "ignore-payload"
fwrite $store/vendor/punk/sample/sub/gitignore.in "nested-payload"
fwrite $store/vendor/punk/sample/keep.txt "keep"
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk sample]
set staging [file join $store staging]
set sres [punk::mix::commandset::layout::lib::layout_stage_chain $chain $staging]
lappend result [fread $staging/.gitignore]
lappend result [fread $staging/sub/.gitignore]
lappend result [file exists $staging/gitignore.in]
lappend result [file exists $staging/sub/gitignore.in]
lappend result [lsort [dict get $sres materialized]]
}\
-cleanup {}\
-result [list ignore-payload nested-payload 0 0 {{gitignore.in .gitignore} {sub/gitignore.in sub/.gitignore}}]
#added 2026-08-01 (agent, G-012)
test stage_inert_anti_targets_store_name {gitignore.in.anti deletes the inherited payload; nothing materializes}\
-setup $common -body {
set store [make_store s31]
fwrite $store/vendor/punk/sample/gitignore.in "ignore-payload"
fwrite $store/vendor/punk/sample/keep.txt "keep"
file mkdir $store/custom/punk/sample
fwrite $store/custom/punk/sample/gitignore.in.anti ""
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk sample]
set staging [file join $store staging]
set sres [punk::mix::commandset::layout::lib::layout_stage_chain $chain $staging]
lappend result [file exists $staging/.gitignore]
lappend result [file exists $staging/gitignore.in]
lappend result [file exists $staging/keep.txt]
lappend result [dict get $sres deletions]
lappend result [dict get $sres materialized]
}\
-cleanup {}\
-result [list 0 0 1 gitignore.in {}]
#added 2026-08-01 (agent, G-012)
test stage_inert_conflict_errors {a folder carrying both the inert and the materialized name errors}\
-setup $common -body {
set store [make_store s32]
fwrite $store/vendor/punk/sample/gitignore.in "ignore-payload"
fwrite $store/vendor/punk/sample/.gitignore "live-config"
set chain [punk::mix::commandset::layout::lib::layout_resolve_chain $store punk sample]
set staging [file join $store staging]
set code [catch {punk::mix::commandset::layout::lib::layout_stage_chain $chain $staging} emsg]
lappend result $code
lappend result [string match "*must never be layout store content*" $emsg]
}\
-cleanup {}\
-result [list 1 1]
#added 2026-08-01 (agent, G-012)
test materialize_inert_forces_staging {single-folder chain carrying an inert payload is staged; the store is never renamed}\
-setup $common -body {
set store [make_store s33]
fwrite $store/vendor/punk/solo/gitignore.in "ignore-payload"
fwrite $store/vendor/punk/solo/a.txt "a"
set linfo [list path [file join $store vendor punk solo] basefolder $store sourceinfo {}]
set staging [file join $store staging]
set mat [punk::mix::commandset::layout::lib::layout_materialize punk.solo -layoutinfo $linfo -stagingdir $staging]
lappend result [dict get $mat staged]
lappend result [expr {[dict get $mat path] ne [file join $store vendor punk solo]}]
lappend result [fread [dict get $mat path]/.gitignore]
lappend result [file exists [file join $store vendor punk solo gitignore.in]]
lappend result [file exists [file join $store vendor punk solo .gitignore]]
lappend result [dict get $mat materialized]
}\
-cleanup {}\
-result [list 1 1 ignore-payload 1 0 {{gitignore.in .gitignore}}]
variable BASE
file delete -force $BASE

Loading…
Cancel
Save