Browse Source

G-030 achieved: make.tcl dogfoods punk::args - tabled usage, declared subcommands, -confirm prompt policy

All 15 make.tcl subcommands are punk::args-declared ((script)::punkboot +
(script)::punkboot::<sub>, built from single-source summary/help/opt dicts with
shared option fragments and explicit @form -synopsis). Bare make.tcl / -help,
'help ?subcommand?' and '<subcommand> -help' render tabled usage; invalid
flags/subcommands raise punk::args usage errors (exit 1) with unambiguous
prefix resolution; 'shell' is a declared passthrough.

Prompt policy: new -confirm 0|1 (default 1) on every confirmation point
(bootsupport minor-staleness gate, vfscommonupdate REPLACE, kit-type mismatch).
Default prompts only on an interactive terminal and aborts fast with guidance
on non-interactive stdin (::punkboot::lib::stdin_is_interactive; Tcl 8.6 lacks
the -inputmode probe and is assumed interactive). -confirm 0 proceeds
(staleness, REPLACE) or skips the mismatched kit. The piped-y agent workflow is
retired; the old vfscommonupdate decline half-abort (printed done, exit 0) now
exits 1.

Degrade rule: punk::args/punk::ansi/textblock boot requires guarded; definition
load caught with fallback to a self-contained scan (-k/-dirty-abort/-confirm
supported) + plain punkboot_gethelp (availability NOTE factored into
punkboot_availability_note, shared by both paths); rendering degrades
independently of parsing (errorstyle minimal / plain help when the ANSI stack
is unhealthy); PUNKBOOT_PLAIN=1 forces degraded mode. punk::args joins the
bootstrap-tracked _runtime_deps staleness set (parsing contract only - the
rendering stack degrades instead; contract docs updated in
src/bootsupport/AGENTS.md + src/modules/AGENTS.md, workflow docs in
src/AGENTS.md + src/tests/shell/AGENTS.md).

Verified: usage/help/error paths, vfscommonupdate fail-fast and -confirm 0 runs
with closed stdin, staleness flag route live (args 0.5.0->0.12.0 tripped the
gate), degraded-mode help/check/info, full 'project -k -confirm 0' kit build.
Dogfooding finding recorded in the goal file: punk::args 0.12.0 reports generic
overflow where 0.5.0 reported choiceviolation for optional-choice mismatches
(worked around with required leaders; candidate error-selection improvement).

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 day ago
parent
commit
7ea1deaae5
  1. 4
      CHANGELOG.md
  2. 4
      GOALS-archive.md
  3. 4
      GOALS.md
  4. 77
      goals/G-030-maketcl-punkargs.md
  5. 2
      goals/G-032-launcher-punkargs.md
  6. 189
      goals/archive/G-030-maketcl-punkargs.md
  7. 2
      punkproject.toml
  8. 5
      src/AGENTS.md
  9. 12
      src/bootsupport/AGENTS.md
  10. 594
      src/make.tcl
  11. 2
      src/modules/AGENTS.md
  12. 2
      src/tests/shell/AGENTS.md

4
CHANGELOG.md

@ -5,6 +5,10 @@ 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.12.24] - 2026-07-14
- G-030 (achieved): make.tcl dogfoods punk::args. All 15 subcommands are punk::args-declared ((script)::punkboot definitions with single-source summaries, shared option fragments and explicit @form -synopsis lines): bare `make.tcl`/`-help` render tabled usage with grouped subcommand summaries, `make.tcl help ?subcommand?` / `<subcommand> -help` show per-subcommand definitions, invalid flags and unknown subcommands raise punk::args usage errors (exit 1) with unambiguous-prefix resolution. New `-confirm 0|1` prompt policy on every confirmation point (bootsupport minor-staleness gate, vfscommonupdate REPLACE, kit-type mismatch): default prompts only on an interactive terminal and aborts fast with guidance on non-interactive stdin (the piped-`y` agent workflow is retired - use `-confirm 0`; the old vfscommonupdate decline half-abort now exits 1). punk::args joined the bootstrap-tracked staleness set (parsing contract only - the punk::ansi/textblock rendering stack degrades instead: minimal errorstyle/plain help when unhealthy, and full scan-based fallback dispatch when punk::args itself is unavailable; PUNKBOOT_PLAIN=1 forces the degraded mode). Bootsupport snapshot refreshed (args 0.5.0->0.12.0 plus punk/lib/ns/repl/tclcore moduledoc) and layout make.tcl/bootsupport copies followed via the punkcheck channel. Docs: src/AGENTS.md, src/bootsupport/AGENTS.md, src/modules/AGENTS.md, src/tests/shell/AGENTS.md. Dogfooding finding recorded in the goal file: punk::args 0.12.0 reports a generic overflow error where 0.5.0 reported a pointed choiceviolation for optional-choice mismatches (worked around with required leaders; candidate punk::args error-selection improvement).
## [0.12.22] - 2026-07-13
- G-074 (achieved): punk::args 0.12.0 adds punk::args::formcheck - on-demand multiform ambiguity analysis. It reports the form pairs of a definition that some argument list could cleanly match simultaneously, confirming every candidate with a real parse of a synthetic witness arglist against both forms - so fully discriminated form pairs can never false-alarm, and each reported witness is a genuine multipleformmatches input. Findings classify as type-weakness (a literal/choice discriminator aligned with a permissive non-validating type such as expr/any/string - the 'lseq 1 count 5' class) vs structural (the forms genuinely share an argument shape - the 'after cancel id|script' class). The new @form -overlapallowed <formname-list> key sanctions a known/documented overlap for formcheck reporting only (parse behaviour untouched; unknown form names rejected at definition resolve); tclcore moduledoc 0.3.4 adopts it for the after cancel pair, leaving ::after with zero unsanctioned findings while ::lseq's expr-typed-end findings stay visible as actionable. New testsuite args/formcheck.test.

4
GOALS-archive.md

@ -85,6 +85,10 @@ Acceptance: LICENSE.txt exists at the repo root containing the standard BSD-2-Cl
Scope: src/modules/punk/args-999999.0a1.0.tm (get_dict value allocation, private::get_dict_can_assign_value, parse_status), src/tests/modules/punk/args/testsuites/args/ (new allocation characterization suite), src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (::lseq as the proving consumer)
Acceptance: a new allocation characterization suite drives the lseq range matrix under explicit -form range - '0 10', '0 10 2', '0 10 by 2', '0 to 10 2', '0 .. 10 by 2', '1 5 by 0' parse per the lseq.n grammar (the three currently-failing cases fixed) and '0 10 2 4' still fails - plus reduced fixtures isolating the shape (optional choice value between required values + trailing optional-member clause) independent of the moduledoc; the ::if noise-word cases (mid-clause ?literal(then)?, clause-leading ?literal(else)?) keep passing - no regression to optional clause members generally; a genuinely invalid arglist's error names the failing element (the '..|to' misblame case pinned fixed); punk::args::parse_status accepts -form (single form name/index at minimum, consistent with parse) with its status structure reporting the form used; full punk::args and punk::ns suites pass with no expectations weakened; before/after results for the probe matrix recorded in this file.
### G-030 [achieved 2026-07-14] make.tcl dogfoods punk::args: tabled usage, declared subcommands, prompt-free flags → detail: goals/archive/G-030-maketcl-punkargs.md
Scope: src/make.tcl (dispatch, help, prompts), src/bootsupport/AGENTS.md + src/modules/AGENTS.md (bootstrap-tracked staleness contract), src/modules/punk/args-999999.0a1.0.tm (only as consumed)
Acceptance: `tclsh src/make.tcl` and `-help` render punk::args tabled usage listing every subcommand with a summary, and `make.tcl help <subcommand>` (or `<subcommand> -help`) shows that subcommand's definition; invalid arguments produce a punk::args usage error rather than ad-hoc messages; every y/n prompt has a documented flag equivalent (proven at least for vfscommonupdate and the project-build confirmations: a run with the flag completes non-interactively with stdin closed) and a non-interactive stdin without the flag fails fast with usage rather than hanging or half-aborting; punk::args is added to the bootstrap-tracked buildversion set with the doc contract updated (src/bootsupport/AGENTS.md, src/modules/AGENTS.md); with bootsupport punk::args unavailable or unloadable, make.tcl still boots and `check`, `bootsupport` and `modules` remain usable with plain-text fallback help (the guarded-require degrade rule); layout make.tcl copies follow via the established sync/G-027 channels (noted, not hand-synced).
### G-074 [achieved 2026-07-13] punk::args multiform ambiguity analysis: on-demand form-overlap detection with sanctioned-overlap annotation → detail: goals/archive/G-074-punkargs-multiform-ambiguity-lint.md
Scope: src/modules/punk/args-999999.0a1.0.tm (analysis command, @form sanction key as decided in the work), src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (::after/::lseq as proving consumers, sanction adoption), src/tests/modules/punk/args/testsuites/args/ (new suite)
Acceptance: the analysis command reports the two known real cases from the G-041 closeout - ::lseq range/start_count overlap via the expr-typed end slot (unsanctioned finding: type-weakness class) and ::after cancelid/cancelscript (documented-overlap class) - and reports nothing for multiform definitions whose forms are fully discriminated (the parse withid/withdef pair, the forms.test afterish fixture); classifications distinguish at minimum type-weakness overlap (a discriminator aligned with a permissive type) from structural overlap (no discriminating slot exists); the sanction annotation silences (or downgrades to acknowledged) a listed form pair without affecting parse behaviour, is rejected at definition resolve when it names unknown forms, and is adopted for the after cancel pair in the tclcore moduledoc; the analysis is conservative in the documented direction (may miss deep ambiguities, must not false-alarm on discriminated forms - the miss/report boundary is documented with the slot model); running it performs no parse of user-supplied words and adds no work to define/resolve for definitions that never call it; a new testsuite covers the finding classes, the sanction, the unknown-form rejection and the no-finding cases; full punk::args suite passes.

