Launcher/make.tcl single help mechanism (user direction; core 0.5.0, 0.53.0)
All -help/--help flag interception withdrawn from the kit launcher and
make.tcl: the leading 'help' word is the ONE help entry (the punkshell
'i <cmd> ...' idiom). It felt like too many ways to do the same thing
and was already colliding - the experimental shellspy processor's own
-help/--help handling was shadowed by the dispatch intercept; it is
reachable again.
Launcher (boot core 0.4.0 -> 0.5.0, moduledoc::punkexe 0.6.0,
moduledoc::punkboot 0.3.0):
- 'help <subcommand> ?arg ...?' accepts and IGNORES trailing words, so
'help' can be prepended to an existing command line ('punk9_beta help
tclsh somefile somearg' now shows the tclsh usage instead of
confusingly falling back to the top-level table).
- Subject validation parses ONLY the subject word against a new
required-subject clone (script)::punkexe.launcherhelpsubject
(resolved_def -antiglobs {arg} -override subject -optional 0): the
display-shape clone let punk::args SKIP a mistyped optional subject
into the unconstrained arg row, silently rendering the top table
where a tabled choice error is wanted (exit 1).
- A leading '-help' falls under the unknown-first-argument refusal
(stderr usage answers it, exit 1); '<subcommand> -help' is the
subcommand's own argument: tclsh regains exception-free stock dash
parity, buildinfo refuses it like any argument (exit 2), a
parse-declared registration rejects it like any undeclared flag.
make.tcl: the wants_help subargs scan ('<subcommand> ... -help') and
the top-level/degraded-scan help-flag branches are removed, along with
the help_flags variable - 'make.tcl bakelist -help' now fails the
kitname choice gate exactly like 'make.tcl bakelist nonexistant', and
'make.tcl -help' the subcommand choice gate (exit 1), the usage error
itself carrying the documentation. 'make.tcl help <sub> <cmdline>'
dry-run behaviour unchanged.
Tests re-pinned to the revised contract (launcherhelp.test with a
kit_nodashhelp probe gate + trailing-args coverage, maketclhelp.test,
maketclcolour.test); punkexe subtree 152/148/4skip/0fail against
freshly baked zipfs + metakit kits; core copies byte-identical across
master/_vfscommon/layout/modpod-template channels.
Claude-Session: https://claude.ai/code/session_01Y1diJnhjUxKgEG6EwYAzxj
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- **Kit anatomy.** Each `src/vfs/*.vfs` folder is a runtime payload; `src/runtime/mapvfs.toml` (G-024, tomlish-parsed; deprecated `mapvfs.config` line format still readable) maps payload folders to platform runtimes under `bin/runtime/<platform>/` as named kit outputs, with named groups and generative version-named schemes; an optional sibling `src/vfs/<name>.vfs.toml` declares payload packages materialized INTO the folder with drop-in-wins precedence (G-115 - spec: `src/vfs/README.md`); `src/vfs/_vfscommon.vfs/` is a generated merge of common libraries (never hand-edited); `src/vfs/_config/` holds the entry scripts. Sources: `src/vfs/AGENTS.md`, `src/runtime/AGENTS.md`.
- **Kit anatomy.** Each `src/vfs/*.vfs` folder is a runtime payload; `src/runtime/mapvfs.toml` (G-024, tomlish-parsed; deprecated `mapvfs.config` line format still readable) maps payload folders to platform runtimes under `bin/runtime/<platform>/` as named kit outputs, with named groups and generative version-named schemes; an optional sibling `src/vfs/<name>.vfs.toml` declares payload packages materialized INTO the folder with drop-in-wins precedence (G-115 - spec: `src/vfs/README.md`); `src/vfs/_vfscommon.vfs/` is a generated merge of common libraries (never hand-edited); `src/vfs/_config/` holds the entry scripts. Sources: `src/vfs/AGENTS.md`, `src/runtime/AGENTS.md`.
- **Entry point.**`src/vfs/_config/punk_main.tcl` (thin main sourcing the shared boot core - G-031). An optional first argument is a dash-delimited package mode composed of tokens from `minted`, `os`, `src`, `internal` (`internal` is always appended when absent; `minted` was named `dev` pre-0.43.0), optionally scoped with the `proj:` prefix (G-033: `minted`/`src` resolve against the project containing the cwd - walk-up to the nearest git/fossil repo root with a punkshell-style src tree - instead of the executable's own project; discovery is always reported, never a silent rebind). The next argument is the subcommand: `shell`, `script`, `punk`, `tclsh`, `shellspy`, `buildinfo`, `help`. A non-subcommand first argument naming an existing file (or `lib:*` reference) is treated as a script invocation; a first argument of exactly `-e` runs the following argument as a Tcl one-liner (G-077, achieved - handled as `script -e`; tool-style kits keep `-e` routed to their own processor); anything else is refused with usage (G-032); no arguments at all means interactive shell. Subcommands map to app packages under `src/lib/` (`app-punkshell`, `app-punkscript`, `app-repl`, `app-shellspy`). The launch surface self-documents through punk::args (G-032, achieved semantics in boot core 0.4.0): `help`/`-help`/`<subcommand> -help` render tabled usage from the moduledoc definitions plus project registrations (`::punkboot::project_subcommand_info` - summaries, definition ids, optional declared parse), degrading to a plain subcommand list when that stack is unavailable. Source: `src/vfs/AGENTS.md`.
- **Entry point.**`src/vfs/_config/punk_main.tcl` (thin main sourcing the shared boot core - G-031). An optional first argument is a dash-delimited package mode composed of tokens from `minted`, `os`, `src`, `internal` (`internal` is always appended when absent; `minted` was named `dev` pre-0.43.0), optionally scoped with the `proj:` prefix (G-033: `minted`/`src` resolve against the project containing the cwd - walk-up to the nearest git/fossil repo root with a punkshell-style src tree - instead of the executable's own project; discovery is always reported, never a silent rebind). The next argument is the subcommand: `shell`, `script`, `punk`, `tclsh`, `shellspy`, `buildinfo`, `help`. A non-subcommand first argument naming an existing file (or `lib:*` reference) is treated as a script invocation; a first argument of exactly `-e` runs the following argument as a Tcl one-liner (G-077, achieved - handled as `script -e`; tool-style kits keep `-e` routed to their own processor); anything else is refused with usage (G-032); no arguments at all means interactive shell. Subcommands map to app packages under `src/lib/` (`app-punkshell`, `app-punkscript`, `app-repl`, `app-shellspy`). The launch surface self-documents through punk::args (G-032; single-mechanism revision 2026-08-05 in boot core 0.5.0): `help ?subcommand? ?arg ...?` - the only help entry, trailing words accepted so `help` can be prepended to a command line - renders tabled usage from the moduledoc definitions plus project registrations (`::punkboot::project_subcommand_info` - summaries, definition ids, optional declared parse), degrading to a plain subcommand list when that stack is unavailable; help FLAGS are never intercepted (a leading `-help` earns the refusal, `<subcommand> -help` belongs to the subcommand). Source: `src/vfs/AGENTS.md`.
- **Package modes pick module provenance.**`internal` uses module snapshots baked into the kit at bake time; `minted` loads the mint output trees at repo-root `modules/` and `lib/`; `src` loads the unbuilt working tree (`src/modules`, `src/lib`, `src/bootsupport`, `src/vendormodules`) with `package prefer latest` so magic-version dev modules (`999999.0a1.0`) beat stamped snapshots. `src` mode is the standard way to verify working-tree changes without a rebuild. Source: `bin/AGENTS.md` "Launch package modes".
- **Package modes pick module provenance.**`internal` uses module snapshots baked into the kit at bake time; `minted` loads the mint output trees at repo-root `modules/` and `lib/`; `src` loads the unbuilt working tree (`src/modules`, `src/lib`, `src/bootsupport`, `src/vendormodules`) with `package prefer latest` so magic-version dev modules (`999999.0a1.0`) beat stamped snapshots. `src` mode is the standard way to verify working-tree changes without a rebuild. Source: `bin/AGENTS.md` "Launch package modes".
- **Payload mount derivation (G-129, achieved).** The boot keys zipfs presence on `tcl::zipfs::mount` and derives where the executable's attached archive actually mounted from the mount table itself (the entry whose archive file is the executable; fallback: the mount containing the boot script) instead of assuming `tcl::zipfs::root` + `//zipfs:/app`. Modern runtimes still answer `//zipfs:/app`; the androwish/undroidwish 8.6 backport has no `root` command, mounts at the executable's own path, and boots the same payload through its `app/main.tcl` in-archive hook (`src/vfs/punk8win.vfs` carries fauxlinks for both conventions). A mounted archive the boot cannot attribute is reported on stderr rather than silently yielding no internal paths. The derivation procs stay defined post-boot (`::punkboot::zipfs_kit_mountbase`). Sources: `src/vfs/AGENTS.md`, `bin/AGENTS.md` "Kit-wrappable runtime requirements", `src/tests/shell/testsuites/punkexe/kitmountpoint.test`.
- **Payload mount derivation (G-129, achieved).** The boot keys zipfs presence on `tcl::zipfs::mount` and derives where the executable's attached archive actually mounted from the mount table itself (the entry whose archive file is the executable; fallback: the mount containing the boot script) instead of assuming `tcl::zipfs::root` + `//zipfs:/app`. Modern runtimes still answer `//zipfs:/app`; the androwish/undroidwish 8.6 backport has no `root` command, mounts at the executable's own path, and boots the same payload through its `app/main.tcl` in-archive hook (`src/vfs/punk8win.vfs` carries fauxlinks for both conventions). A mounted archive the boot cannot attribute is reported on stderr rather than silently yielding no internal paths. The derivation procs stay defined post-boot (`::punkboot::zipfs_kit_mountbase`). Sources: `src/vfs/AGENTS.md`, `bin/AGENTS.md` "Kit-wrappable runtime requirements", `src/tests/shell/testsuites/punkexe/kitmountpoint.test`.
- **Static package capture (G-058, achieved).** Boot probe-loads the runtime's statically linked packages in a throwaway interp and records what each provides; every interp the shell fabricates (boot, codethread, shellthread workers) seeds `package ifneeded <name> <ver> {load {} <prefix>}` from that record, and `punk::packagepreference` resolves static-vs-bundled version-aware. Sources: `src/vfs/AGENTS.md`, `goals/archive/G-058-static-runtime-packages.md`.
- **Static package capture (G-058, achieved).** Boot probe-loads the runtime's statically linked packages in a throwaway interp and records what each provides; every interp the shell fabricates (boot, codethread, shellthread workers) seeds `package ifneeded <name> <ver> {load {} <prefix>}` from that record, and `punk::packagepreference` resolves static-vs-bundled version-aware. Sources: `src/vfs/AGENTS.md`, `goals/archive/G-058-static-runtime-packages.md`.
- **`script` subcommand (G-015, achieved).** The lean non-interactive path: default punk shell module/alias environment, no shellfilter stacks/transforms, honest exit codes, launch plumbing emits nothing on stdout/stderr (exec-style callers see only the script's own output). Supports `lib:<name>` scriptlib resolution; a first argument of exactly `-e` runs the second argument as a Tcl one-liner (G-077, achieved - `::argv0` is `-e`, trailing args in `::argv`, a usage error when the script argument is missing, stdin never read by the form itself, non-empty final result echoed), also reachable as top-level `<punkexe> -e` via a boot-core reclassification ahead of the unknown-first-arg refusal. App package: `src/lib/app-punkscript/`.
- **`script` subcommand (G-015, achieved).** The lean non-interactive path: default punk shell module/alias environment, no shellfilter stacks/transforms, honest exit codes, launch plumbing emits nothing on stdout/stderr (exec-style callers see only the script's own output). Supports `lib:<name>` scriptlib resolution; a first argument of exactly `-e` runs the second argument as a Tcl one-liner (G-077, achieved - `::argv0` is `-e`, trailing args in `::argv`, a usage error when the script argument is missing, stdin never read by the form itself, non-empty final result echoed), also reachable as top-level `<punkexe> -e` via a boot-core reclassification ahead of the unknown-first-arg refusal. App package: `src/lib/app-punkscript/`.
- **`tclsh` subcommand (G-118, achieved).** Plain-tclsh emulation with NO punk modules loaded: stock argument-form parity (`-encoding name file`; any other leading `-` argument means no script, everything stays in `::argv`; no `-e` one-liner - deliberate stock parity; the punk-level one-liner lives at the top level and as `script -e` (G-077, achieved 2026-08-04 - goals/archive/G-077-punkexe-dash-e-oneliner.md)), `lib:` refused with a pointer at `script`, missing script file is a clean exit 1. On piperepl-patched runtimes (the punk family runtimes - `src/buildsuites/suite_tcl90/patches/`) the C-level machinery publishes `::tclsh` launch state (`istty`, `dorepl`, `evalinput`, `inputbuffer`, `reopened`) allowing piped input to hand over to a live console repl at eof; runtimes without the machinery evaluate piped stdin whole-buffer with honest exit codes and fail fast on console stdin. The subcommand-family contract (tclsh/script/shell/punk/shellspy/buildinfo/help) is documented as punk::args definitions in `src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm` (ids `(script)::punkexe` + per-subcommand; view via `punk::args::usage`), which the G-032 launcher help wiring renders and parses from at dispatch time (a help flag as tclsh's SOLE argument is the one deliberate stock-parity exception). Pipe-testable behaviour pinned by `src/tests/shell/testsuites/punkexe/tclshcmd.test` and `launcherhelp.test`.
- **`tclsh` subcommand (G-118, achieved).** Plain-tclsh emulation with NO punk modules loaded: stock argument-form parity (`-encoding name file`; any other leading `-` argument means no script, everything stays in `::argv`; no `-e` one-liner - deliberate stock parity; the punk-level one-liner lives at the top level and as `script -e` (G-077, achieved 2026-08-04 - goals/archive/G-077-punkexe-dash-e-oneliner.md)), `lib:` refused with a pointer at `script`, missing script file is a clean exit 1. On piperepl-patched runtimes (the punk family runtimes - `src/buildsuites/suite_tcl90/patches/`) the C-level machinery publishes `::tclsh` launch state (`istty`, `dorepl`, `evalinput`, `inputbuffer`, `reopened`) allowing piped input to hand over to a live console repl at eof; runtimes without the machinery evaluate piped stdin whole-buffer with honest exit codes and fail fast on console stdin. The subcommand-family contract (tclsh/script/shell/punk/shellspy/buildinfo/help) is documented as punk::args definitions in `src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm` (ids `(script)::punkexe` + per-subcommand; view via `punk::args::usage`), which the G-032 launcher help wiring renders and parses from at dispatch time (`help tclsh` for the usage; stock dash parity is exception-free since the 2026-08-05 single-mechanism revision). Pipe-testable behaviour pinned by `src/tests/shell/testsuites/punkexe/tclshcmd.test` and `launcherhelp.test`.
- **`bin/` launch surface.** Built kits plus polyglot `.cmd` utilities (`punk-runtime`, `punk-getzig`, `punk-tclargs`, `dtplite`) generated from `src/scriptapps/` sources by the `punk::mix` scriptwrap machinery - outputs are never hand-edited. `bin/punk-runtime.cmd` fetches/lists/selects plain runtimes under `bin/runtime/<platform>/` from the punkbin artifact server with sha1 verification. Source: `bin/AGENTS.md`.
- **`bin/` launch surface.** Built kits plus polyglot `.cmd` utilities (`punk-runtime`, `punk-getzig`, `punk-tclargs`, `dtplite`) generated from `src/scriptapps/` sources by the `punk::mix` scriptwrap machinery - outputs are never hand-edited. `bin/punk-runtime.cmd` fetches/lists/selects plain runtimes under `bin/runtime/<platform>/` from the punkbin artifact server with sha1 verification. Source: `bin/AGENTS.md`.
@ -97,7 +97,7 @@ platform-specific difference left in examples is path separator style
### Launch package modes (built punk shells)
### Launch package modes (built punk shells)
The first argument to a built punk shell may be a dash-delimited package mode composed of tokens from `minted`, `os`, `src`, `internal` (e.g. `punksys src`, `punk902z minted-os`), optionally scoped with the `proj:` prefix (e.g. `punk91 proj:internal-src`). (`minted` was named `dev` before punkshell 0.43.0 - clean rename, no alias: old kits accept only `dev`, current kits only `minted`.) `internal` is the default and is always appended when absent. A first argument that is not a valid mode list is treated as a subcommand instead (a `proj:` prefix on a non-mode string is not consumed either). Implementation: `src/vfs/_config/punk_main.tcl` (search `all_package_modes`). The full packagemode contract (block meanings, ordering, proj: examples) is the punk::args documentation at id `(script)::punkexe` (module `punk::args::moduledoc::punkexe`), and the launch surface is self-documenting in every current kit (G-032): `<punkexe> help ?subcommand?`, `<punkexe> -help` or `<punkexe> <subcommand> -help` render tabled usage (with the packagemode contract as the leader row) - degrading to a plain subcommand list where the punk::args stack is unavailable. An unknown first argument that names no existing file (and no `lib:*` scriptlib reference) is refused with that usage rather than silently attempted as a script. A first argument of exactly `-e` instead runs the following argument as a Tcl one-liner (G-077; equivalently `<punkexe> script -e <script> ?arg ...?`).
The first argument to a built punk shell may be a dash-delimited package mode composed of tokens from `minted`, `os`, `src`, `internal` (e.g. `punksys src`, `punk902z minted-os`), optionally scoped with the `proj:` prefix (e.g. `punk91 proj:internal-src`). (`minted` was named `dev` before punkshell 0.43.0 - clean rename, no alias: old kits accept only `dev`, current kits only `minted`.) `internal` is the default and is always appended when absent. A first argument that is not a valid mode list is treated as a subcommand instead (a `proj:` prefix on a non-mode string is not consumed either). Implementation: `src/vfs/_config/punk_main.tcl` (search `all_package_modes`). The full packagemode contract (block meanings, ordering, proj: examples) is the punk::args documentation at id `(script)::punkexe` (module `punk::args::moduledoc::punkexe`), and the launch surface is self-documenting in every current kit (G-032; single-mechanism revision 2026-08-05): `<punkexe> help ?subcommand? ?arg ...?` renders tabled usage (with the packagemode contract as the leader row; trailing words after the subject are accepted and ignored, so `help` can be prepended to an existing command line) - degrading to a plain subcommand list where the punk::args stack is unavailable. Help FLAGS are never intercepted: `-help` after a subcommand is that subcommand's own argument, and a leading `-help` falls under the refusal below. An unknown first argument that names no existing file (and no `lib:*` scriptlib reference) is refused with that usage rather than silently attempted as a script. A first argument of exactly `-e` instead runs the following argument as a Tcl one-liner (G-077; equivalently `<punkexe> script -e <script> ?arg ...?`).
`src` mode is the one that matters for verifying working-tree changes:
`src` mode is the one that matters for verifying working-tree changes:
@ -69,7 +69,7 @@ Recovery after a wrong path guess:
- Provenance warnings (dirty-src gate + vendorupdate source-project check) print with a plain column-0 `PROVENANCE-WARNING:` token (grep for it in captured make.tcl output) and are recapped at the end of the run via make.tcl's wrapped `::exit`. Interactive terminal runs get a 3-second ctrl-c grace countdown before a dirty run proceeds; piped/agent runs skip it. `tclsh src/make.tcl check` reports the current src provenance status and what the producing commands would do.
- Provenance warnings (dirty-src gate + vendorupdate source-project check) print with a plain column-0 `PROVENANCE-WARNING:` token (grep for it in captured make.tcl output) and are recapped at the end of the run via make.tcl's wrapped `::exit`. Interactive terminal runs get a 3-second ctrl-c grace countdown before a dirty run proceeds; piped/agent runs skip it. `tclsh src/make.tcl check` reports the current src provenance status and what the producing commands would do.
- 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`).
- 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 release workflow (the TERMINOLOGY stage-verb key, 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 make.tcl 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 TERMINOLOGY / 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.
- `tclsh src/make.tcl workflow` prints an embedded ASCII data-flow overview of the release workflow (the TERMINOLOGY stage-verb key, 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 make.tcl 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 TERMINOLOGY / 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; help depth G-143, achieved): `tclsh src/make.tcl help ?subcommand? ?arg ...?`or `<subcommand> ?arg ...? -help` shows tabled usage; invalid arguments produce punk::args usage errors. Bare `make.tcl help` (and `make.tcl`/`-help`) renders a rich top-level overview (2026-08-02, the `i info` style): per-subcommand cells carrying the one-line summary plus that subcommand's own auto-generated synopsis line(s) - per-form lines for the multi-form subcommands - grouped by SUBGROUPS (rich `-choicelabels` built by `::punkboot::argdoc::overview_labels` from `punk::args::synopsis` per id, hung on the separate `(script)::punkboot.overview` id so dispatch never pays the label cost; the lean `(script)::punkboot` id remains the dispatch surface). `-choicecolumns` is 1 for now - the long one-line summaries make a 2-column layout too wide without table CELL WRAPPING; revisit `-choicecolumns 2` when that lands (user decision 2026-08-02; the `## FORM` header lines are likewise deliberately dropped from the cells). tool and buildsuite are multi-form definitions (one @form per action with a literal action leader), so whole-subcommand help renders one synopsis line per action, and help accepts the subcommand's own COMMAND LINE after the subject and DRY-RUNS it through that subcommand's declaration: `make.tcl help tool build -test 0 punkzip` (equivalently that command line with `-help` appended) parses via punk::args form auto-selection and renders the build form's usage, while a line the subcommand would reject (unknown action word, option-first line, unknown flag in option position) gets the same pointed punk::args noformmatch diagnosis dispatch gives - synopsis plus per-form reasons - on stderr with exit 1, never a plain-help fallback. There is deliberately no bare-action carve-out: an action word alone is dry-run too, so a form with required values reports them via the diagnosis (`help buildsuite build` exits 1 naming the missing suitename - the error table carries the form's argument rows, so it IS the form documentation). An accepted line is confirmed with a one-line received-args report after the table (`dry-run: line accepted (form build) - action = build | -test = 0 | toolname = punkzip`); punk::args' positional model consumes flag-like words at/after the first value position as VALUES where the value arg is unconstrained (tool's toolname), and the report makes such swallows visible - value args with restricted choices (bake/bakelist kitnames as of 2026-08-02) instead reject the flag-like word at the choice gate, exactly as dispatch does. Declaration-level passthroughs (shell args, buildsuite driver args) always parse clean; numeric form indexes are deliberately not an interface. tool DISPATCH parses through its definition: unknown actions/flags are punk::args usage errors (exit 1), and the declared positional model puts options before the tool names (`make.tcl tool build -test 0 <name> ...`, matching bake's flags-before-names convention; the historic flag-anywhere order is deliberately not accepted - a flag-shaped tool name earns a stderr hint). buildsuite keeps its passthrough driver-args contract - its forms serve help/synopsis accuracy only. 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 degraded tool handler keeps the historic manual tail parse and exit-2 surface). Piped characterization: `src/tests/shell/testsuites/punkexe/maketclhelp.test`. 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 — with `-help` bodies expanding as deferred display fields so the HELPTEXTS block indentation deliberately renders as a centred Description (no `@normalize` re-basing). Synopses are the automatic punk::args bracket notation via the G-144 `@cmd -name` fallback; explicit `@form -synopsis` overrides were retired 2026-08-01 except the top-level `make.tcl ?subcommand? ?flags?` line (it states the bare-invocation/flags reality the auto render cannot) - do not reintroduce per-subcommand overrides without cause. See src/modules/AGENTS.md "punk::args definition authoring ergonomics" before editing them or authoring similar definitions.
- make.tcl subcommands and flags are punk::args-declared (G-030; help depth G-143, achieved; single-mechanism revision 2026-08-05, user direction): `tclsh src/make.tcl help ?subcommand? ?arg ...?`shows tabled usage - the leading `help` word is the ONLY help entry; help FLAGS (-help/--help/-h//?) are ordinary arguments that fail whatever gate they meet (top level: the subcommand choice violation; tool option position: noformmatch; a value position with restricted choices: the choice gate; degraded scan: Unknown flag + help exit 1), with the usage error itself carrying the documentation. Invalid arguments produce punk::args usage errors. Bare `make.tcl help` (and bare `make.tcl`) renders a rich top-level overview (2026-08-02, the `i info` style): per-subcommand cells carrying the one-line summary plus that subcommand's own auto-generated synopsis line(s) - per-form lines for the multi-form subcommands - grouped by SUBGROUPS (rich `-choicelabels` built by `::punkboot::argdoc::overview_labels` from `punk::args::synopsis` per id, hung on the separate `(script)::punkboot.overview` id so dispatch never pays the label cost; the lean `(script)::punkboot` id remains the dispatch surface). `-choicecolumns` is 1 for now - the long one-line summaries make a 2-column layout too wide without table CELL WRAPPING; revisit `-choicecolumns 2` when that lands (user decision 2026-08-02; the `## FORM` header lines are likewise deliberately dropped from the cells). tool and buildsuite are multi-form definitions (one @form per action with a literal action leader), so whole-subcommand help renders one synopsis line per action, and help accepts the subcommand's own COMMAND LINE after the subject and DRY-RUNS it through that subcommand's declaration: `make.tcl help tool build -test 0 punkzip` parses via punk::args form auto-selection and renders the build form's usage, while a line the subcommand would reject (unknown action word, option-first line, unknown flag in option position) gets the same pointed punk::args noformmatch diagnosis dispatch gives - synopsis plus per-form reasons - on stderr with exit 1, never a plain-help fallback. There is deliberately no bare-action carve-out: an action word alone is dry-run too, so a form with required values reports them via the diagnosis (`help buildsuite build` exits 1 naming the missing suitename - the error table carries the form's argument rows, so it IS the form documentation). An accepted line is confirmed with a one-line received-args report after the table (`dry-run: line accepted (form build) - action = build | -test = 0 | toolname = punkzip`); punk::args' positional model consumes flag-like words at/after the first value position as VALUES where the value arg is unconstrained (tool's toolname), and the report makes such swallows visible - value args with restricted choices (bake/bakelist kitnames as of 2026-08-02) instead reject the flag-like word at the choice gate, exactly as dispatch does. Declaration-level passthroughs (shell args, buildsuite driver args) always parse clean; numeric form indexes are deliberately not an interface. tool DISPATCH parses through its definition: unknown actions/flags are punk::args usage errors (exit 1), and the declared positional model puts options before the tool names (`make.tcl tool build -test 0 <name> ...`, matching bake's flags-before-names convention; the historic flag-anywhere order is deliberately not accepted - a flag-shaped tool name earns a stderr hint). buildsuite keeps its passthrough driver-args contract - its forms serve help/synopsis accuracy only. 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 degraded tool handler keeps the historic manual tail parse and exit-2 surface). Piped characterization: `src/tests/shell/testsuites/punkexe/maketclhelp.test`. 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 — with `-help` bodies expanding as deferred display fields so the HELPTEXTS block indentation deliberately renders as a centred Description (no `@normalize` re-basing). Synopses are the automatic punk::args bracket notation via the G-144 `@cmd -name` fallback; explicit `@form -synopsis` overrides were retired 2026-08-01 except the top-level `make.tcl ?subcommand? ?flags?` line (it states the bare-invocation/flags reality the auto render cannot) - do not reintroduce per-subcommand overrides without cause. 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 + 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.
- 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/_bake/<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`) bakes 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 `BAKE-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`.
- **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/_bake/<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`) bakes 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 `BAKE-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 `BAKE-WARNING`s and the kit still bakes 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 rebakes. **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.
- **Payload/target consistency checks (G-133), both ADVISORY** - warnings are recapped `BAKE-WARNING`s and the kit still bakes 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 rebakes. **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.
#0.3.0 - single-mechanism revision (2026-08-05, user direction): help-flag interception withdrawn from the launcher - registration argsid text now names 'help <name>' as the only launcher-rendered route (apps own their -help handling); unknownfirst text covers the G-077 '-e' one-liner and states help flags are never intercepted
#0.2.0 - G-032 registration surface extension documented: project_subcommand_info (summary/argsid/package/parse) in the registration definition + thin-main obligations; built-in list gains help; launcher help/flag semantics (unknownfirst=script gate, argsid-gated -help interception) recorded
#0.2.0 - G-032 registration surface extension documented: project_subcommand_info (summary/argsid/package/parse) in the registration definition + thin-main obligations; built-in list gains help; launcher help/flag semantics (unknownfirst=script gate, argsid-gated -help interception) recorded
#0.1.0 - initial (G-031 runtime-queryable boot-core API doc): (script)::punkboot overview + thin-main obligations, (script)::punkboot::registration (project_subcommands / launch_defaults), live definitions for the persistent ::punkboot procs (zipfs_mount_pairs, zipfs_kit_mountbase, path_within, proj_root_find); boot_state surface documented
#0.1.0 - initial (G-031 runtime-queryable boot-core API doc): (script)::punkboot overview + thin-main obligations, (script)::punkboot::registration (project_subcommands / launch_defaults), live definitions for the persistent ::punkboot procs (zipfs_mount_pairs, zipfs_kit_mountbase, path_within, proj_root_find); boot_state surface documented
#0.6.0 - single-mechanism revision (2026-08-05, user direction): all -help flag documentation withdrawn - 'help ?subcommand? ?arg ...?' is the sole help entry (trailing words accepted/ignored - the prepend-help idiom); (script)::punkexe::help gains the arg row + max -1; tclsh parity stated exception-free; per-subcommand -help paragraphs replaced with 'help <sub>' pointers; buildinfo refuses help flags like any argument
#0.5.0 - G-077 -e one-liner forms documented: (script)::punkexe::script gains the oneliner @form + -help paragraph; top-level (script)::punkexe documents the '-e' first-argument reclassification and gains its oneliner @form; (script)::punkexe::tclsh boundary text now points at the live forms
#0.5.0 - G-077 -e one-liner forms documented: (script)::punkexe::script gains the oneliner @form + -help paragraph; top-level (script)::punkexe documents the '-e' first-argument reclassification and gains its oneliner @form; (script)::punkexe::tclsh boundary text now points at the live forms
test launcherhelp_registration_dashhelp {'demo -help' renders the declared definition; handler not invoked}\
#updated 2026-08-05 (agent, user direction) - no argsid-gated '-help'
-constraints {punkexeavailable kit_g032} -body {
#interception: for a parse-declared registration '-help' is an undeclared
#flag and fails the declared-parse gate (exit 1, handler not invoked);
#'help demo' remains the help route
test launcherhelp_registration_dashhelp {'demo -help' (parse-declared registration): rejected by the declared-parse gate like any undeclared flag; handler not invoked}\
test maketcl_colour_help_tables_zero_remnants {piped tclsh src/make.tcl help for every declared subject plus bare help/-help: zero ESC bytes and zero orphan CSI-fragment text} -constraints {tclshavailable} -body {
#updated 2026-08-05 (agent, user direction) - the '-help' invocation removed
#with the single-mechanism revision (help flags are ordinary arguments now:
#'make.tcl -help' is a subcommand choice violation, exit 1)
test maketcl_colour_help_tables_zero_remnants {piped tclsh src/make.tcl help for every declared subject plus bare help: zero ESC bytes and zero orphan CSI-fragment text} -constraints {tclshavailable} -body {
#Declared subjects: the dict keys of ::punkboot::argdoc SUMMARIES in src/make.tcl,
#Declared subjects: the dict keys of ::punkboot::argdoc SUMMARIES in src/make.tcl,
#pulled verbatim by the help subject arg's -choices. Characterization of the
#pulled verbatim by the help subject arg's -choices. Characterization of the
#declared set as of 2026-08-01 - a new subcommand lands in SUMMARIES and here.
#declared set as of 2026-08-01 - a new subcommand lands in SUMMARIES and here.
set subjects {bakehouse packages modules libs bake bakelist vfslibs bin vendorupdate libfetch bootsupport vfscommonupdate info check projectversion workflow shell help buildsuite tool}
set subjects {bakehouse packages modules libs bake bakelist vfslibs bin vendorupdate libfetch bootsupport vfscommonupdate info check projectversion workflow shell help buildsuite tool}
set invocations [list help -help]
set invocations [list help]
foreach subject $subjects {lappend invocations [list help $subject]}
foreach subject $subjects {lappend invocations [list help $subject]}
@ -17,7 +17,7 @@ VFS (Virtual File System) folders define the runtime payloads that get wrapped i
- `punkdeclare.vfs` + `punkdeclare.vfs.toml` are the G-115 demonstration kit: VCS carries only the boot fauxlink; the whole `lib_tcl9/` binary payload (tcludp vendor tree + tcllibc packages tier) materializes from the declaration and is deliberately ignored in both VCS. Bake with `make.tcl bake -confirm 0 punkdeclare` (bake_default=false keeps it out of full bakes); its smoke-require (`udp`, `tcllibc`) proves the declared payload resolves inside the built artifact.
- `punkdeclare.vfs` + `punkdeclare.vfs.toml` are the G-115 demonstration kit: VCS carries only the boot fauxlink; the whole `lib_tcl9/` binary payload (tcludp vendor tree + tcllibc packages tier) materializes from the declaration and is deliberately ignored in both VCS. Bake with `make.tcl bake -confirm 0 punkdeclare` (bake_default=false keeps it out of full bakes); its smoke-require (`udp`, `tcllibc`) proves the declared payload resolves inside the built artifact.
- A `*.vfs` folder baked against a runtime is expected to carry a root-level startup script: an actual `main.tcl`, or a root fauxlink resolving to the name `main.tcl` whose target exists and is a `.tcl` file. `make.tcl` warns when neither is present (column-0 `BAKE-WARNING:` token, ANSI-highlighted, recapped at end of run like provenance warnings) but still bakes - a kit without a startup script is legal. It must NOT carry both, nor two root fauxlinks resolving to `main.tcl`: the bake REFUSES such a kit per-kit under FAILED KITS (G-031 collision gate, predicate `punkboot::utils::vfs_startup_script_report` - merge traversal order would otherwise silently decide the boot script). Overlay-only folders with no runtime mapping (e.g `_vfscommon.vfs`) are not checked.
- A `*.vfs` folder baked against a runtime is expected to carry a root-level startup script: an actual `main.tcl`, or a root fauxlink resolving to the name `main.tcl` whose target exists and is a `.tcl` file. `make.tcl` warns when neither is present (column-0 `BAKE-WARNING:` token, ANSI-highlighted, recapped at end of run like provenance warnings) but still bakes - a kit without a startup script is legal. It must NOT carry both, nor two root fauxlinks resolving to `main.tcl`: the bake REFUSES such a kit per-kit under FAILED KITS (G-031 collision gate, predicate `punkboot::utils::vfs_startup_script_report` - merge traversal order would otherwise silently decide the boot script). Overlay-only folders with no runtime mapping (e.g `_vfscommon.vfs`) are not checked.
- Componentized boot (G-031, 0.45.0): the kit main is a THIN project-owned file that declares project subcommands (`::punkboot::project_subcommands` - built-ins tclsh/shellspy/punk/shell/script/buildinfo/help cannot be shadowed) and launch defaults (`noargs`/`unknownfirst`), publishes `::punkboot::launch_args`/`main_script`, then sources the layout-owned boot core: `<vfsroot>/punkboot/core.tcl` in kits (regenerated into `_vfscommon.vfs` from the `_config/punkboot_core.tcl` master by `make.tcl vfscommonupdate`), parent-dir candidate for nested `app/main.tcl`, sibling `punkboot_core.tcl` when a main runs as a plain script from `_config` (the kitmountpoint characterization shape). The core carries the boot machinery and default dispatch and is versioned (`::punkboot::core_version`). Behavioural statements about `punk_main.tcl`/`project_main.tcl` in the bullets below describe machinery that now lives in the boot core they source.
- Componentized boot (G-031, 0.45.0): the kit main is a THIN project-owned file that declares project subcommands (`::punkboot::project_subcommands` - built-ins tclsh/shellspy/punk/shell/script/buildinfo/help cannot be shadowed) and launch defaults (`noargs`/`unknownfirst`), publishes `::punkboot::launch_args`/`main_script`, then sources the layout-owned boot core: `<vfsroot>/punkboot/core.tcl` in kits (regenerated into `_vfscommon.vfs` from the `_config/punkboot_core.tcl` master by `make.tcl vfscommonupdate`), parent-dir candidate for nested `app/main.tcl`, sibling `punkboot_core.tcl` when a main runs as a plain script from `_config` (the kitmountpoint characterization shape). The core carries the boot machinery and default dispatch and is versioned (`::punkboot::core_version`). Behavioural statements about `punk_main.tcl`/`project_main.tcl` in the bullets below describe machinery that now lives in the boot core they source.
- Launcher help + declared parse (G-032, core 0.3.0): the dispatch renders and parses through punk::args - `<punkexe> help ?subcommand?` / `-help` / `<subcommand> -help` render tabled usage from the `punk::args::moduledoc::punkexe` ids plus the live registrations (per-kit clones `(script)::punkexe.launcher`/`.launcherhelp`); an unknown first argument naming no existing file (and no `lib:*` reference) is refused with usage (stderr, exit 1) instead of silently reclassifying to `script`; `buildinfo` enforces its no-arguments contract through punk::args (historic one-liner + tabled usage, exit 2). Optional per-name registration metadata `::punkboot::project_subcommand_info` (`summary`/`argsid`/`package`/`parse` - documented by `punk::args::moduledoc::punkboot`) puts project subcommands in the help tables by registration alone and can gate their arguments through a declared definition (usage errors exit 1, handler not invoked). Carve-outs: tool-style kits (`unknownfirst` naming a project subcommand) keep help FLAGS routed to their processor ('help' word remains); `tclsh` intercepts a help flag only as its SOLE argument (stock dash parity); a registration without `argsid` keeps `-help` passthrough to its handler. Everything is lazy + guarded (G-030 doctrine): normal boots never load punk::args, and with the stack unavailable (or `PUNKBOOT_PLAIN=1`) help degrades to a plain subcommand list while parsing degrades to the historic switch semantics. Characterization: `src/tests/shell/testsuites/punkexe/launcherhelp.test`.
- Launcher help + declared parse (G-032, core 0.3.0; single-mechanism revision 2026-08-05 in core 0.5.0 - user direction): the dispatch renders and parses through punk::args with ONE help entry, the leading `help` word - `<punkexe> help ?subcommand? ?arg ...?` renders tabled usage from the `punk::args::moduledoc::punkexe` ids plus the live registrations (per-kit clones `(script)::punkexe.launcher`/`.launcherhelp`); words after the subject are accepted and ignored, so `help` can be prepended to an existing command line. Help FLAGS (-help/--help etc) are never intercepted anywhere: a leading one falls under the unknown-first-argument refusal (whose stderr usage answers it, exit 1), and after a subcommand they are the subcommand's own arguments (`tclsh` keeps full stock dash parity; app-shellspy handles -help/--help/help itself; `buildinfo` refuses any argument - one-liner + tabled usage, exit 2; a parse-declared registration rejects them like any undeclared flag). An unknown first argument naming no existing file (no `lib:*` reference, and - G-077 - not `-e`) is refused with usage (stderr, exit 1) instead of silently reclassifying to `script`. Optional per-name registration metadata `::punkboot::project_subcommand_info` (`summary`/`argsid`/`package`/`parse` - documented by `punk::args::moduledoc::punkboot`) puts project subcommands in the help tables by registration alone and can gate their arguments through a declared definition (usage errors exit 1, handler not invoked). Everything is lazy + guarded (G-030 doctrine): normal boots never load punk::args, and with the stack unavailable (or `PUNKBOOT_PLAIN=1`) help degrades to a plain subcommand list while parsing degrades to the historic switch semantics. Characterization: `src/tests/shell/testsuites/punkexe/launcherhelp.test`.
- Top-level `-e` one-liner (G-077, core 0.4.0): on kits whose unknownfirst default is `script`, a first argument of exactly `-e` is reclassified to the `script` subcommand's own `-e` handling ahead of the G-032 unknown-first-arg refusal (the whole arglist, `-e` included, becomes its arguments) - `<punkexe> -e <script> ?arg ...?` runs the code as a one-liner (app-punkscript 1.1: `::argv0` is `-e`, trailing args in `::argv`, errorInfo + exit 1 on error, a usage error when the script argument is missing, stdin never read by the form itself, non-empty final result echoed). Tool-style kits keep `-e` routed to their processor; `<punkexe> tclsh -e ...` keeps deliberate stock parity (G-118). Characterization: `src/tests/shell/testsuites/punkexe/scriptexec.test` (-e section).
- Top-level `-e` one-liner (G-077, core 0.4.0): on kits whose unknownfirst default is `script`, a first argument of exactly `-e` is reclassified to the `script` subcommand's own `-e` handling ahead of the G-032 unknown-first-arg refusal (the whole arglist, `-e` included, becomes its arguments) - `<punkexe> -e <script> ?arg ...?` runs the code as a one-liner (app-punkscript 1.1: `::argv0` is `-e`, trailing args in `::argv`, errorInfo + exit 1 on error, a usage error when the script argument is missing, stdin never read by the form itself, non-empty final result echoed). Tool-style kits keep `-e` routed to their processor; `<punkexe> tclsh -e ...` keeps deliberate stock parity (G-118). Characterization: `src/tests/shell/testsuites/punkexe/scriptexec.test` (-e section).
- VFS content must be compatible with the target platform runtime.
- VFS content must be compatible with the target platform runtime.
- A root-level `punkshell.ico` in a kit's own custom `.vfs` folder overrides
- A root-level `punkshell.ico` in a kit's own custom `.vfs` folder overrides