4
GOALS.md

@ -166,10 +166,6 @@ Detail: goals/G-028-file-locker-identification.md
Scope: src/make.tcl (test-module packaging step), src/tests/ (source of truth), src/modules/test/ (generated #modpod targets), src/modules/punk/mix/ (modpod tooling as needed)
Detail: goals/G-029-testmodules-from-srctests.md
### G-030 [proposed] make.tcl dogfoods punk::args: tabled usage, declared subcommands, prompt-free flags
Scope: src/make.tcl (dispatch, help, prompts), src/bootsupport/AGENTS.md + src/modules/AGENTS.md (bootstrap-tracked staleness contract), src/modules/punk/args-999999.0a1.0.tm (only as consumed)
Detail: goals/G-030-maketcl-punkargs.md
### G-031 [proposed] Componentized kit boot: thin project-owned main + shared layout-owned boot core
Scope: src/vfs/_config/ (punk_main.tcl, project_main.tcl restructure), src/vfs/_vfscommon.vfs (boot core delivery), src/project_layouts/ (thin-main skeleton, via established sync channels)
Detail: goals/G-031-componentized-kit-boot.md

77
goals/G-030-maketcl-punkargs.md

@ -1,77 +0,0 @@
# G-030 make.tcl dogfoods punk::args: tabled usage, declared subcommands, prompt-free flags
Status: proposed
Scope: src/make.tcl (dispatch, help, prompts), src/bootsupport/AGENTS.md + src/modules/AGENTS.md (bootstrap-tracked staleness contract), src/modules/punk/args-999999.0a1.0.tm (only as consumed)
Goal: make.tcl - the first surface a developer sees - parses its subcommands and options via punk::args and showcases the tabled usage output for help and argument errors, every interactive y/n prompt gains a declared flag equivalent so agents can drive make.tcl with arguments instead of piped input, punk::args joins the bootstrap-tracked staleness set, and the boot phase plus the environment-repair commands keep working with degraded plain help when the bootsupport punk::args (or the table-rendering stack) is stale or unavailable.
Acceptance: `tclsh src/make.tcl` and `-help` render punk::args tabled usage listing every subcommand with a summary, and `make.tcl help <subcommand>` (or `<subcommand> -help`) shows that subcommand's definition; invalid arguments produce a punk::args usage error rather than ad-hoc messages; every y/n prompt has a documented flag equivalent (proven at least for vfscommonupdate and the project-build confirmations: a run with the flag completes non-interactively with stdin closed) and a non-interactive stdin without the flag fails fast with usage rather than hanging or half-aborting; punk::args is added to the bootstrap-tracked buildversion set with the doc contract updated (src/bootsupport/AGENTS.md, src/modules/AGENTS.md); with bootsupport punk::args unavailable or unloadable, make.tcl still boots and `check`, `bootsupport` and `modules` remain usable with plain-text fallback help (the guarded-require degrade rule); layout make.tcl copies follow via the established sync/G-027 channels (noted, not hand-synced).
## Context
make.tcl is the first surface a developer meets in a punkshell-derived project,
but its command handling predates the ecosystem's own tooling: a hand-built
help text, a bare `known_commands` list, ad-hoc argument handling per
subcommand, and interactive y/n prompts (`vfscommonupdate`'s REPLACE
confirmation, the project-build confirmations, bootsupport staleness prompts).
Eating our own dogfood - punk::args definitions per subcommand with the tabled
usage rendering - both showcases the ecosystem where it is most visible and
makes the parsing declarative.
The prompts are the practical payoff: agent-driven builds currently pipe `y`
into stdin (fragile, undiscoverable - observed 2026-07-07 while rebuilding
tomlish.exe). With punk::args parsing, prompt-free operation is just another
declared option per subcommand, and non-interactive stdin without the flag can
fail fast with usage instead of hanging on a read or half-aborting.
Precedents already in-tree: src/tests/runtests.tcl parses via punk::args
((script)::runtests definition), and the tomlish cmdline app presents tabled
usage as its first surface.
## Approach
- One PUNKARGS definition per subcommand (@cmd -summary/-help + opts), plus a
top-level definition enumerating subcommands - replacing punkboot_gethelp
and the hand-maintained help strings. `make.tcl`, `make.tcl -help`, and
per-subcommand help all render through punk::args::usage.
- Prompt policy: interactive stdin -> prompt as today (default); flag supplied
(e.g. -confirm 0 / -y, naming decision recorded here) -> proceed without
prompting; non-interactive stdin without the flag -> usage error, fast.
The bootsupport staleness prompt keeps its abort/prompt/proceed
classification but gains the same flag route.
- **Degradation is a hard requirement** (the 2026-07-06/07 incidents are the
cautionary tale: a stale bundled fileutil disabled the very make.tcl step
needed to fix it). The boot phase stays self-contained; punk::args is
guarded-lazily required post-boot (the punkboot::utils pattern documented in
src/bootsupport/AGENTS.md); when unavailable, dispatch and the repair
commands (check, bootsupport, modules) keep working with plain-text fallback
help. Note the tabled rendering also pulls punk::ansi/textblock/punk::char -
exactly the modules whose same-version staleness broke shellfilter this week
- so rendering must degrade independently of parsing (punk::args plain
output when the ANSI stack is unhealthy).
- Staleness tracking: punk::args joins the bootstrap-tracked buildversion set
(currently punkcheck, punk::repo, punk::mix, punk::tdl) with the same
bump-at-least-minor-on-call-site-API-change discipline; whether the
rendering stack is also tracked or left to degrade is an implementation
decision to record here (initial lean: track punk::args only - it is the
parsing contract; rendering degrades).
- Layout copies of make.tcl follow through the existing punkcheck-tracked
sync (custom/_project) and the G-012/G-027 channels for the rest - noted in
completion reports, never hand-synced.
## Alternatives considered
- Ad-hoc -y flags bolted onto the current hand parsing - rejected: solves the
agent problem but leaves the first-surface help un-showcased and the parsing
divergent from the ecosystem's own conventions.
- Requiring punk::args at boot (no fallback) - rejected: make.tcl must be able
to repair the very bootsupport snapshot it loads punk::args from.
## Notes
- Related: G-015 (agents driving punk tooling non-interactively), G-024
(mapvfs toml errors deserve the same declared-argument quality), G-025 (the
version-report subcommand should be born punk::args-defined), G-027 (pull
updates deliver the improved make.tcl to derived projects).
- Observation parked here from the same session: runtests/make.tcl-adjacent
tooling that returns result dicts should document their key shape (see the
runx -tcl exitcode note in G-015's detail).
- Archived-goal references in this file: G-015 achieved 2026-07-07 (goals/archive/G-015-script-subcommand-piped-stdin.md).

2
goals/G-032-launcher-punkargs.md

@ -62,4 +62,4 @@ in `-help` by registration alone.
natural first parsed subcommand; exit-code semantics), G-025 (version-report
subcommand born punk::args-defined), G-013 (the debugrepl activation review
mentions proper-command conversions that would slot into the same pattern).
- Archived-goal references in this file: G-015 achieved 2026-07-07 (goals/archive/G-015-script-subcommand-piped-stdin.md).
- Archived-goal references in this file: G-015 achieved 2026-07-07 (goals/archive/G-015-script-subcommand-piped-stdin.md); G-030 achieved 2026-07-14 (goals/archive/G-030-maketcl-punkargs.md) - the make.tcl counterpart shipped: follow its shipped decisions (`-confirm 0|1` prompt policy, explicit `@form -synopsis`, track-parsing-degrade-rendering split, required-choice leaders for pointed choiceviolation errors).

189
goals/archive/G-030-maketcl-punkargs.md

@ -0,0 +1,189 @@
# G-030 make.tcl dogfoods punk::args: tabled usage, declared subcommands, prompt-free flags
Status: achieved 2026-07-14
Scope: src/make.tcl (dispatch, help, prompts), src/bootsupport/AGENTS.md + src/modules/AGENTS.md (bootstrap-tracked staleness contract), src/modules/punk/args-999999.0a1.0.tm (only as consumed)
Goal: make.tcl - the first surface a developer sees - parses its subcommands and options via punk::args and showcases the tabled usage output for help and argument errors, every interactive y/n prompt gains a declared flag equivalent so agents can drive make.tcl with arguments instead of piped input, punk::args joins the bootstrap-tracked staleness set, and the boot phase plus the environment-repair commands keep working with degraded plain help when the bootsupport punk::args (or the table-rendering stack) is stale or unavailable.
Acceptance: `tclsh src/make.tcl` and `-help` render punk::args tabled usage listing every subcommand with a summary, and `make.tcl help <subcommand>` (or `<subcommand> -help`) shows that subcommand's definition; invalid arguments produce a punk::args usage error rather than ad-hoc messages; every y/n prompt has a documented flag equivalent (proven at least for vfscommonupdate and the project-build confirmations: a run with the flag completes non-interactively with stdin closed) and a non-interactive stdin without the flag fails fast with usage rather than hanging or half-aborting; punk::args is added to the bootstrap-tracked buildversion set with the doc contract updated (src/bootsupport/AGENTS.md, src/modules/AGENTS.md); with bootsupport punk::args unavailable or unloadable, make.tcl still boots and `check`, `bootsupport` and `modules` remain usable with plain-text fallback help (the guarded-require degrade rule); layout make.tcl copies follow via the established sync/G-027 channels (noted, not hand-synced).
## Context
make.tcl is the first surface a developer meets in a punkshell-derived project,
but its command handling predates the ecosystem's own tooling: a hand-built
help text, a bare `known_commands` list, ad-hoc argument handling per
subcommand, and interactive y/n prompts (`vfscommonupdate`'s REPLACE
confirmation, the project-build confirmations, bootsupport staleness prompts).
Eating our own dogfood - punk::args definitions per subcommand with the tabled
usage rendering - both showcases the ecosystem where it is most visible and
makes the parsing declarative.
The prompts are the practical payoff: agent-driven builds currently pipe `y`
into stdin (fragile, undiscoverable - observed 2026-07-07 while rebuilding
tomlish.exe). With punk::args parsing, prompt-free operation is just another
declared option per subcommand, and non-interactive stdin without the flag can
fail fast with usage instead of hanging on a read or half-aborting.
Precedents already in-tree: src/tests/runtests.tcl parses via punk::args
((script)::runtests definition), and the tomlish cmdline app presents tabled
usage as its first surface.
## Approach
- One PUNKARGS definition per subcommand (@cmd -summary/-help + opts), plus a
top-level definition enumerating subcommands - replacing punkboot_gethelp
and the hand-maintained help strings. `make.tcl`, `make.tcl -help`, and
per-subcommand help all render through punk::args::usage.
- Prompt policy: interactive stdin -> prompt as today (default); flag supplied
(e.g. -confirm 0 / -y, naming decision recorded here) -> proceed without
prompting; non-interactive stdin without the flag -> usage error, fast.
The bootsupport staleness prompt keeps its abort/prompt/proceed
classification but gains the same flag route.
- **Degradation is a hard requirement** (the 2026-07-06/07 incidents are the
cautionary tale: a stale bundled fileutil disabled the very make.tcl step
needed to fix it). The boot phase stays self-contained; punk::args is
guarded-lazily required post-boot (the punkboot::utils pattern documented in
src/bootsupport/AGENTS.md); when unavailable, dispatch and the repair
commands (check, bootsupport, modules) keep working with plain-text fallback
help. Note the tabled rendering also pulls punk::ansi/textblock/punk::char -
exactly the modules whose same-version staleness broke shellfilter this week
- so rendering must degrade independently of parsing (punk::args plain
output when the ANSI stack is unhealthy).
- Staleness tracking: punk::args joins the bootstrap-tracked buildversion set
(currently punkcheck, punk::repo, punk::mix, punk::tdl) with the same
bump-at-least-minor-on-call-site-API-change discipline; whether the
rendering stack is also tracked or left to degrade is an implementation
decision to record here (initial lean: track punk::args only - it is the
parsing contract; rendering degrades).
- Layout copies of make.tcl follow through the existing punkcheck-tracked
sync (custom/_project) and the G-012/G-027 channels for the rest - noted in
completion reports, never hand-synced.
## Alternatives considered
- Ad-hoc -y flags bolted onto the current hand parsing - rejected: solves the
agent problem but leaves the first-surface help un-showcased and the parsing
divergent from the ecosystem's own conventions.
- Requiring punk::args at boot (no fallback) - rejected: make.tcl must be able
to repair the very bootsupport snapshot it loads punk::args from.
## Notes
- Related: G-015 (agents driving punk tooling non-interactively), G-024
(mapvfs toml errors deserve the same declared-argument quality), G-025 (the
version-report subcommand should be born punk::args-defined), G-027 (pull
updates deliver the improved make.tcl to derived projects).
- Observation parked here from the same session: runtests/make.tcl-adjacent
tooling that returns result dicts should document their key shape (see the
runx -tcl exitcode note in G-015's detail).
- Archived-goal references in this file: G-015 achieved 2026-07-07 (goals/archive/G-015-script-subcommand-piped-stdin.md).
## Progress
2026-07-14: implemented whole and verified; punkproject 0.12.24.
### What shipped (src/make.tcl)
- Declarative dispatch: punk::args definitions `(script)::punkboot` (top level:
subcommand leader with -choicegroups/-choicelabels subtables) and
`(script)::punkboot::<subcommand>` for all 15 subcommands, built in
`::punkboot::argdoc` from single-source dicts (SUMMARIES/HELPTEXTS/SUBOPTS/
OPT_SYNOPSES) plus shared option fragments (OPT_CONFIRM/OPT_DIRTYABORT/
OPT_FORCEKILL) composed via multi-part `punk::args::define`. Explicit
`@form -synopsis` per definition so usage shows the real invocation
(`make.tcl modules ?-dirty-abort? ?-confirm 0|1?`) rather than the internal id.
- `make.tcl` / `-help` / `help ?subcommand?` / `<subcommand> -help` all render
punk::args tabled usage (the missing/broken bootsupport-package NOTE was
factored into `punkboot_availability_note` and is appended to both the tabled
and the plain-fallback help). Invalid flags and unknown subcommands raise
punk::args usage errors (exit 1); unambiguous subcommand prefixes resolve
(`make.tcl vfsc...`).
- `shell` is a declared passthrough: its definition documents that trailing args
go to the repl unparsed.
- Prompt policy - **naming decision: `-confirm 0|1` (boolean, default 1)**,
declared on every subcommand that can hit a confirmation point:
- `-confirm 1` (default): prompt when stdin is an interactive terminal;
when stdin is NOT interactive, abort fast at the confirmation point with
guidance instead of reading stdin (no more piped `y`, no half-aborts -
the old vfscommonupdate decline path printed "done" and exited 0; it now
exits 1 with "-aborted-").
- `-confirm 0`: never prompt; documented per-site resolutions: staleness
minor-bump gate -> proceed, vfscommonupdate REPLACE -> proceed, kit
source/target type mismatch -> skip that kit (only an interactive 'y'
can force building a mismatched kit).
All three `gets stdin` confirmation sites reworked; interactivity probe is
`::punkboot::lib::stdin_is_interactive` (8.7+/9 `-inputmode` on terminal
channels only; **Tcl 8.6 caveat**: no probe exists, stdin is assumed
interactive there - piped 8.6 runs should pass -confirm explicitly;
documented in the option help and bootsupport AGENTS.md).
- Staleness tracking: punk::args joined `_runtime_deps`
(`punk/args-buildversion.txt`). **Decision recorded: track punk::args only** -
it is the parsing contract; the tabled-rendering stack (punk::ansi, textblock)
stays untracked and degrades instead.
- Degrade rule implemented at three levels: (1) boot requires for
punk::args/punk::ansi/textblock are guarded (stderr note, no abort);
(2) definition build is caught - any failure falls back to a self-contained
scan (rewritten from the old hand parser; handles -k/-dirty-abort/-confirm
and help) plus plain-text punkboot_gethelp; (3) rendering degrades
independently of parsing (errorstyle minimal when the ANSI stack is
unhealthy; usage render failure falls back to plain help).
`PUNKBOOT_PLAIN=1` forces the degraded mode (troubleshooting/test hook).
- Flag plumbing: `-k` (project/vfs/bin), `-dirty-abort` (the nine
build/promotion commands) and `-confirm` now flow from parsed opts into
`::punkboot::opt_forcekill/opt_dirty_abort/opt_confirm`; the old
`lsearch $::argv` sites read those variables instead.
### Verification (all under tclsh 9.0.2, from repo root)
- Bare/`-help`/`help <sub>`/`<sub> -help`: tabled usage with grouped
subcommand/summary subtables; `help` with a bad subject and an unknown
subcommand both raise choiceviolation usage errors (exit 1);
`modules -bogus` raises invalidoption (exit 1).
- `vfscommonupdate </dev/null` -> fast abort exit 1 with -confirm guidance,
no changes; `vfscommonupdate -confirm 0 </dev/null` -> completed exit 0,
idempotent (no git churn).
- Staleness flag route proven live: adding punk::args to _runtime_deps
immediately tripped the minor gate (bootsupport 0.5.0 vs source 0.12.0):
`info </dev/null` aborted fast exit 1; `modules -confirm 0` and
`bootsupport -confirm 0` proceeded non-interactively and refreshed the
snapshot (args 0.12.0 + punk 0.2.6, lib 0.4.3, ns 0.5.0, repl 0.5.1,
tclcore moduledoc 0.3.4), after which the gate is silent.
- Degraded mode: `PUNKBOOT_PLAIN=1` runs of bare help / `check` / `info
-confirm 0` / unknown-command all work on the plain scan + gethelp
(exit codes preserved). The guarded-require path was also exercised for real
during development (a transient condition inversion ran the whole session on
the fallback - plain help rendered, flags worked).
- Full `project -k -confirm 0` regression build with stdin a pipe: completed,
kits built/installed (see repo CHANGELOG 0.12.24 entry).
- Layout make.tcl + bootsupport copies followed via the established punkcheck
channel (`make.tcl bootsupport` run; not hand-synced) - G-027 remains the
delivery route to derived projects.
### Findings on punk::args (dogfooding yield)
- Optional-arg choice-mismatch error-quality regression between 0.5.0 and
0.12.0: a leader/value with `-optional 1` and restricted -choices that
receives a non-matching word now falls through to a generic
"Received more values than can be assigned" (toomanyarguments) instead of
the pointed choiceviolation that 0.5.0 reported (verified: identical defs,
only the loaded punk::args version differs; -choicegroups and @form presence
are NOT factors). Worked around here by declaring `subcommand`/`subject`
`-optional 0` (dispatch only parses when a word is present, so the empty
case never reaches parse). Candidate punk::args improvement: single-form
error selection should prefer a nearby choiceviolation over
toomanyarguments when a rejected optional arg explains the overflow
(adjacent to the rank_form_failures work; relates to proposed G-073).
- `@cmd -name` is displayed in the COMMAND header but the auto-generated
synopsis uses the definition id - script-level definitions with ids like
`(script)::punkboot::<sub>` want an explicit `@form -synopsis` (adopted
here; possible future punk::args nicety: fall back to @cmd -name for
synopsis rendering).
### Deferred / out of scope
- The kit-type-mismatch confirmation cannot be triggered without a mismatched
runtime/target configuration - its -confirm handling is code-reviewed, not
runtime-proven (the prompt sites share the same policy helper).
- punk::args error-selection improvement above left to punk::args goals
(G-072/G-073 territory) rather than patched mid-goal.
- Old `echo y | make.tcl vfscommonupdate` agent workflow is retired; docs
updated (src/AGENTS.md, src/tests/shell/AGENTS.md, src/bootsupport/AGENTS.md,
src/modules/AGENTS.md).

2
punkproject.toml

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

5
src/AGENTS.md

@ -66,7 +66,8 @@ Recovery after a wrong path guess:
- Use `tclsh src/make.tcl vendorupdate` to refresh vendormodules from config. It warns (non-fatal) for each source project whose fossil/git checkout is dirty - vendored artifacts built from a dirty tree have no committed provenance; commit in the source project to clear the warning (enforcement policy tracked by goal G-026).
- All build/promotion commands (`project`, `packages`, `modules`, `libs`, `vfs`, `vfslibs`, `bin`, `bootsupport`, `vfscommonupdate`) warn similarly when this project's own `src/` has uncommitted fossil/git changes (dirt outside `src/` is ignored). Warn-only by default; pass `-dirty-abort` to make the check aborting. For evaluating uncommitted source without a build, prefer `<builtexe> src` / `<builtexe> src shell`. The check is `punkboot::utils::vcs_dirty_warnings` (optional scope argument) loaded guardedly from bootsupport - if the snapshot is stale/missing the check degrades to a skip notice (but `-dirty-abort` then aborts rather than silently losing the requested strictness).
- Provenance warnings (dirty-src gate + vendorupdate source-project check) print with a plain column-0 `PROVENANCE-WARNING:` token (grep for it in captured build output) and are recapped at the end of the run via make.tcl's wrapped `::exit`. Interactive terminal runs get a 3-second ctrl-c grace countdown before a dirty build proceeds; piped/agent runs skip it. `tclsh src/make.tcl check` reports the current src provenance status and what the build commands would do.
- Use `tclsh src/make.tcl vfscommonupdate` to rebuild `_vfscommon.vfs`.
- 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`).
- 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.
- Uncommitted `make.tcl`-generated outputs may be batched into one commit, regardless of how many `make.tcl` invocations produced them. This covers punkcheck-managed build outputs that are VCS-tracked: `src/bootsupport/`, `_vfscommon.vfs/modules` + `/lib`, and declared per-kit `*.vfs/lib_tcl<N>/<pkg>` subfolders. (Root `modules/`, `lib/`, `modules_tcl<N>/`, `lib_tcl<N>/` are gitignored and never committed.) Each module's old→new version rename is atomic; a regeneration reflects one build-output refresh, not independent hand-edits. This is a carve-out from generic "split aggressively" commit guidance for punkcheck-managed build outputs only — it does not cover user-curated VFS content (`_config/`, per-kit `*.vfs/` outside declared `lib_tcl<N>` subfolders, `_aside/`, `mkzipfix.vfs`, `_vfscommon.vfs/doc`), which commits separately per its own concerns.
- Use `punk make.tcl project` or `punk902z make.tcl project` inside Punk shell when building binaries through Punk.
- Binary images are platform-specific; build on each target platform rather than expecting a cross-platform flag.
@ -81,7 +82,7 @@ Recovery after a wrong path guess:
- Review VS Code Tcl lint diagnostics before submitting new agent-produced Tcl code, but do not use lint tooling to reformat existing code.
- When touching VFS payloads, describe regeneration steps in durable docs if the workflow changes.
- When a tool summary reports cleanup issues such as `git diff --check` whitespace errors, verify against exact command output and exit code before running secondary scans or reporting the issue.
- `make.tcl` performs version-aware bootsupport staleness detection: major bumps abort, minor bumps prompt (y/N), patch bumps warn-and-proceed. See `src/bootsupport/AGENTS.md` "Bootsupport Staleness Handling" for the full contract and the version-bump discipline agents must follow.
- `make.tcl` performs version-aware bootsupport staleness detection: major bumps abort, minor bumps prompt (y/N; `-confirm 0` proceeds without prompting, non-interactive stdin without it aborts fast), patch bumps warn-and-proceed. See `src/bootsupport/AGENTS.md` "Bootsupport Staleness Handling" for the full contract and the version-bump discipline agents must follow.
- Project-level version bumps are governed by the root `AGENTS.md` "Project Versioning" section. Any `src/` change that ships user-visible shell behaviour requires a `punkproject.toml` version bump and a `CHANGELOG.md` entry as part of the DOX closeout pass. Run `tclsh src/make.tcl projectversion` to verify.
## Verification

12
src/bootsupport/AGENTS.md

@ -21,17 +21,19 @@ Modules and libraries required during the build/bootstrap/make process before th
## Bootsupport Staleness Handling
`src/make.tcl` compares each tracked bootsupport module's highest `.tm` version against the corresponding `src/modules/<...>-buildversion.txt` for these runtime-critical packages: `punkcheck`, `punk::repo`, `punk::mix`, `punk::tdl`. When the source version is higher than the bootsupport version, `make.tcl` classifies the bump level from the version delta and reacts as follows (except for the `check` command, which only reports staleness at the end of its output):
`src/make.tcl` compares each tracked bootsupport module's highest `.tm` version against the corresponding `src/modules/<...>-buildversion.txt` for these runtime-critical packages: `punkcheck`, `punk::repo`, `punk::mix`, `punk::tdl`, `punk::args`. When the source version is higher than the bootsupport version, `make.tcl` classifies the bump level from the version delta and reacts as follows (except for the `check` command, which only reports staleness at the end of its output):
- **Major bump** (e.g 1.x -> 2.x): abort unconditionally. Breaking change means the old bootsupport snapshot will not work.
- **Minor bump** (e.g 0.1 -> 0.2): prompt the user (y/N, default No) whether to proceed. Backward-compat aliases *should* keep old bootsupport functional, but the user must opt in.
- **Minor bump** (e.g 0.1 -> 0.2): prompt the user (y/N, default No) whether to proceed. Backward-compat aliases *should* keep old bootsupport functional, but the user must opt in. With `-confirm 0` proceed without prompting; with the default `-confirm 1` and a non-interactive stdin, abort fast with guidance instead of reading stdin (G-030 prompt policy).
- **Patch bump** (e.g 0.1.1 -> 0.1.2): warn and proceed. No prompt.
The prompt is self-contained Tcl (modeled on `punk::lib::askuser` via `::punkboot::lib::bootsupport_prompt_yesno`) because it runs in the early boot phase before punk packages are guaranteed loaded. The classifier is `::punkboot::lib::bootsupport_bump_level`.
The prompt is self-contained Tcl (modeled on `punk::lib::askuser` via `::punkboot::lib::bootsupport_prompt_yesno`) because it runs in the early boot phase before punk packages are guaranteed loaded. The classifier is `::punkboot::lib::bootsupport_bump_level`; stdin interactivity is probed by `::punkboot::lib::stdin_is_interactive` (Tcl 8.6 lacks the `-inputmode` probe and is assumed interactive - piped 8.6 runs should pass `-confirm` explicitly).
`punk::args` is tracked because make.tcl dogfoods it for subcommand dispatch and tabled help (G-030). Deliberately, only punk::args itself joined the tracked set - it is the parsing contract; its tabled-rendering stack (`punk::ansi`, `textblock` and their deps) is untracked and left to degrade (make.tcl falls back to minimal errorstyle / plain-text help when the rendering stack is unhealthy, and to fully self-contained scan-based dispatch when punk::args itself is unavailable - the guarded-require degrade rule). `PUNKBOOT_PLAIN=1` forces the degraded dispatch/help for troubleshooting.
### Version-Bump Discipline (agent-relevant)
Because the staleness classifier keys off the version delta, under-bumping a module (e.g recording a patch bump when the call-site API actually changed) will cause `make.tcl` to mis-classify the bump and either wrongly proceed without prompting or wrongly abort. This discipline applies specifically to the four files the classifier reads — `punkcheck-buildversion.txt`, `punk/repo-buildversion.txt`, `punk/mix-buildversion.txt` (top-level `punk::mix` only, not its submodules), and `punk/tdl-buildversion.txt`. Follow the rules in `src/modules/AGENTS.md` "Versioning And Releases" exactly:
Because the staleness classifier keys off the version delta, under-bumping a module (e.g recording a patch bump when the call-site API actually changed) will cause `make.tcl` to mis-classify the bump and either wrongly proceed without prompting or wrongly abort. This discipline applies specifically to the five files the classifier reads — `punkcheck-buildversion.txt`, `punk/repo-buildversion.txt`, `punk/mix-buildversion.txt` (top-level `punk::mix` only, not its submodules), `punk/tdl-buildversion.txt`, and `punk/args-buildversion.txt`. Follow the rules in `src/modules/AGENTS.md` "Versioning And Releases" exactly:
- Call-site API change (new flags, renamed flags, changed call shapes) -> at least a **minor** bump, even if the module's own interface is unchanged.
- Backward-compatible API additions (new procs, new flags with defaults) -> **minor**.
@ -42,7 +44,7 @@ For modules outside this tracked set (including `punk::mix::util`, `punk::mix::c
If the set of runtime-critical packages in `make.tcl`'s `_runtime_deps` list ever grows, update this list (and the matching one in `src/modules/AGENTS.md`) so the two stay in sync.
After changing any build-critical module (`punkcheck`, `punk::repo`, `punk::mix`, `punk::tdl`), rebuild bootsupport with `cd src && tclsh make.tcl modules && tclsh make.tcl bootsupport` so the snapshot matches the source version and staleness does not trip.
After changing any build-critical module (`punkcheck`, `punk::repo`, `punk::mix`, `punk::tdl`, `punk::args`), rebuild bootsupport with `cd src && tclsh make.tcl modules && tclsh make.tcl bootsupport` so the snapshot matches the source version and staleness does not trip (append `-confirm 0` for unattended runs).
## Work Guidance

594
src/make.tcl

@ -20,7 +20,6 @@ namespace eval ::punkboot {
variable scriptfolder [file normalize [file dirname [info script]]]
variable foldername [file tail $scriptfolder]
variable pkg_requirements [list]; variable pkg_missing [list];variable pkg_loaded [list]
variable non_help_flags [list -k -dirty-abort]
variable help_flags [list -help --help /? -h]
variable known_commands [list project modules libs packages vfs vfslibs bin info check shell vendorupdate bootsupport vfscommonupdate projectversion]
}
@ -311,6 +310,22 @@ proc ::punkboot::lib::bootsupport_bump_level {old_ver new_ver} {
return "patch"
}
# stdin interactivity probe for the confirmation-prompt policy (G-030).
# Tcl 8.7+/9: -inputmode is supported only on terminal channels, so a successful
# query means stdin is a console. Under Tcl 8.6 there is no -inputmode anywhere and
# no reliable pure-Tcl tty test in the boot phase - we return 1 (assume interactive,
# the pre-G-030 behaviour of reading stdin) so a real 8.6 console still gets its
# prompt. Piped/non-interactive runs under 8.6 should pass -confirm explicitly.
proc ::punkboot::lib::stdin_is_interactive {} {
if {![catch {chan configure stdin -inputmode}]} {
return 1
}
if {[package vsatisfies [package provide Tcl] 8.7-]} {
return 0
}
return 1
}
# Self-contained y/N prompt. Returns 1 for y/yes (case-insensitive), 0 otherwise.
# Used by the staleness abort path to ask the user whether to proceed past a
# minor-version bump. Default is No (Enter = abort) so accidental Enter is safe.
@ -530,6 +545,7 @@ if {$bootsupport_paths_exist || $sourcesupport_paths_exist} {
} punk::repo punk/repo-buildversion.txt punk/repo-*.tm {*}{
} punk::mix punk/mix-buildversion.txt punk/mix-*.tm {*}{
} punk::tdl punk/tdl-buildversion.txt punk/tdl-*.tm {*}{
} punk::args punk/args-buildversion.txt punk/args-*.tm {*}{
}]
set _stale_modules [list]
foreach {pkg buildversion_rel bootsupport_glob} $_runtime_deps {
@ -565,9 +581,16 @@ if {$bootsupport_paths_exist || $sourcesupport_paths_exist} {
package require punk::mix
package require punkcheck
package require punk::lib
package require punk::args
package require punk::ansi
package require textblock
#G-030 degrade rule: punk::args (declarative dispatch/help) and its tabled-rendering stack
#(punk::ansi, textblock) are guarded - a stale/missing snapshot of these must never brick
#the make.tcl commands used to repair it (check, bootsupport, modules). Dispatch and help
#fall back to the self-contained scan / plain-text help further below when absent.
foreach _optpkg {punk::args punk::ansi textblock} {
if {[catch {package require $_optpkg} _opterr]} {
puts stderr "make.tcl: boot package $_optpkg unavailable from bootsupport ($_opterr) - continuing degraded (plain help/argument handling)"
}
}
unset -nocomplain _optpkg _opterr
set package_paths_modified 1
@ -1364,7 +1387,13 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " - run the punk shell using bootsupport libraries." \n
append h " $scriptname projectversion" \n
append h " - advisory check: verify CHANGELOG.md matches punkproject.toml and warn if src/ has changes since the last project-version bump." \n \n
append h " $scriptname help ?subcommand?" \n
append h " - show usage for $scriptname or one of its subcommands (equivalent: $scriptname <subcommand> -help)" \n \n
append h " Flags:" \n
append h " -confirm 0|1" \n
append h " - interactive y/n confirmation policy (default 1: prompt when stdin is a terminal, abort fast when it is not)." \n
append h " Use -confirm 0 for unattended runs: the bootsupport minor-staleness gate and the vfscommonupdate REPLACE" \n
append h " confirmation proceed without prompting; kits with a source/target kit-type mismatch are skipped." \n
append h " -dirty-abort" \n
append h " - abort build/promotion commands (project packages modules libs vfs vfslibs bin bootsupport vfscommonupdate) when src/ has" \n
append h " uncommitted VCS changes. Default is warn-only: artifacts built from dirty src have no committed provenance." \n
@ -1372,6 +1401,20 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " Use '$scriptname check' to see the current provenance status. To evaluate uncommitted source without building," \n
append h " use '<builtexe> src' or '<builtexe> src shell'." \n \n
append h "" \n
append h [punkboot_availability_note]
return $h
}
#Missing/broken bootsupport-package NOTE shown with help output.
#Factored out of punkboot_gethelp (G-030) so both the punk::args tabled help path and the
#plain-text fallback help append the same information. Returns "" when there is nothing to report.
proc ::punkboot::punkboot_availability_note {} {
variable pkg_availability
if {![info exists pkg_availability]} {
return ""
}
global A
if {![array size A]} {punkboot::define_global_ansi}
set h ""
if {[llength [dict get $pkg_availability missing]] || [llength [dict get $pkg_availability broken]]} {
set has_recommended 0
set has_nonoptional 0
@ -1389,10 +1432,10 @@ proc ::punkboot::punkboot_gethelp {args} {
if {$has_recommended || $has_nonoptional} {
append h "* $A(HIGHLIGHT)** NOTE ** ***$A(RST)" \n
append h " punk boot has detected that the following packages could not be loaded from the bootsystem path:" \n
set missing_out [get_display_missing_packages $pkg_availability]
set missing_out [get_display_missing_packages $pkg_availability]
append h $missing_out
set broken_out [get_display_broken_packages $pkg_availability]
set broken_out [get_display_broken_packages $pkg_availability]
append h $broken_out
append h "* $A(HIGHLIGHT)** *** *** ***$A(RST)" \n
@ -1402,7 +1445,7 @@ proc ::punkboot::punkboot_gethelp {args} {
append h " Call 'make.tcl check' and examine the last table (which includes bootsupport + executable-provided packages)" \n
append h " See if there are any items marked missing or broken that aren't marked as '(known optional)'" \n
append h " If all are marked (known optional) then it should work." \n
append h " A package marked (known optional) and (RECOMMENDED) may make the build/install processes run a lot faster. (e.g tcllibc)" \n
append h " A package marked (known optional) and (RECOMMENDED) may make the build/install processes run a lot faster. (e.g tcllibc)" \n
append h "* $A(HIGHLIGHT)** *** *** ***$A(RST)" \n\n
#append h "Successfully Loaded packages:" \n
#append h " " [join $::punkboot::pkg_loaded "\n "] \n
@ -1417,124 +1460,385 @@ proc ::punkboot::punkboot_gethelp {args} {
##############################################################################################################
##############################################################################################################
set scriptargs $::argv
namespace eval ::punkboot::app {
namespace ensemble create
proc help {args} {
puts "test help"
}
}
namespace eval ::punkboot::app::argdoc {
proc punkboot_subcommands {} {
dict set groups "package" {packages modules libs}
dict set groups "informational" {check info help}
return [punk::args::ensemble_subcommands_definition -groupdict $groups -columns 4 ::punkboot::app]
}
#punk::args::define {
# @id -id punkboot::app
# @cmd -name punkboot::app\
# -summary\
# "Project builder"\
# -help\
# ""
# @form -form help
# @leaders
# subcommand -type "literal(help)"
# @opts
# @values
# what -type string -choices {modules libs shell}
# @form -form modules
# subcommand -type "literal(modules)"
# @form -form libs
# subcommand -type "literal(libs)"
# @form -form shell
# subcommand -type "literal(shell)"
# arg -type any -optional 1 -multiple 1
#}
punk::args::define {
@dynamic
@id -id ::punkboot::app
@cmd -name ::punkboot::app\
-summary\
"Project builder"\
-help\
""
@leaders
${[punkboot::app::argdoc::punkboot_subcommands]}
}
}
namespace eval ::punkboot::app {
proc test {args} {
puts "test"
# ----------------------------------------------------------------------------------------------
# G-030: make.tcl dogfoods punk::args.
# Subcommands and their options are declared as punk::args definitions ((script)::punkboot and
# (script)::punkboot::<subcommand>) and parsed with punk::args::parse - 'make.tcl',
# 'make.tcl help ?subcommand?' and 'make.tcl <subcommand> -help' render tabled usage, and invalid
# arguments produce punk::args usage errors.
# Degrade rule (hard requirement - see goals/archive/G-030-maketcl-punkargs.md): punk::args is
# guarded. When it (or a definition feature it needs) is unavailable from bootsupport, dispatch
# falls back to the self-contained scan below and help falls back to plain-text
# ::punkboot::punkboot_gethelp, so the environment-repair commands (check, bootsupport, modules)
# keep working. The tabled rendering additionally depends on the punk::ansi/textblock stack -
# rendering degrades independently of parsing (minimal errorstyle / plain fallback help) when
# that stack is unhealthy. Only punk::args itself joins the bootstrap-tracked staleness set
# (_runtime_deps above): it is the parsing contract; the rendering stack is left to degrade.
# ----------------------------------------------------------------------------------------------
set ::punkboot::punkargs_ok 0
#PUNKBOOT_PLAIN=1 forces the plain fallback dispatch/help - a troubleshooting/test hook for the
#degrade contract (e.g when a half-broken punk::args snapshot loads but misbehaves).
if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBOOT_PLAIN)]} {
puts stderr "make.tcl: PUNKBOOT_PLAIN set - using plain argument handling and help"
} elseif {![catch {package require punk::args}]} {
if {![catch {
namespace eval ::punkboot::argdoc {
#single source for the subcommand one-line summaries: each subcommand definition's
#@cmd -summary and the top-level subcommand -choicelabels are both built from this.
variable SUMMARIES {
project "Full project build - packages plus kit/zipkit executables from src/vfs and src/runtime"
packages "Build .tm modules and pkgIndex.tcl libraries from src into <projectdir> (no kit executables)"
modules "Build .tm modules from src/modules, src/vendormodules etc into <projectdir>/modules etc"
libs "Build pkgIndex.tcl libraries from src/lib, src/vendorlib etc into <projectdir>/lib etc"
vfs "Build kit/zipkit executables from src/vfs and src/runtime into <projectdir>/bin"
vfslibs "Propagate declared vendored platform-library packages into kit vfs lib_tcl<N> trees"
bin "Install executables from src/bin into <projectdir>/bin, then build kits as for vfs"
vendorupdate "Update src/vendormodules based on src/vendormodules/include_modules.config"
bootsupport "Update src/bootsupport modules (and project-layout copies) from built project modules"
vfscommonupdate "Replace src/vfs/_vfscommon.vfs modules and libs with the project's built modules and libs"
info "Show the name and base folder of the project to be built"
check "Show module/library paths and any potentially problematic packages for running this script"
projectversion "Advisory check: CHANGELOG.md vs punkproject.toml and src/ changes since last version bump"
shell "Run the punk shell using bootsupport libraries"
help "Show usage for make.tcl or one of its subcommands"
}
variable HELPTEXTS {
project
"Run the full project build - includes src/vfs/* checks and builds.
Builds/copies .tm modules and pkgIndex.tcl based libraries from src to
<projectdir>/modules, <projectdir>/lib etc, propagates declared vendored
platform-libraries into kit vfs trees (as for vfslibs), then scans
src/runtime and src/vfs folders to build kit/zipkit executables and
installs them to <projectdir>/bin."
packages
"Build (or copy if build not required) both .tm and pkgIndex.tcl based
packages from src to their corresponding locations under <projectdir>.
This does not scan src/runtime and src/vfs folders to build kit/zipkit
executables."
modules
"Build (or copy if build not required) .tm modules from src/modules,
src/vendormodules etc to their corresponding locations under <projectdir>.
This does not scan src/runtime and src/vfs folders to build kit/zipkit
executables."
libs
"Build (or copy if build not required) pkgIndex.tcl based libraries from
src/lib, src/vendorlib etc to their corresponding locations under
<projectdir>.
This does not scan src/runtime and src/vfs folders to build kit/zipkit
executables."
vfs
"Scan src/vfs and src/runtime folders and build kit/zipkit executables
into <projectdir>/bin.
Does not first rebuild modules/libs - use 'make.tcl project' for the
full pipeline."
vfslibs
"Propagate declared vendored platform-library packages into kit vfs
lib_tcl<N> trees.
Declarations (per-package per-kit, with superseded-version removal)
live in src/runtime/vendorlib_vfs.toml.
Also runs automatically as part of 'make.tcl project'."
bin
"Install executables from src/bin to <projectdir>/bin, then check vfs
folders and build kit/zipkit executables as for the vfs subcommand."
vendorupdate
"Update the src/vendormodules based on src/vendormodules/include_modules.config.
Update the src/vendorlib based on src/vendorlib/config.toml (todo)."
bootsupport
"Update the src/bootsupport modules as well as the project-layout
src/bootsupport modules if those folders exist.
Bootsupport modules are pulled from locations specified in
include_modules.config files within each src/bootsupport subdirectory.
This should usually be from modules that have been built and tested in
<projectdir>/modules, <projectdir>/lib etc.
Bootsupport modules are available to make.tcl."
vfscommonupdate
"Update the src/vfs/_vfscommon.vfs from compiled src/modules and src/lib etc.
Before calling this (followed by 'make.tcl project') - you can test using
'<builtexe>(.exe) dev' - this will load modules from your <projectdir>/modules,
<projectdir>/lib paths instead of from the kit/zipkit.
Replacing the _vfscommon.vfs contents requires confirmation - see -confirm."
info
"Show the name and base folder of the project to be built."
check
"Show module/library paths and any potentially problematic packages for
running this script.
Also reports bootsupport staleness and the src provenance status (what
the build/promotion commands would do)."
projectversion
"Advisory check: verify CHANGELOG.md matches punkproject.toml and warn
if src/ has changes since the last project-version bump."
}
#shared option fragments composed into the per-subcommand definitions below
variable OPT_CONFIRM {
-confirm -type boolean -default 1 -help\
"Interactive y/n confirmation policy.
-confirm 1 (default): confirmation points prompt when stdin is an
interactive terminal. When stdin is NOT interactive (piped/closed),
make.tcl aborts fast at the confirmation point with guidance instead
of reading stdin.
-confirm 0: never prompt - each confirmation point takes its documented
non-interactive resolution:
bootsupport minor-version staleness gate -> proceed
vfscommonupdate REPLACE confirmation -> proceed
kit source/target type mismatch -> skip building that kit
Note: Tcl 8.6 lacks the terminal probe (-inputmode) so stdin is assumed
interactive there - piped runs under 8.6 should always pass -confirm
explicitly."
}
variable OPT_DIRTYABORT {
-dirty-abort -type none -default 0 -help\
"Abort when src/ has uncommitted VCS changes.
Default is warn-only: artifacts built from dirty src have no committed
provenance. Warnings carry a plain PROVENANCE-WARNING: prefix (greppable
in redirected output) and are recapped at the end of the run.
Use 'make.tcl check' to see the current provenance status. To evaluate
uncommitted source without building, use '<builtexe> src' or
'<builtexe> src shell'."
}
variable OPT_FORCEKILL {
-k -type none -default 0 -help\
"Terminate running processes matching the executable being built
(if applicable) so the build can install over it."
}
#which shared option fragments each subcommand declares
#(shell and help have bespoke definitions below: shell passes trailing args
# through to the repl untouched, help takes a subject value)
variable SUBOPTS {
project {OPT_FORCEKILL OPT_DIRTYABORT OPT_CONFIRM}
packages {OPT_DIRTYABORT OPT_CONFIRM}
modules {OPT_DIRTYABORT OPT_CONFIRM}
libs {OPT_DIRTYABORT OPT_CONFIRM}
vfs {OPT_FORCEKILL OPT_DIRTYABORT OPT_CONFIRM}
vfslibs {OPT_DIRTYABORT OPT_CONFIRM}
bin {OPT_FORCEKILL OPT_DIRTYABORT OPT_CONFIRM}
vendorupdate {OPT_CONFIRM}
bootsupport {OPT_DIRTYABORT OPT_CONFIRM}
vfscommonupdate {OPT_DIRTYABORT OPT_CONFIRM}
info {OPT_CONFIRM}
check {}
projectversion {}
}
#synopsis fragments matching the shared option fragments (explicit @form -synopsis
#so usage shows the real invocation 'make.tcl <subcommand> ...' rather than the
#internal definition id)
variable OPT_SYNOPSES {
OPT_FORCEKILL "?-k?"
OPT_DIRTYABORT "?-dirty-abort?"
OPT_CONFIRM "?-confirm 0|1?"
}
foreach {sub optvars} $SUBOPTS {
set synparts [list "make.tcl $sub"]
foreach ov $optvars {
lappend synparts [dict get $OPT_SYNOPSES $ov]
}
set defparts [list]
lappend defparts "@id -id (script)::punkboot::$sub"
lappend defparts "@cmd -name [list "make.tcl $sub"] -summary [list [dict get $SUMMARIES $sub]] -help [list [dict get $HELPTEXTS $sub]]"
lappend defparts "@form -synopsis [list [join $synparts { }]]"
lappend defparts "@opts"
foreach ov $optvars {
lappend defparts [set $ov]
}
lappend defparts "@values -min 0 -max 0"
punk::args::define {*}$defparts
}
punk::args::define\
"@id -id (script)::punkboot::shell"\
"@cmd -name [list "make.tcl shell"] -summary [list [dict get $SUMMARIES shell]] -help [list\
"Run the punk shell (repl) using bootsupport libraries.
All arguments after the shell subcommand are passed through to the
repl untouched (they are not parsed or validated by make.tcl)."]"\
{@form -synopsis "make.tcl shell ?arg ...?"}\
{@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help\
"Arguments passed through to the punk repl."}
punk::args::define\
"@id -id (script)::punkboot::help"\
"@cmd -name [list "make.tcl help"] -summary [list [dict get $SUMMARIES help]] -help [list\
"Show tabled usage for make.tcl as a whole, or for a single subcommand.
Equivalent to 'make.tcl <subcommand> -help'."]"\
{@form -synopsis "make.tcl help ?subcommand?"}\
"@values -min 1 -max 1"\
"subject -type string -optional 0 -choices [list [dict keys $SUMMARIES]] -help {Subcommand to show usage for.}"
#top-level definition: the subcommand table rendered by 'make.tcl' / 'make.tcl -help'
variable SUBGROUPS {
"build & promotion" {project packages modules libs vfs vfslibs bin}
"source maintenance" {vendorupdate bootsupport vfscommonupdate}
"informational" {info check projectversion help}
"interactive" {shell}
}
punk::args::define\
"@id -id (script)::punkboot"\
"@cmd -name make.tcl -summary {punkshell project build tool (punk boot)} -help [list\
"punk boot: make any tclkits and modules in <projectdir>/src folders and place
them and associated data files/scripts in the parent folder of src - e.g in
'bin' and 'modules' folders at the same level as 'src'.
General usage: make.tcl <subcommand> ?flags?
'make.tcl help <subcommand>' or 'make.tcl <subcommand> -help' shows a
subcommand's own usage. Interactive y/n confirmations can be driven
non-interactively with the -confirm flag declared on the relevant
subcommands."]"\
{@form -synopsis "make.tcl ?subcommand? ?flags?"}\
"@leaders -min 1 -max 1"\
"subcommand -type string -optional 0 -choicecolumns 1 -choicegroups [list $SUBGROUPS] -choicelabels [list $SUMMARIES] -help\
{Use 'make.tcl help <subcommand>' or 'make.tcl <subcommand> -help' for details of a subcommand.}"
unset -nocomplain sub optvars defparts ov
}
} _defserr]} {
set ::punkboot::punkargs_ok 1
} else {
puts stderr "make.tcl: punk::args subcommand definitions failed to load ($_defserr) - falling back to plain argument handling"
}
}
#set argd [punk::args::parse $scriptargs -form 0 withid ::punkboot::app]
###lassign [dict values $argd] leaders opts values received
#
#puts stdout [punk::args::usage -scheme nocolour ::punkboot::app]
#exit 1
#error/usage rendering style: tabled (enhanced) when the rendering stack is healthy,
#minimal plain text otherwise - rendering degrades independently of parsing.
set ::punkboot::errstyle minimal
if {$::punkboot::punkargs_ok && [package provide punk::ansi] ne "" && [package provide textblock] ne ""} {
set ::punkboot::errstyle enhanced
}
set do_help 0
if {![llength $scriptargs]} {
set do_help 1
} else {
foreach h $::punkboot::help_flags {
if {[lsearch $scriptargs $h] >= 0} {
set help_subject ""
set help_exitcode 0
#defaults for the option-derived variables (fallback scan and passthrough subcommands rely on these)
set ::punkboot::opt_forcekill 0
set ::punkboot::opt_dirty_abort 0
set ::punkboot::opt_confirm 1
if {$::punkboot::punkargs_ok} {
set first [lindex $scriptargs 0]
if {![llength $scriptargs] || $first in $::punkboot::help_flags} {
set do_help 1
} else {
#validate/resolve the subcommand word via the top-level definition
#(allows unambiguous prefixes; unknown subcommands get a punk::args usage error)
if {[catch {punk::args::parse [list $first] -errorstyle $::punkboot::errstyle withid (script)::punkboot} argd]} {
puts stderr $argd
exit 1
}
set subcommand [dict get $argd leaders subcommand]
set subargs [lrange $scriptargs 1 end]
set wants_help 0
foreach h $::punkboot::help_flags {
if {$h in $subargs} {set wants_help 1 ; break}
}
if {$subcommand eq "help"} {
set do_help 1
break
if {[llength $subargs]} {
if {[catch {punk::args::parse $subargs -errorstyle $::punkboot::errstyle withid (script)::punkboot::help} argd]} {
puts stderr $argd
exit 1
}
set help_subject [dict get $argd values subject]
}
} elseif {$wants_help} {
set do_help 1
set help_subject $subcommand
} elseif {$subcommand eq "shell"} {
#declared passthrough: everything after 'shell' goes to the repl unparsed
set ::punkboot::command shell
} else {
if {[catch {punk::args::parse $subargs -errorstyle $::punkboot::errstyle withid (script)::punkboot::$subcommand} argd]} {
puts stderr $argd
exit 1
}
set ::punkboot::command $subcommand
set _opts [dict get $argd opts]
foreach {_optname _var} {-k opt_forcekill -dirty-abort opt_dirty_abort -confirm opt_confirm} {
if {[dict exists $_opts $_optname]} {
set ::punkboot::$_var [dict get $_opts $_optname]
}
}
unset -nocomplain _opts _optname _var
}
}
}
set commands_found [list]
foreach a $scriptargs {
if {![string match -* $a]} {
lappend commands_found $a
} else {
if {$a ni $::punkboot::non_help_flags} {
} else {
#self-contained fallback scan (degraded mode: punk::args or a needed definition feature
#unavailable). Keeps check/bootsupport/modules and the -k/-dirty-abort/-confirm flags working.
set commands_found [list]
set argsleft $scriptargs
while {[llength $argsleft]} {
set a [lindex $argsleft 0]
set argsleft [lrange $argsleft 1 end]
if {[string match -* $a] || $a eq "/?"} {
switch -- $a {
-k {set ::punkboot::opt_forcekill 1}
-dirty-abort {set ::punkboot::opt_dirty_abort 1}
-confirm {
set v [lindex $argsleft 0]
set argsleft [lrange $argsleft 1 end]
if {![string is boolean -strict $v]} {
puts stderr "make.tcl: -confirm requires a boolean value (got '$v')"
exit 1
}
set ::punkboot::opt_confirm [expr {bool($v)}]
}
default {
if {$a in $::punkboot::help_flags} {
set do_help 1
} else {
puts stderr "Unknown flag: $a\n"
set do_help 1
set help_exitcode 1
}
}
}
} else {
lappend commands_found $a
}
}
if {![llength $scriptargs] || [lindex $commands_found 0] eq "help"} {
set do_help 1
} elseif {!$do_help} {
if {[llength $commands_found] != 1} {
set do_help 1
set help_exitcode 1
} elseif {[lindex $commands_found 0] ni $::punkboot::known_commands} {
puts stderr "Unknown command: [lindex $commands_found 0]\n\n"
set do_help 1
set help_exitcode 1
}
}
}
if {[llength $commands_found] != 1 } {
set do_help 1
} elseif {[lindex $commands_found 0] ni $::punkboot::known_commands} {
puts stderr "Unknown command: [lindex $commands_found 0]\n\n"
set do_help 1
if {!$do_help} {
set ::punkboot::command [lindex $commands_found 0]
}
}
if {$do_help} {
puts stdout "Checking package availability..."
set ::punkboot::pkg_availability [::punkboot::check_package_availability -quiet 1 $::punkboot::bootsupport_requirements]
set ::punkboot::pkg_availability [::punkboot::check_package_availability -quiet 1 $::punkboot::bootsupport_requirements]
foreach pkg_request [dict get $::punkboot::pkg_availability loaded] {
#puts stderr "---> $pkg_request"
lassign $pkg_request pkgname vrequest
set vloaded [dict get $::punkboot::pkg_availability info $pkg_request version] ;#version that was selected to load in response to vrequest during test
#catch {package require $pkgname {*}$vrequest} ;#todo
package require $pkgname {*}$vrequest ;#todo
#package require $pkgname $vloaded-$vloaded
}
puts stdout [::punkboot::punkboot_gethelp]
exit 0
set help_shown 0
if {$::punkboot::punkargs_ok} {
#tabled usage via punk::args - degrade to the plain fallback help if the rendering
#stack (punk::ansi/textblock etc) can't produce it
set usage_id (script)::punkboot
if {$help_subject ne ""} {
set usage_id (script)::punkboot::$help_subject
}
if {![catch {punk::args::usage $usage_id} usage_out]} {
puts stdout $usage_out
set availability_note [::punkboot::punkboot_availability_note]
if {$availability_note ne ""} {
puts stdout $availability_note
}
set help_shown 1
} else {
puts stderr "make.tcl: punk::args tabled usage unavailable ($usage_out) - falling back to plain help"
}
}
if {!$help_shown} {
puts stdout [::punkboot::punkboot_gethelp]
}
exit $help_exitcode
}
set ::punkboot::command [lindex $commands_found 0]
if {[lsearch $::argv -k] >= 0} {
set forcekill 1
} else {
set forcekill 0
}
set forcekill $::punkboot::opt_forcekill
#puts stdout "::argv $::argv"
# ----------------------------------------
@ -1574,11 +1878,20 @@ if {[info exists ::punkboot::stale_bootsupport] && $::punkboot::command ne "chec
exit 1
}
if {$_have_minor} {
if {![::punkboot::lib::bootsupport_prompt_yesno "Minor-version staleness backward-compat aliases should keep old bootsupport functional."]} {
#G-030 prompt policy: -confirm 0 proceeds without prompting; with the default -confirm 1
#a non-interactive stdin aborts fast with guidance instead of reading stdin.
if {!$::punkboot::opt_confirm} {
puts stderr "-proceeding with stale bootsupport (-confirm 0 supplied)-"
} elseif {![::punkboot::lib::stdin_is_interactive]} {
puts stderr "-aborted- (minor-version bootsupport staleness and stdin is not interactive)"
puts stderr " Update bootsupport (recommended - see above), or rerun with -confirm 0 to proceed with stale bootsupport."
exit 1
} elseif {![::punkboot::lib::bootsupport_prompt_yesno "Minor-version staleness backward-compat aliases should keep old bootsupport functional."]} {
puts stderr "-aborted- by user"
exit 1
} else {
puts stderr "-proceeding with stale bootsupport at user's request-"
}
puts stderr "-proceeding with stale bootsupport at user's request-"
}
# patch-only staleness: warn and proceed (warning already printed above)
}
@ -1669,7 +1982,7 @@ if {![llength [info commands ::punkboot::exit_original]]} {
}
if {$::punkboot::command in {project packages modules libs vfs vfslibs bin bootsupport vfscommonupdate}} {
set dirty_abort [expr {[lsearch $::argv -dirty-abort] >= 0}]
set dirty_abort $::punkboot::opt_dirty_abort
lassign [::punkboot::get_src_provenance_warnings $projectroot "make.tcl $::punkboot::command"] have_scoped_dirty_check dirty_warnings
if {$have_scoped_dirty_check} {
if {[llength $dirty_warnings]} {
@ -2054,29 +2367,41 @@ if {$::punkboot::command eq "vfscommonupdate"} {
puts "script: [info script]"
puts stdout "Updating vfs/_vfscommon.vfs"
puts stdout "REPLACE src/vfs/_vfscommon.vfs/* with project's modules and libs?? y|n"
if {[gets stdin] eq "y"} {
puts proceeding...
proc vfscommonupdate {projectroot} {
file delete -force $projectroot/src/vfs/_vfscommon.vfs/modules
file copy $projectroot/modules $projectroot/src/vfs/_vfscommon.vfs/
#temp? (avoid zipfs mkimg windows dotfile bug)
file delete $projectroot/src/vfs/_vfscommon.vfs/modules/.punkcheck
file delete -force $projectroot/src/vfs/_vfscommon.vfs/lib
file copy $projectroot/lib $projectroot/src/vfs/_vfscommon.vfs/
#temp?
file delete $projectroot/src/vfs/_vfscommon.vfs/lib/.punkcheck
#G-030 prompt policy: -confirm 0 proceeds without prompting; with the default -confirm 1
#a non-interactive stdin aborts fast with guidance instead of reading stdin.
set _proceed 0
if {!$::punkboot::opt_confirm} {
puts stdout "REPLACE src/vfs/_vfscommon.vfs/* with project's modules and libs (-confirm 0 supplied - proceeding without prompting)"
set _proceed 1
} elseif {![::punkboot::lib::stdin_is_interactive]} {
puts stderr "-aborted- (vfscommonupdate REPLACE confirmation required but stdin is not interactive)"
puts stderr " Rerun with -confirm 0 to proceed without prompting (see 'make.tcl vfscommonupdate -help')."
exit 1
} else {
puts stdout "REPLACE src/vfs/_vfscommon.vfs/* with project's modules and libs?? y|n"
if {[string tolower [string trim [gets stdin]]] eq "y"} {
set _proceed 1
}
vfscommonupdate $projectroot
} else {
puts aborting...
}
if {!$_proceed} {
puts stderr "-aborted- by user (vfscommonupdate: no changes made)"
exit 1
}
puts proceeding...
proc vfscommonupdate {projectroot} {
file delete -force $projectroot/src/vfs/_vfscommon.vfs/modules
file copy $projectroot/modules $projectroot/src/vfs/_vfscommon.vfs/
#temp? (avoid zipfs mkimg windows dotfile bug)
file delete $projectroot/src/vfs/_vfscommon.vfs/modules/.punkcheck
file delete -force $projectroot/src/vfs/_vfscommon.vfs/lib
file copy $projectroot/lib $projectroot/src/vfs/_vfscommon.vfs/
#temp?
file delete $projectroot/src/vfs/_vfscommon.vfs/lib/.punkcheck
}
vfscommonupdate $projectroot
puts stdout "\nvfscommonupdate done "
flush stderr
@ -3860,13 +4185,24 @@ foreach vfstail $vfs_tails {
puts stderr "WARNING: mismatch between kit type of source runtime [punk::ansi::a brightyellow]$extract_kit_type[punk::ansi::a] and target kit type [punk::ansi::a brightyellow]$target_kit_type[punk::ansi::a]"
puts stderr "The resulting kit is unlikely to initialise"
puts stderr "--------------------------------------------"
puts stdout "Continue to try to build $targetkit from $rtname anyway? y|n"
if {[string tolower [gets stdin]] eq "y"} {
puts proceeding...
#G-030 prompt policy: only an interactive 'y' can force building a mismatched
#kit - non-interactive runs (and -confirm 0) skip this kit and continue.
set _build_anyway 0
if {!$::punkboot::opt_confirm} {
puts stderr "skipping kit $targetkit (source/target kit type mismatch; -confirm 0 policy is to skip)"
} elseif {![::punkboot::lib::stdin_is_interactive]} {
puts stderr "skipping kit $targetkit (source/target kit type mismatch and stdin is not interactive)"
} else {
puts stdout "Continue to try to build $targetkit from $rtname anyway? y|n"
if {[string tolower [string trim [gets stdin]]] eq "y"} {
set _build_anyway 1
}
}
if {!$_build_anyway} {
#skip to next kit
continue
}
puts proceeding...
}
if {[file exists $sourcefolder/vfs/_vfscommon.vfs]} {

2
src/modules/AGENTS.md

@ -319,7 +319,7 @@ return
#0.1.2 - fixed path_relative delegation edge case on Windows
```
This keeps the changelog discoverable alongside the version number without requiring a separate CHANGES file.
- **Bootstrap-tracked files only**`punkcheck-buildversion.txt`, `punk/repo-buildversion.txt`, `punk/mix-buildversion.txt` (the top-level `punk::mix` file only; `punk::mix::util`, `punk::mix::cli`, and `punk/mix/commandset/*` version independently and are not covered by this check), and `punk/tdl-buildversion.txt`: bump at least **minor** whenever a call site inside that file is updated to use a new API, even if the file's own interface is unchanged. `src/make.tcl` reads only these four files to classify bootsupport staleness (major=abort, minor=prompt, patch=silent-proceed); under-bumping one of them to patch when the call-site change is more significant causes `make.tcl` to mis-classify and either wrongly proceed without prompting or wrongly abort. See `src/bootsupport/AGENTS.md` "Bootsupport Staleness Handling" for the full contract.
- **Bootstrap-tracked files only**`punkcheck-buildversion.txt`, `punk/repo-buildversion.txt`, `punk/mix-buildversion.txt` (the top-level `punk::mix` file only; `punk::mix::util`, `punk::mix::cli`, and `punk/mix/commandset/*` version independently and are not covered by this check), `punk/tdl-buildversion.txt`, and `punk/args-buildversion.txt` (added by G-030: make.tcl dogfoods punk::args for dispatch/help; only the parsing contract is tracked — the punk::ansi/textblock rendering stack degrades instead): bump at least **minor** whenever a call site inside that file is updated to use a new API, even if the file's own interface is unchanged. `src/make.tcl` reads only these five files to classify bootsupport staleness (major=abort, minor=prompt, patch=silent-proceed); under-bumping one of them to patch when the call-site change is more significant causes `make.tcl` to mis-classify and either wrongly proceed without prompting or wrongly abort. See `src/bootsupport/AGENTS.md` "Bootsupport Staleness Handling" for the full contract.
- **All other modules** (including `punk::mix` submodules): call-site updates follow ordinary judgement from the Patch/Minor/Major rules above — a behavior-preserving call-site change is a patch (or no bump if genuinely a no-op); reserve minor for changes that add capability to the module's *own* API. `src/make.tcl` does not read these versions, so there is no automated consequence, but keep the changelog accurate since it is the only record of the module's real semantic version.
- **Manually versioned modules** (currently only `punk::libunknown`): the real `major.minor.patch` version is the filename suffix and the `package provide` block value; there is no `<name>-buildversion.txt` and the build does not stamp a version. The Patch/Minor/Major bump rules above apply identically — an agent changing such a module bumps by (1) renaming the file (`git mv`) to the new version, (2) updating the `# Application <name> <version>` Meta line, the doctools `manpage_begin` version and the provide-block `set version`, and (3) appending a changelog comment line to the version-history block in the module header (it substitutes for the buildversion.txt changelog). Before the first bump of such a module, verify nothing requires it by exact version or hardcoded filename (for punk::libunknown: punk_main.tcl and punk::repl glob `libunknown-*.tm` and pick the highest by vcompare, bootsupport's include_modules.config lists it by name only, and all requires are unversioned - verified 2026-07-11). New modules should use the magic version mechanism instead.
- Modules with the magic version number must not appear in output paths such as `<projectdir>/modules`.

2
src/tests/shell/AGENTS.md

@ -18,7 +18,7 @@ Tests for shell-level behavior, command-line execution, and stdin/stdout interac
- Tests that spawn the built executable must be hang-proof: use the event-loop `punk_run` pattern (pipe stdin, half-close for EOF, timeout then force-kill) rather than plain `exec`, because known failure modes leave the child waiting on a reopened console.
- `testsuites/punkexe/shellexit.test` guards piped-stdin termination behaviour of the built executable (no `invalid thread handle` on shutdown races, no console-reopen hang on eof/error paths, exitcode propagation); do not weaken these tests - a red result means a regression in punk::repl shutdown or app-punkshell eof handling.
- `testsuites/punkexe/scriptexec.test` covers the `script` subcommand (app-punkscript, G-015): piped execution + honest exit codes, result echo, file-form argv, the `lib:<name>` scriptlib-resolution mechanism, and the tclsh-matching Tk main-loop behaviour. The `lib:` and Tk cases resolve committed fixtures under `scriptlib/_punktest/` (test-owned; a `lib:`-resolvable fixture cannot live in a tcltest temp dir since resolution only searches scriptlib locations relative to the exe). Tk cases are gated by the `punktk` constraint, auto-detected by probing whether the kit can load Tk.
- punkexe tests run against the built binary: after changing punk::repl or app-punkshell source, rebuild via `make.tcl packages`, `make.tcl vfscommonupdate` (requires interactive `y`), then `make.tcl project` before trusting results; `make.tcl project` alone does not refresh `_vfscommon.vfs`.
- punkexe tests run against the built binary: after changing punk::repl or app-punkshell source, rebuild via `make.tcl packages`, `make.tcl vfscommonupdate -confirm 0` (unattended runs must pass the flag - the REPLACE confirmation aborts fast on non-interactive stdin instead of reading a piped `y`), then `make.tcl project` before trusting results; `make.tcl project` alone does not refresh `_vfscommon.vfs`.
- `testsuites/binscripts/runtimebash_wsl.test` (G-059) exercises the runtime.cmd unix payload (`src/scriptapps/bin/runtime.bash`) through WSL: active/use/run resolution, env override, stale-active guidance, single-candidate fallback, and `list -remote` local-vs-server comparison (offline via a crafted cached sha1sums.txt + `PUNKBIN_URL` pointed at an unreachable address). Gated by the `wsllinux` capability constraint from `src/tests/testsupport/wslprobe.tcl`; all execution happens in a WSL-native staging dir, and a final test asserts the Windows checkout's git status is unchanged by the run.
- `testsuites/punkexe/staticruntime.test` (G-058) targets a kit built on a runtime with statically-linked packages: resolved from `env(PUNK_STATICKIT_TEST_EXE)`, else `<projectroot>/bin/punk91.exe`, gated by the capability-probed `statickitavailable` constraint (a probe run must show a captured `::punkboot::static_packages` baseline including Thread - not mere file existence). It asserts repl boot without the codethread Thread failure, Thread resolution in the code interp / fabricated interps / snapshot-seeded threads, and twapi resolution per the static-vs-bundled policy. The verification runtime (tclsfe-x64.exe) is pinned in the punkbin artifact repo so the constraint is satisfiable off the original dev machine. Kit script-exec paths must be passed with forward slashes.
- GOAL tests (constraint `punkgoals`, enabled by env `PUNK_TEST_GOALS=1`) cover the piped-stdin-then-interactive-shell drop-in (`shellpipe_eof_then_interactive_shell`, `shellpipe_script_and_eof_then_interactive_shell`); they are excluded from normal runs because they require an openable console in the test environment and briefly attach a live shell to it - they pass in console-attached environments against a current build.

Loading…
Cancel
Save