Browse Source

G-169 achieved: app-owned launch-subcommand documentation consumed by the launcher (app-punkscript 1.2, core 0.6.0, punkexe moduledoc 0.8.0)

User-directed activation and implementation in one arc (proposed -> active at
the user's word with the activation-freshness survey recorded, -> achieved +
archived on acceptance). Co-location kills the doc-drift class: the actual
'-e' parsing and its punk::args documentation now live in the same file.

app-punkscript 1.2 + new package app-punkscript-docs 1.2 (same file):
src/lib/app-punkscript/punkscript.tcl carries the (script)::punkexe::script
definition (the G-168 selection-sound shape moved VERBATIM - value strings
byte-identical, renders match the pre-handover moduledoc) in
::app::punkscript::argdoc with an idempotent docs section - a plain
namespace-variable set plus a membership-guarded lappend to
::punk::args::register::NAMESPACES; no package requires, no punk::args
dependency, no definition evaluation (lazy on the first query naming the id -
the docpackages mechanism pinned ahead in docpackages.test). pkgIndex.tcl
provides app-punkscript-docs from the SAME file via an execution gate
(::app::punkscript::docs_only set around the source; the file returns after
the docs section when the gate is up, and 'package provide app-punkscript'
sits below the gate) - requiring the docs NEVER runs a script, and a later
app require re-sources with the gate down so the app runs normally (probed:
docs-then-app in one process, file/'-e' one-liner/'-e'-alone usage error all
behave identically through the new file).

punkboot core 0.6.0: new launcher_builtin_docpackages map (script ->
app-punkscript-docs); launcher_show_help's built-in arm does a guarded lazy
'package require' of the mapped docs package before the usage query -
help-path only, never on boot/dispatch (ready-probe and dispatch untouched).
The G-031 registration-model header comment now names the durable doc id and
records the app-owned 'package'-key option. Copies synced byte-identical:
project-0.1 layout + modpod-template layout.

punkexe moduledoc 0.8.0 (single-definer handover): (script)::punkexe::script
is no longer defined here - a pointer comment stands in its place and the
Overview records the ownership rule (docs live with the owning code:
app-owned for app-handled subcommands, this moduledoc for core-owned
surfaces like tclsh/buildinfo/help and the top-level dispatch). Single
definer verified: loading both sources raises no duplicate-definition
(undefine) notices - the handover withdraws the moduledoc entry in the same
change-set per the docpackages last-loaded-wins pre-test pin.

Durable-doc decision (the acceptance's 'location decided in the work'): the
registration definition id (script)::punkboot::registration IS the durable
doc for the *_main.tcl declaration keys (summary/argsid/package/parse incl
the parse=1 gate, all already documented there); punkboot moduledoc 0.3.1
extends its 'package' key text with the app-owned docs-package pattern (gate
shape pointed at src/lib/app-punkscript/pkgIndex.tcl). Kit authors reach it
from the core header contract, the punkexe moduledoc Overview and the
project_main.tcl worked examples.

Help-path load cost measured and recorded (goal file): the app-ownership
marginal cost is the guarded docs require - 3.5 ms (inert). The ~396 ms
first-render total is the one-time punk::ansi/textblock render-stack load +
definition evaluation, incurred identically wherever the definition lives;
boot/dispatch cost is zero.

Tests: launcherhelp.test 25/25 - 24 existing pins unchanged plus new
launcherhelp_appowned_script_docs: the fake-main harness (G-031 shape, no
bake) run through the baked kit's tclsh subcommand in SRC package mode,
where the source tree serves the nested boot with version precedence - the
src moduledoc defines no script id, so the successful tabular render
carrying the G-168-era scriptfile help text (line-break-safe markers; the
oneliner form's rows are not in the default form-0 table) proves the
app-owned lazy resolution; stderr asserted free of 'clearing existing data'
notices. Harness lesson recorded: manual tm/auto_path prepends in a fake
main are WIPED by the nested boot's path configuration - the src package
mode is the correct lever. Full modules/punk/args subtree 337 passed / 3
pre-existing skips / 0 failed on BOTH tclsh90 and tclsh87 - the G-168
punkexemodel.test/formcheck.test pins now load the script id via
app-punkscript-docs (src/lib derived from the test file location) and guard
the handover shape at its new home. The 10 failures in maketclbakelist.test
/ maketcllibfetch.test are PRE-EXISTING (reproduced identically at HEAD
with this change-set stashed; the G-139 libfetch-test reading is a recorded
pending user decision). Degraded/plain path intact (PUNKBOOT_PLAIN pin
unchanged; mixed promotion states degrade gracefully by construction).
Minted punkexe-0.8.0.tm + punkboot-0.3.1.tm via make.tcl modules.

Goals: G-169 entry moved to GOALS-archive.md, detail file to goals/archive/
with mechanism, decisions, measured costs and verification evidence; two
open Follow-ons (the coherent-set vfscommonupdate/bake promotion carrying
lib/app-punkscript 1.2 + core 0.6.0 + punkexe 0.8.0 together; extending app
ownership to shell/punk/shellspy). Reference sweep: G-170's Related line
updated (the defs' home now decided - its formhint un-suppression edits
both homes). src/lib AGENTS.md app-punkscript bullet + src/tests/modules
AGENTS.md punk/args bullet extended. goals_lint clean (81 active / 89
archived).

Claude-Session: https://claude.ai/code/session_0156PuejSCGjgeGb7jiABrDU
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 2 weeks ago
parent
commit
97e4aacd4f
  1. 4
      GOALS-archive.md
  2. 4
      GOALS.md
  3. 46
      goals/G-169-app-owned-launch-docs.md
  4. 7
      goals/G-170-launcher-help-depth.md
  5. 153
      goals/archive/G-169-app-owned-launch-docs.md
  6. 2
      src/lib/AGENTS.md
  7. 10
      src/lib/app-punkscript/pkgIndex.tcl
  8. 120
      src/lib/app-punkscript/punkscript.tcl
  9. 9
      src/modules/punk/args/moduledoc/punkboot-999999.0a1.0.tm
  10. 3
      src/modules/punk/args/moduledoc/punkboot-buildversion.txt
  11. 103
      src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm
  12. 3
      src/modules/punk/args/moduledoc/punkexe-buildversion.txt
  13. 31
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/vfs/_config/punkboot_core.tcl
  14. 31
      src/project_layouts/vendor/punk/project-0.1/src/vfs/_config/punkboot_core.tcl
  15. 2
      src/tests/modules/AGENTS.md
  16. 14
      src/tests/modules/punk/args/testsuites/args/formcheck.test
  17. 14
      src/tests/modules/punk/args/testsuites/args/punkexemodel.test
  18. 27
      src/tests/shell/testsuites/punkexe/launcherhelp.test
  19. 31
      src/vfs/_config/punkboot_core.tcl

4
GOALS-archive.md

@ -20,6 +20,10 @@ Do not edit archived records here except to fix a broken path. Archived detail f
## Archived goals
### G-169 [achieved 2026-08-05] App-owned launch-subcommand documentation consumed by the launcher → detail: goals/archive/G-169-app-owned-launch-docs.md
Scope: src/lib/app-punkscript/punkscript.tcl (carries its subcommand's punk::args definition via inert registration); src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm (single-definer handover); src/vfs/_config/punkboot_core.tcl (built-in help resolution via app-owned ids, *_main.tcl declaration surface documented, + synced copies via established channels); src/tests/shell/testsuites/punkexe/launcherhelp.test
Acceptance: app-punkscript carries the script definition and 'help script' renders it on a baked kit via lazy resolution (guarded package require + docpackages/argsid - no eager load at boot; help-path load cost measured and recorded); the punkexe moduledoc no longer defines the id (no duplicate-definition warnings; single definer verified); degraded/plain help path intact; the *_main.tcl declaration keys are documented where kit authors look (location decided in the work: core header contract + a durable doc) including the parse=1 gate; launcherhelp.test passes with a new app-owned-resolution pin. (Achieved: app-punkscript/app-punkscript-docs 1.2 - the definition (G-168 shape verbatim) inertly registered in the app's own file, provided docs-only via an execution-gated source; punkboot core 0.6.0 launcher_builtin_docpackages guarded lazy require on the help path; punkexe moduledoc 0.8.0 handover with the ownership rule recorded; durable doc = (script)::punkboot::registration (punkboot moduledoc 0.3.1) + core header + project_main.tcl examples; docs require measured 3.5 ms (render stack dominates identically at any home); launcherhelp.test 25/25 incl the src-mode app-owned-resolution pin, args subtree green tclsh90+tclsh87.)
### G-168 [achieved 2026-08-05] Selection-sound launch definitions: script/tclsh forms match dispatch reality → detail: goals/archive/G-168-selection-sound-launch-definitions.md
Scope: src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm ((script)::punkexe::script + ::tclsh form discrimination); src/lib/app-punkscript/punkscript.tcl (behaviour oracle, error-surface decision); src/vfs/_config/punkboot_core.tcl (only if the dispatch-parse decision adopts gating, + synced copies via established channels); src/tests/modules/punk/args/testsuites/args/ (new model-parity suite); src/tests/shell/testsuites/punkexe/launcherhelp.test (as touched)
Acceptance: against the script definition, parse/parse_status select oneliner for {-e <code> ...}, select file for {<word> ...} where <word> ne -e, and {-e} alone reports noformmatch with oneliner viable-incomplete (the app's "-e requires a script argument" reality); against tclsh, {file args} selects scriptfile, a leading-dash word selects stdin, and an incomplete -encoding tail falls through to stdin (stock parity); formcheck reports no unsanctioned findings; a real-vs-model probe matrix (G-055 method) against actual dispatch/app-punkscript behaviour is recorded in this file; whether DISPATCH adopts parse-gating for script/tclsh (tabled pointed errors replacing app-punkscript's plain ones) is decided and recorded with rationale and, if adopted, a measured script-launch latency delta; the new behaviour is pinned in the args testsuite and launcherhelp.test passes unchanged (or with deliberately updated pins named here). (Achieved at punkexe moduledoc 0.7.0: file form scriptfile -regexprefail {^-e$} + ./-e message, oneliner '-e' as required solo flag ('-e' alone verdicts viable-incomplete via the G-164 re-probe); tclsh scriptfile {^-} fence + stdin firstarg {^[^-]} fence with the complete '-encoding <name> <scriptfile>' shape the recorded sanctioned LIMIT (@form -overlapallowed; parse raises multipleformmatches per G-041 no-silent-preference); dispatch parse-gating decided NOT adopted; probe matrix + decisions in the detail file; new punkexemodel.test + 3 formcheck GAP flips, args subtree green tclsh90+tclsh87, launcherhelp.test 24/24 unchanged.)

4
GOALS.md

@ -402,10 +402,6 @@ Detail: goals/G-166-cmdinfo-unavailable-surfacing.md
Scope: src/modules/punk/args-999999.0a1.0.tm (form selection/candidacy availability filter + tailored raises, option-scan unavailable-name recognition, synopsis/table renderers - annotated form lines, unavailable option rows, automated other-shapes note, formstatus 'available' field, -form display-vs-parse split); src/modules/punk/ns-999999.0a1.0.tm (cmdhelp -return text form-section heading annotation; form narrowing inherits the availability filter); src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (adoption on curated real 8.6-vs-9 deltas); src/tests/modules/punk/args/testsuites/args/ (new suite + formviability/tclcoreparity extensions)
Detail: goals/G-167-punkargs-version-delta-availability.md
### G-169 [proposed] App-owned launch-subcommand documentation consumed by the launcher
Scope: src/lib/app-punkscript/punkscript.tcl (carries its subcommand's punk::args definition via inert registration); src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm (single-definer handover); src/vfs/_config/punkboot_core.tcl (built-in help resolution via app-owned ids, *_main.tcl declaration surface documented, + synced copies via established channels); src/tests/shell/testsuites/punkexe/launcherhelp.test
Detail: goals/G-169-app-owned-launch-docs.md
### G-170 [proposed] Launcher help depth: form narrowing, parse-true dry-run, actionable form hints
Scope: src/vfs/_config/punkboot_core.tcl (help arm, + synced copies via established channels); punkexe launch definitions wherever homed after G-168/G-169 (formhint un-suppression); src/tests/shell/testsuites/punkexe/launcherhelp.test
Detail: goals/G-170-launcher-help-depth.md

46
goals/G-169-app-owned-launch-docs.md

@ -1,46 +0,0 @@
# G-169 App-owned launch-subcommand documentation consumed by the launcher
Status: proposed
Scope: src/lib/app-punkscript/punkscript.tcl (carries its subcommand's punk::args definition via inert registration); src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm (single-definer handover); src/vfs/_config/punkboot_core.tcl (built-in help resolution via app-owned ids, *_main.tcl declaration surface documented, + synced copies via established channels); src/tests/shell/testsuites/punkexe/launcherhelp.test
Goal: A subcommand's punk::args definition can live in its handling app and the launcher consumes it - proven on app-punkscript owning (script)::punkexe::script (inert PUNKARGS registration, no load-time side effects, lazily resolved at help/error time) with the moduledoc entry handed over (one definer per id); the ownership rule is recorded (docs live with the owning code - app-owned for app-handled subcommands, moduledoc for core-owned surfaces like tclsh); and the existing *_main.tcl project-subcommand declaration surface (summary/package/argsid/parse) is documented as the official authoring mechanism it already is.
Acceptance: app-punkscript carries the script definition and 'help script' renders it on a baked kit via lazy resolution (guarded package require + docpackages/argsid - no eager load at boot; help-path load cost measured and recorded); the punkexe moduledoc no longer defines the id (no duplicate-definition warnings; single definer verified); degraded/plain help path intact; the *_main.tcl declaration keys are documented where kit authors look (location decided in the work: core header contract + a durable doc) including the parse=1 gate; launcherhelp.test passes with a new app-owned-resolution pin.
## Context
2026-08-05 discussion: the actual '-e' parsing lives in app-punkscript while its
documentation lives in the punkexe moduledoc - co-location kills that drift
class. Nothing structural prevents launcher consumption of app-owned docs: the
argsid+package indirection and parse=1 gate already exist for project
subcommands (punkboot_core.tcl:1779-1791, 2168-2192), and punk::args 0.12.7's
docpackages association loads a documenting package lazily on first usage query.
The moduledoc pattern remains right where implementation is external or
core-owned (tclcore precedent: separation + parity pins).
## Notes
- Coordination with G-168 (achieved 2026-08-05 - goals/archive/G-168-selection-sound-launch-definitions.md):
G-168 landed first, so THIS goal is the one that lands second and edits the
definitions at their then-current home (never two definers) - the re-homed
script definition carries G-168's fencing shape (file form scriptfile
-regexprefail {^-e$} + ./-e message, oneliner '-e' as a required solo flag)
and any tclsh re-home carries the {^-}/{^[^-]} fences plus the stdin form's
-overlapallowed {scriptfile} sanction; the punkexemodel.test /
formcheck.test pins guard the handover.
- Related: G-075 (proposed) - sibling documentation-reachability surface.
- Related: G-042/G-043 (proposed) - adjacent declaration-composition patterns;
deliberate non-overlap (launch surface, not repl subshells).
- Overlap survey 2026-08-05: app-punkscript intersects no live goals; the
punkboot_core.tcl matches are the G-168 survey's set (G-089 nearest, others
incidental).
- 2026-08-05 pre-test pins (new args-tier docpackages.test, green tclsh90+tcl87):
registration in ::punk::args::register::NAMESPACES is inert - the next
update_definitions call id-SCANS the source without evaluating definitions,
id_exists never triggers loading, and real_id/usage lazily load the
registered source on the first query naming the documented namespace,
including tag-prefixed script-level families (the (script)::punkexe::* path
this goal rides). Duplicate ids resolve last-loaded-wins accompanied only by
a stderr undefine notice - the engine carries a recorded detect-duplicates
todo (punk::args update_definitions scan loop) - so the single-definer
handover must withdraw the moduledoc entry in the same change-set that lands
app ownership rather than rely on any warning surface
(docpackages_duplicate_definers_last_registered_wins).

7
goals/G-170-launcher-help-depth.md

@ -20,7 +20,12 @@ builds the destination, then points at it.
recorded LIMIT (the complete '-encoding <name> <scriptfile>' shape raises a
sanctioned multipleformmatches naming both forms - the honest two-form
diagnosis for that line; G-168's Progress records the rationale).
- Related: G-169 (proposed) - the defs' home determines where templates land.
- Related: G-169 (achieved 2026-08-05 - goals/archive/G-169-app-owned-launch-docs.md) -
the defs' home determines where templates land: now decided - the script
definition (and its interim -formhint "" suppression this goal replaces)
lives in src/lib/app-punkscript/punkscript.tcl (app-punkscript-docs);
tclsh and the other core-owned definitions stay in the punkexe moduledoc.
The un-suppression arm edits BOTH homes.
- Related: G-151 (achieved 2026-08-05 - goals/archive/G-151-punkargs-annotated-success-render.md) -
the dry-run arm's received-args report is G-151's punk::args-level annotated
success render, now landed as punk::args::parse_report (punk::args 0.18.0,

153
goals/archive/G-169-app-owned-launch-docs.md

@ -0,0 +1,153 @@
# G-169 App-owned launch-subcommand documentation consumed by the launcher
Status: achieved 2026-08-05
Scope: src/lib/app-punkscript/punkscript.tcl (carries its subcommand's punk::args definition via inert registration); src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm (single-definer handover); src/vfs/_config/punkboot_core.tcl (built-in help resolution via app-owned ids, *_main.tcl declaration surface documented, + synced copies via established channels); src/tests/shell/testsuites/punkexe/launcherhelp.test
Goal: A subcommand's punk::args definition can live in its handling app and the launcher consumes it - proven on app-punkscript owning (script)::punkexe::script (inert PUNKARGS registration, no load-time side effects, lazily resolved at help/error time) with the moduledoc entry handed over (one definer per id); the ownership rule is recorded (docs live with the owning code - app-owned for app-handled subcommands, moduledoc for core-owned surfaces like tclsh); and the existing *_main.tcl project-subcommand declaration surface (summary/package/argsid/parse) is documented as the official authoring mechanism it already is.
Acceptance: app-punkscript carries the script definition and 'help script' renders it on a baked kit via lazy resolution (guarded package require + docpackages/argsid - no eager load at boot; help-path load cost measured and recorded); the punkexe moduledoc no longer defines the id (no duplicate-definition warnings; single definer verified); degraded/plain help path intact; the *_main.tcl declaration keys are documented where kit authors look (location decided in the work: core header contract + a durable doc) including the parse=1 gate; launcherhelp.test passes with a new app-owned-resolution pin.
## Context
2026-08-05 discussion: the actual '-e' parsing lives in app-punkscript while its
documentation lives in the punkexe moduledoc - co-location kills that drift
class. Nothing structural prevents launcher consumption of app-owned docs: the
argsid+package indirection and parse=1 gate already exist for project
subcommands (punkboot_core.tcl:1779-1791, 2168-2192), and punk::args 0.12.7's
docpackages association loads a documenting package lazily on first usage query.
The moduledoc pattern remains right where implementation is external or
core-owned (tclcore precedent: separation + parity pins).
## Notes
- Coordination with G-168 (achieved 2026-08-05 - goals/archive/G-168-selection-sound-launch-definitions.md):
G-168 landed first, so THIS goal is the one that lands second and edits the
definitions at their then-current home (never two definers) - the re-homed
script definition carries G-168's fencing shape (file form scriptfile
-regexprefail {^-e$} + ./-e message, oneliner '-e' as a required solo flag)
and any tclsh re-home carries the {^-}/{^[^-]} fences plus the stdin form's
-overlapallowed {scriptfile} sanction; the punkexemodel.test /
formcheck.test pins guard the handover.
- Related: G-075 (proposed) - sibling documentation-reachability surface.
- Related: G-170 (proposed) - back-pointer added at activation 2026-08-05
(activation-freshness survey; G-170 already points here): its help-depth
arms render whatever home this goal gives the definitions, and its
formhint un-suppression edits them at that home.
- Related: G-042/G-043 (proposed) - adjacent declaration-composition patterns;
deliberate non-overlap (launch surface, not repl subshells).
- Overlap survey 2026-08-05: app-punkscript intersects no live goals; the
punkboot_core.tcl matches are the G-168 survey's set (G-089 nearest, others
incidental).
- 2026-08-05 pre-test pins (new args-tier docpackages.test, green tclsh90+tcl87):
registration in ::punk::args::register::NAMESPACES is inert - the next
update_definitions call id-SCANS the source without evaluating definitions,
id_exists never triggers loading, and real_id/usage lazily load the
registered source on the first query naming the documented namespace,
including tag-prefixed script-level families (the (script)::punkexe::* path
this goal rides). Duplicate ids resolve last-loaded-wins accompanied only by
a stderr undefine notice - the engine carries a recorded detect-duplicates
todo (punk::args update_definitions scan loop) - so the single-definer
handover must withdraw the moduledoc entry in the same change-set that lands
app ownership rather than rely on any warning surface
(docpackages_duplicate_definers_last_registered_wins).
## Progress
Achieved 2026-08-05 in one session (user-directed activation and
implementation same day; app-punkscript 1.2 + app-punkscript-docs 1.2,
punkexe moduledoc 0.8.0, punkboot moduledoc 0.3.1, punkboot core 0.6.0).
Mechanism landed:
- src/lib/app-punkscript/punkscript.tcl carries the (script)::punkexe::script
definition (the G-168 selection-sound shape moved VERBATIM - value strings
byte-identical, so renders match the pre-handover moduledoc) in
::app::punkscript::argdoc, with an idempotent docs section: a plain
namespace-variable set plus a membership-guarded lappend to
::punk::args::register::NAMESPACES - no package requires, no punk::args
dependency, no definition evaluation (evaluation stays lazy on the first
query naming the id, per the docpackages pre-test pins).
- Docs-only loading: pkgIndex.tcl provides package app-punkscript-docs 1.2
from the SAME file via an execution gate (::app::punkscript::docs_only set
around the source; punkscript.tcl returns after the docs section when the
gate is up, and 'package provide app-punkscript' moves below the gate) -
requiring the docs NEVER runs a script, and a later
'package require app-punkscript' re-sources with the gate down so the app
runs normally (probed: docs-then-app in one process, APPRAN both orders,
'-e'-alone usage error intact, no duplicate registrations).
- Launcher consumption: punkboot_core 0.6.0 adds launcher_builtin_docpackages
(dict: script -> app-punkscript-docs); launcher_show_help's built-in arm
does a guarded 'package require' of the mapped docs package before the
usage query - help-path only, never on boot/dispatch (the ready-probe and
dispatch paths are untouched). Copies synced: project-0.1 layout +
modpod-template layout byte-identical to the master (the template copy also
re-synced by make.tcl modules' punkcheck step).
- Single-definer handover: the punkexe moduledoc (0.8.0) no longer defines
the id - a pointer comment stands in its place and the Overview records the
ownership rule (docs live with the owning code: app-owned for app-handled
subcommands, moduledoc for core-owned surfaces like tclsh/buildinfo/help
and the top-level dispatch).
Decision - durable-doc location for the *_main.tcl declaration keys (the
acceptance's 'location decided in the work'): the registration definition id
(script)::punkboot::registration in the punkboot moduledoc IS the durable
doc - it already documented all four project_subcommand_info keys
(summary/argsid/package/parse incl the parse=1 gate) and its 'package' key
text now also documents the app-owned docs-package pattern (0.3.1, with the
gate shape pointed at src/lib/app-punkscript/pkgIndex.tcl). The core header
contract (punkboot_core.tcl G-031 registration-model comment) now names that
id explicitly and records the app-owned option; worked examples remain in
the project_main.tcl template (verified present: summary/argsid example
block). Kit authors reach the durable doc from the core header, the punkexe
moduledoc Overview, and the template.
Help-path load cost (measured 2026-08-05, tclsh90, src tree): the
app-ownership marginal cost is the guarded docs require - 3.5 ms (inert:
namespace-variable set + registration lappend + gate return). The first
'help script' render measured 396 ms total, dominated by the one-time
punk::ansi/textblock render-stack load + definition evaluation that occurs
IDENTICALLY wherever the definition lives - so app ownership adds only the
~3.5 ms require to the help path, and nothing to boot/dispatch.
Verification evidence:
- launcherhelp.test 25/25: the 24 existing pins unchanged plus the new
launcherhelp_appowned_script_docs pin - the fake-main harness (G-031
shape, no bake) run THROUGH the baked kit's tclsh subcommand in src
package mode, where the source tree serves the nested boot with version
precedence: the src moduledoc defines no script id, so the successful
tabular render carrying the G-168-era scriptfile help text (line-break-safe
markers) proves app-owned lazy resolution; stderr asserted free of
duplicate-definition ('clearing existing data') notices. Post-bake the
kit-internal stack carries the same set and the render is identical.
(Harness lesson recorded: manual tm/auto_path prepends in a fake main are
WIPED by the nested boot's path configuration - the src package mode is
the correct lever, and it resolves the project from the executable's own
location.)
- Full modules/punk/args subtree 337 passed / 3 pre-existing skips / 0
failed on BOTH tclsh90 and tclsh87 - the G-168 punkexemodel.test /
formcheck.test pins now load the script id via app-punkscript-docs
(src/lib derived from the test file location) and guard the handover
exactly as the G-168 coordination note intended.
- Src-level probes: moduledoc 0.8.0 loaded first -> id_exists 0 for the
script id; app-punkscript-docs require -> registered, app NOT run,
usage renders, forms {file oneliner}, G-168 selections/verdicts/formcheck
all intact through the app-owned definition; no undefine notices.
- App parity probes (the restructure must not break the runner): file/'-e'
one-liner/'-e'-alone-usage-error/docs-then-app sequences all behave
identically through the new file (src paths; template-provider warnings
are minimal-path environment noise, absent in kit contexts).
- shell/testsuites/punkexe subtree: launcherhelp 25/25 and the other
launcher-adjacent suites green; the 10 failures in maketclbakelist.test /
maketcllibfetch.test are PRE-EXISTING (reproduced identically at HEAD
with this change-set stashed; the G-139 libfetch-test reading is a
recorded pending user decision from the 2026-08-05 follow-on sweep).
- Minted punkexe-0.8.0.tm + punkboot-0.3.1.tm via make.tcl modules.
- Degraded/plain path intact: the PUNKBOOT_PLAIN launcherhelp pin passes
unchanged, and mixed promotion states degrade gracefully by construction
(old core never requires the docs package and the old moduledoc still
defines the id; new core + old lib falls to the guarded-require failure
and the plain summary line).
## Follow-ons
Follow-on: the next vfscommonupdate/bake promotion carries the coherent set together - src/lib/app-punkscript (pkgIndex + punkscript.tcl 1.2), punkboot core 0.6.0 and punkexe moduledoc 0.8.0 (plus punkboot moduledoc 0.3.1) - after which the baked kit serves 'help script' from the app-owned definition in internal mode; mixed intermediate states degrade gracefully as recorded in Progress => open
Follow-on: extend app ownership to the other app-handled built-ins (shell/punk via app-punkshell, shellspy via app-shellspy) whose moduledoc entries are summary-level passthrough contracts today - same docs-package pattern, launcher_builtin_docpackages gains their entries => open

2
src/lib/AGENTS.md

@ -16,7 +16,7 @@ Source of truth for all editable `pkgIndex.tcl`-based library packages. These ar
- Libraries here include:
- `app-punk/` — Punk REPL app entry (`app-punk::repl`)
- `app-punkshell/` — Punkshell app entry (`app-punkshell`); on eof of its input channel the `env(PUNK_PIPE_EOF)` policy (`exit`|`interactive`|unset=console heuristic) decides between terminating and reopening the console for an interactive repl — automated callers should set `exit` or end piped input with an explicit `exit`
- `app-punkscript/` — lean one-shot script runner behind the punk executable `script` subcommand (G-015): runs a file, a `lib:<name>` scriptlib script (resolved via `punk::path::scriptlib_resolve`; an extensionless call also matches an extensionless file whose first lines identify it as tcl - `# tcl` comment or tcl shebang incl. the sh-trampoline - with the called spelling winning when both spellings exist), or piped stdin in the default punk shell module/alias environment with honest exit codes; no shellfilter stacks or logging side effects, never falls into an interactive shell — the reliable path for automated/agent callers (prefer this over piping into `shell`); stdin form echoes the script's final result when non-empty; GUI parity with `tclsh` — a script leaving a registered Tk main loop is serviced until its window closes (so `script app.tcl` keeps a Tk app alive), while console scripts and errored scripts exit immediately; G-077 (1.1): a first argument of exactly `-e` runs the second argument as a Tcl one-liner (`::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 as for the stdin form) — also reachable as top-level `<punkexe> -e` via a boot-core reclassification (core 0.4.0)
- `app-punkscript/` — lean one-shot script runner behind the punk executable `script` subcommand (G-015): runs a file, a `lib:<name>` scriptlib script (resolved via `punk::path::scriptlib_resolve`; an extensionless call also matches an extensionless file whose first lines identify it as tcl - `# tcl` comment or tcl shebang incl. the sh-trampoline - with the called spelling winning when both spellings exist), or piped stdin in the default punk shell module/alias environment with honest exit codes; no shellfilter stacks or logging side effects, never falls into an interactive shell — the reliable path for automated/agent callers (prefer this over piping into `shell`); stdin form echoes the script's final result when non-empty; GUI parity with `tclsh` — a script leaving a registered Tk main loop is serviced until its window closes (so `script app.tcl` keeps a Tk app alive), while console scripts and errored scripts exit immediately; G-077 (1.1): a first argument of exactly `-e` runs the second argument as a Tcl one-liner (`::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 as for the stdin form) — also reachable as top-level `<punkexe> -e` via a boot-core reclassification (core 0.4.0); G-169 (1.2): the file also carries and inertly registers the `(script)::punkexe::script` punk::args definition (single definer — handed over from the punkexe moduledoc, G-168 selection-sound shape unchanged), provided docs-only as package `app-punkscript-docs` through an execution-gated source of the same file (pkgIndex.tcl sets `::app::punkscript::docs_only`; the gate returns before the app body, so requiring the docs never runs a script) — the launcher's help path requires it lazily (punkboot_core 0.6.0 `launcher_builtin_docpackages`), and the ownership rule is docs-live-with-owning-code (app-owned for app-handled subcommands, moduledoc for core-owned surfaces)
- `app-shellspy/` — ShellSpy app entry (`app-shellspy`)
- `app_shell/` — Shell app helpers (`app_shell`)
- `app_shellrun/` — Shell run helpers (`app_shellrun`)

10
src/lib/app-punkscript/pkgIndex.tcl

@ -1 +1,9 @@
package ifneeded app-punkscript 1.1 [list source [file join $dir punkscript.tcl]]
package ifneeded app-punkscript 1.2 [list source [file join $dir punkscript.tcl]]
#G-169: docs-only load of the same file - the execution gate in punkscript.tcl
#returns before the app body, so requiring the docs never runs a script. Used
#by the launcher's help path (guarded lazy require in punkboot_core.tcl).
package ifneeded app-punkscript-docs 1.2 [list ::apply {{dir} {
namespace eval ::app::punkscript {variable docs_only 1}
source [file join $dir punkscript.tcl]
namespace eval ::app::punkscript {variable docs_only 0}
}} $dir]

120
src/lib/app-punkscript/punkscript.tcl

@ -1,4 +1,122 @@
package provide app-punkscript 1.1
#G-169: launch-subcommand documentation lives WITH the parsing code it describes
#(co-location kills the doc-drift class): this file carries the
#(script)::punkexe::script punk::args definition and registers it inertly - a
#plain namespace-variable write plus a membership-guarded registration lappend.
#No package requires, no punk::args dependency, no definition evaluation here
#(the G-030 degradation doctrine; punk::args evaluates the definition lazily on
#the first help/usage query naming the id - the docpackages mechanism).
#Two load contexts share this file (see pkgIndex.tcl):
# package require app-punkscript the app: docs section, then the script
# runner body below
# package require app-punkscript-docs docs only - the launcher's help path
# (guarded lazy require in
# punkboot_core.tcl launcher_show_help):
# the execution gate below returns before
# the app body, so requiring the docs
# NEVER runs a script
#The docs section is idempotent (set + guarded lappend): a docs-then-app
#require sequence re-sources this file without duplicate registrations.
namespace eval ::app::punkscript::argdoc {
variable PUNKARGS
#single definer for (script)::punkexe::script (G-169 handover from the
#punkexe moduledoc, which no longer defines it). The G-168 selection-sound
#shape is contract - guarded by the args-tier punkexemodel.test /
#formcheck.test pins: the file form fences scriptfile against exactly '-e'
#(with the ./-e message), the oneliner form declares '-e' as a required
#solo flag so a bare '-e' verdicts viable-incomplete.
#NOTE: the braced definition below keeps the punkexe moduledoc's internal
#indentation (directives at 12, continuations at 16/18) so the definition
#TEXT is byte-identical to the pre-handover moduledoc entry - braced -help
#values carry their leading whitespace into the render.
set PUNKARGS [list [list {
@id -id (script)::punkexe::script
#-formhint suppressed (empty): the ' i -form N ...' default is not
#typeable at the CLI where this renders, and launcher help is
#subject-only (no per-form narrowing to hint at). Interim until
#such narrowing exists (G-170 owns the un-suppression).
@cmd -name "punkexe script" -&
-summary -&
"Run a script non-interactively in the punk script environment." -&
-formhint "" -&
-help -&
"Run a Tcl script and exit with an honest exit code
(app-punkscript): script errors exit nonzero and the launch
plumbing emits nothing of its own on stdout/stderr, so
exec-style callers see only the script's output. The default
punk module/alias environment is loaded (unlike 'tclsh').
The script comes from the file argument, from piped stdin when
no argument is given (terminal stdin without a script argument
is a usage error - no interactive fallback), or via scriptlib
resolution when the argument has the form 'lib:name' (resolved
from the scriptlib locations associated with the executable,
including extensionless and shebang'd scripts).
A first argument of exactly '-e' runs the SECOND argument as a
Tcl one-liner (G-077): non-interactive, ::argv0 is '-e',
arguments after the script text land in ::argv, and a
non-empty final result is echoed to stdout (the same one-shot
eval ergonomics as the stdin form). '-e' with no following
script argument is a usage error - never an interactive
fall-through, and never stock tclsh's -e misparse (argv-swallow
plus stdin read). The form works with or without piped stdin
present; stdin is read only if the one-liner itself reads it.
A file literally named '-e' stays reachable as './-e'. The
same one-liner is available at the top level as
'<punkexe> -e <script> ?arg ...?'.
Help flags are ordinary script arguments in every position
(no interception - 2026-08-05 single-mechanism revision):
use '<punkexe> help script' for this usage."
@form -form {file} -synopsis "<punkexe> script ?scriptfile|lib:name? ?arg ...?"
@values -min 0 -max -1
scriptfile -type string -optional 1 -&
-regexprefail {^-e$} -&
-regexprefailmsg -&
"the script subcommand's scriptfile may not be exactly '-e' (reserved for the -e one-liner form; a file literally named -e is reachable as ./-e)" -&
-help -&
"Script file path, or lib:name for scriptlib resolution.
Omitted: the script is read from piped stdin.
Any first word other than exactly '-e' is a file path here -
including other dash-led words ('-x' names a file, not an
option; G-168 selection-soundness: the fence on exactly '-e'
makes form auto-selection agree with dispatch)."
arg -type any -optional 1 -multiple 1 -help -&
"Arguments passed to the script in ::argv."
@form -form {oneliner} -synopsis "<punkexe> script -e <script> ?arg ...?"
@opts
-e -type none -optional 0 -help -&
"The one-liner selector (G-077): required, and only meaningful
as the first word after 'script' - a later '-e' is an
ordinary script/file argument (the file form applies). '-e'
with no following script argument is a usage error - never an
interactive fall-through. (G-168: declared as a required solo
flag so a bare '-e' verdicts the form viable-incomplete -
matching the app's 'requires a script argument' reality.)"
@values -min 1 -max -1
script -type string -optional 0 -help -&
"Tcl code to evaluate as a one-liner (G-077): errors print
errorInfo to stderr with exit 1; a non-empty final result
is echoed to stdout."
arg -type any -optional 1 -multiple 1 -help -&
"Arguments passed to the one-liner in ::argv (::argv0 is '-e')."
}]]
}
namespace eval ::punk::args::register {
#inert docpackages registration (punk::args 0.12.7 lane): id-scanned on the
#next update_definitions, definitions evaluated only on a query naming them
variable NAMESPACES
if {![info exists NAMESPACES] || "::app::punkscript::argdoc" ni $NAMESPACES} {
lappend NAMESPACES ::app::punkscript::argdoc
}
}
package provide app-punkscript-docs 1.2
if {[info exists ::app::punkscript::docs_only] && $::app::punkscript::docs_only} {
#docs-only load context (app-punkscript-docs): stop before the app body
return
}
package provide app-punkscript 1.2
#Lean one-shot script runner for the punk executable 'script' subcommand (goal G-015).
#
#Contract (G-015 - see goals/archive/G-015-script-subcommand-piped-stdin.md):

9
src/modules/punk/args/moduledoc/punkboot-999999.0a1.0.tm

@ -124,7 +124,14 @@ tcl::namespace::eval punk::args::moduledoc::punkboot {
own -help handling regardless.)
package package to require (guarded) before resolving
argsid - typically a moduledoc-style package
carrying the definition
carrying the definition, or an app-owned docs
package: an execution-gated source of the app's
own file, so the definition lives beside the
parsing code it describes and the help path
never runs the app (the app-punkscript-docs
precedent for the built-in 'script' subcommand -
G-169; see src/lib/app-punkscript/pkgIndex.tcl
for the gate shape)
parse boolean (default 0): validate the subcommand's
arguments through argsid BEFORE the handler runs;
a punk::args usage error (stderr, exit 1) stops

3
src/modules/punk/args/moduledoc/punkboot-buildversion.txt

@ -1,6 +1,7 @@
0.3.0
0.3.1
#First line must be a semantic version number
#all other lines are ignored.
#0.3.1 - documentation-only (G-169): the registration definition's 'package' key text now documents the app-owned docs-package pattern (an execution-gated source of the app's own file, so a subcommand's definition lives beside its parsing code and the help path never runs the app - the app-punkscript-docs precedent for the built-in 'script' subcommand, with the gate shape in src/lib/app-punkscript/pkgIndex.tcl). This registration definition is the recorded durable-doc location for the *_main.tcl declaration keys (summary/argsid/package/parse incl the parse=1 gate), alongside the punkboot_core.tcl header contract and the project_main.tcl worked examples.
#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.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

103
src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm

@ -33,14 +33,22 @@
#*** !doctools
#[section Overview]
#[para] These definitions are the documentation home for the launch subcommand
#[para] family of the punk executables: tclsh, script, shell, punk, shellspy,
#[para] buildinfo, help (G-118 item 10). The launcher help/parse wiring (G-032)
#[para] renders and parses from them at dispatch time: the boot core's help
#[para] These definitions are the documentation home for the CORE-OWNED part of
#[para] the launch subcommand family of the punk executables: tclsh, shell,
#[para] punk, shellspy, buildinfo, help and the top-level dispatch (G-118 item
#[para] 10). The 'script' subcommand's definition is APP-OWNED (G-169): it
#[para] lives in src/lib/app-punkscript/punkscript.tcl beside the code that
#[para] parses those arguments, provided docs-only as package
#[para] app-punkscript-docs - the ownership rule is docs-live-with-owning-code
#[para] (app-owned for app-handled subcommands, this moduledoc for core-owned
#[para] surfaces). The launcher help/parse wiring (G-032)
#[para] renders and parses from these ids at dispatch time: the boot core's help
#[para] surface clones (script)::punkexe / (script)::punkexe::help per kit with
#[para] the live subcommand set, and per-subcommand ids back the 'help
#[para] <subcommand>' renders and the declared argument gates (the single help
#[para] mechanism - -help flag forms withdrawn 2026-08-05). Id convention follows the
#[para] mechanism - -help flag forms withdrawn 2026-08-05), with app-owned ids
#[para] lazily loaded via their docs package on the help path. Id convention
#[para] follows the
#[para] make.tcl precedent for script-level surfaces: (script)::punkexe and
#[para] (script)::punkexe::<subcommand>, with explicit @form -synopsis overrides
#[para] since a constructed id is not an invocable command name.
@ -342,77 +350,20 @@ tcl::namespace::eval punk::args::moduledoc::punkexe {
#is the help route). buildinfo below is the dispatch-parsed
#subcommand (exact @values contract enforced through punk::args).
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
lappend PUNKARGS [list {
@id -id (script)::punkexe::script
#-formhint suppressed (empty): as for tclsh above - no CLI 'i', no
#per-form help narrowing. Interim until such narrowing exists.
@cmd -name "punkexe script" -&
-summary -&
"Run a script non-interactively in the punk script environment." -&
-formhint "" -&
-help -&
"Run a Tcl script and exit with an honest exit code
(app-punkscript): script errors exit nonzero and the launch
plumbing emits nothing of its own on stdout/stderr, so
exec-style callers see only the script's output. The default
punk module/alias environment is loaded (unlike 'tclsh').
The script comes from the file argument, from piped stdin when
no argument is given (terminal stdin without a script argument
is a usage error - no interactive fallback), or via scriptlib
resolution when the argument has the form 'lib:name' (resolved
from the scriptlib locations associated with the executable,
including extensionless and shebang'd scripts).
A first argument of exactly '-e' runs the SECOND argument as a
Tcl one-liner (G-077): non-interactive, ::argv0 is '-e',
arguments after the script text land in ::argv, and a
non-empty final result is echoed to stdout (the same one-shot
eval ergonomics as the stdin form). '-e' with no following
script argument is a usage error - never an interactive
fall-through, and never stock tclsh's -e misparse (argv-swallow
plus stdin read). The form works with or without piped stdin
present; stdin is read only if the one-liner itself reads it.
A file literally named '-e' stays reachable as './-e'. The
same one-liner is available at the top level as
'<punkexe> -e <script> ?arg ...?'.
Help flags are ordinary script arguments in every position
(no interception - 2026-08-05 single-mechanism revision):
use '<punkexe> help script' for this usage."
@form -form {file} -synopsis "<punkexe> script ?scriptfile|lib:name? ?arg ...?"
@values -min 0 -max -1
scriptfile -type string -optional 1 -&
-regexprefail {^-e$} -&
-regexprefailmsg -&
"the script subcommand's scriptfile may not be exactly '-e' (reserved for the -e one-liner form; a file literally named -e is reachable as ./-e)" -&
-help -&
"Script file path, or lib:name for scriptlib resolution.
Omitted: the script is read from piped stdin.
Any first word other than exactly '-e' is a file path here -
including other dash-led words ('-x' names a file, not an
option; G-168 selection-soundness: the fence on exactly '-e'
makes form auto-selection agree with dispatch)."
arg -type any -optional 1 -multiple 1 -help -&
"Arguments passed to the script in ::argv."
@form -form {oneliner} -synopsis "<punkexe> script -e <script> ?arg ...?"
@opts
-e -type none -optional 0 -help -&
"The one-liner selector (G-077): required, and only meaningful
as the first word after 'script' - a later '-e' is an
ordinary script/file argument (the file form applies). '-e'
with no following script argument is a usage error - never an
interactive fall-through. (G-168: declared as a required solo
flag so a bare '-e' verdicts the form viable-incomplete -
matching the app's 'requires a script argument' reality.)"
@values -min 1 -max -1
script -type string -optional 0 -help -&
"Tcl code to evaluate as a one-liner (G-077): errors print
errorInfo to stderr with exit 1; a non-empty final result
is echoed to stdout."
arg -type any -optional 1 -multiple 1 -help -&
"Arguments passed to the one-liner in ::argv (::argv0 is '-e')."
}]
#(script)::punkexe::script is NOT defined here (G-169 single-definer
#handover 2026-08-05): the definition lives with the code that parses
#the arguments it describes - src/lib/app-punkscript/punkscript.tcl,
#inertly registered there and provided docs-only as package
#app-punkscript-docs (an execution-gated source of the app's own
#file, so requiring the docs never runs a script). The launcher's
#help path lazily requires that package (punkboot_core.tcl
#launcher_show_help via launcher_builtin_docpackages).
#Ownership rule this records (G-169): docs live with the owning
#code - app-owned for app-handled subcommands (script), moduledoc
#for core-owned surfaces (tclsh and the dispatch-level definitions
#here). The G-168 selection-sound shape travelled with the
#definition unchanged and stays pinned by punkexemodel.test /
#formcheck.test regardless of home.
# -- --- --- --- --- --- --- --- --- --- --- --- --- ---
lappend PUNKARGS [list {
@id -id (script)::punkexe::shell

3
src/modules/punk/args/moduledoc/punkexe-buildversion.txt

@ -1,6 +1,7 @@
0.7.0
0.8.0
#First line must be a semantic version number
#all other lines are ignored.
#0.8.0 - G-169 single-definer handover: (script)::punkexe::script is no longer defined here - the definition (G-168 selection-sound shape unchanged, byte-identical value strings) moved to src/lib/app-punkscript/punkscript.tcl beside the code that parses those arguments, inertly registered there and provided docs-only as package app-punkscript-docs (an execution-gated source of the app's own file; requiring the docs never runs a script). A pointer comment stands where the definition lived; the Overview records the ownership rule (docs live with the owning code - app-owned for app-handled subcommands, this moduledoc for core-owned surfaces like tclsh/buildinfo/help and the top-level dispatch). The launcher (punkboot_core 0.6.0) lazily requires the docs package on the script help path via the new launcher_builtin_docpackages map - guarded, help-path only, never on boot/dispatch. Single definer verified: loading both this moduledoc and app-punkscript-docs raises no duplicate-definition (undefine) notices. Consumers of the id (punkexemodel.test, formcheck.test punkexe pins) load it via app-punkscript-docs.
#0.7.0 - G-168 selection-sound launch definitions: the two multi-form definitions become parse-discriminable so form auto-selection agrees with actual dispatch/app behaviour (previously display-honest but parse-dishonest - every primary launch line double-matched). (script)::punkexe::script: the file form fences scriptfile with -regexprefail {^-e$} (+ -regexprefailmsg naming ./-e reachability - word-1 '-e' is reserved by app-punkscript behaviour, any OTHER dash word is a file); the oneliner form declares the '-e' word as a real element: a required -type none flag plus the existing required script value (the auto_exec::hash shape) so a bare {-e} reports noformmatch with the oneliner form verdicting viable-incomplete via the G-164 re-probe (the app's '-e requires a script argument' reality). (script)::punkexe::tclsh: scriptfile gains -regexprefail {^-} (stock tclMain.c: a dash-led word in script-file position means NO script) and the stdin form's first word is declared as a fenced 'firstarg' (-regexprefail {^[^-]} - a non-dash first word IS a script file) with the remaining args unchanged; selection now matches stock classification for every line except the complete '-encoding <name> <scriptfile>' shape (word-shape-matches both forms; stock's arity-priority is not expressible per-word) - that pair is declared @form -overlapallowed {scriptfile} on the stdin form (formcheck sanction; parse still raises multipleformmatches naming both forms per the G-041 no-silent-preference contract - the recorded G-168 LIMIT, surfacing only in declaration-driven consumers since dispatch never parses these ids). @form -synopsis overrides kept (G-144 follow-on decision): the <punkexe> placeholder matches the file's prose convention and neither the raw id nor @cmd -name ('punkexe tclsh') is an invocable name; the stdin form's override also keeps the user-facing '?arg ...?' line over the model's firstarg/arg split. Dispatch parse-gating deliberately NOT adopted (decision recorded in goals/archive/G-168-*): script/tclsh stay declared passthrough surfaces. Verified by a real-vs-model probe matrix (G-055 method) against bin/punk91 dispatch + app-punkscript (recorded in the goal file); pinned in args testsuites (formcheck.test GAP flips + new punkexemodel.test).
#0.6.1 - G-149-adjacent interim (user direction 2026-08-05): the two multi-form definitions ((script)::punkexe::script, ::tclsh) declare @cmd -formhint "" - the usage table's per-form hint labels are suppressed (renders under punk::args 0.17.0+; older snapshots ignore the key and show the ' i -form N ...' default). Rationale: the repl-oriented default is not typeable at the CLI where these tables render (and the (script) ids live in the boot interp, invisible to the repl's 'i'), while a make.tcl-style actionable replacement does not exist - launcher help is deliberately subject-only (trailing words ignored, the prepend-help idiom; G-032 single-mechanism revision), so there is no per-form destination a hint could name, and a ' <punkexe> help <sub>' pointer would be circular on the help path and redundant on the dispatch-error path (that table already shows all forms). Interim: if launcher help gains form-name narrowing (candidate direction discussed 2026-08-05 - 'help script oneliner' via usage -form, the G-150 cmdhelp precedent), replace the suppression with a real ' <punkexe> help <sub> %formname%' template.
#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

31
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/vfs/_config/punkboot_core.tcl vendored

@ -55,7 +55,7 @@
namespace eval ::punkboot {
#boot-core identity (G-031; stamped into kits + reported by the G-025
#buildinfo surfaces)
variable core_version 0.5.0
variable core_version 0.6.0
}
if {![info exists ::punkboot::launch_args]} {
namespace eval ::punkboot [list variable launch_args $::argv]
@ -1529,7 +1529,11 @@ if {![info exists ::punkboot::launch_defaults]} {
#Launcher help + declared-parse machinery (G-032; -help flag forms
#withdrawn 2026-08-05 by user direction - single mechanism)
#The launch surface documents itself through punk::args (definition ids
#from the punk::args::moduledoc::punkexe family) via ONE mechanism, the
#from the punk::args::moduledoc::punkexe family for core-owned surfaces,
#plus app-owned docs packages for app-handled built-ins - G-169: 'script'
#lives in app-punkscript, provided docs-only as app-punkscript-docs and
#lazily required on its help path via launcher_builtin_docpackages) via
#ONE mechanism, the
#leading 'help' word (the punkshell 'i <cmd> ...' idiom):
# <punkexe> help tabled top-level usage; the table
# lists built-ins AND project-registered
@ -1571,6 +1575,13 @@ if {![info exists ::punkboot::launch_defaults]} {
buildinfo "Print the kit's build/identity stamp report and exit (G-025)." \
help "Show usage for the launch surface or one of its subcommands (G-032)." \
]
#G-169: app-handled built-ins whose punk::args definitions live in
#their app (docs-only package - an execution-gated source of the
#app's own file, so the require never runs the app). Guarded lazy
#require on the help path only - never on boot/dispatch.
variable launcher_builtin_docpackages [dict create \
script app-punkscript-docs \
]
proc launcher_punkargs_ready {} {
#lazy guarded capability probe, one verdict per process. ok requires
#punk::args AND the moduledoc launch definitions to load and resolve
@ -1774,6 +1785,13 @@ if {![info exists ::punkboot::launch_defaults]} {
} elseif {$subject eq "help"} {
set id [launcher_helpdef_id]
} elseif {$subject in $launch_builtin_subcommands} {
#G-169: app-owned built-in docs load lazily here (guarded);
#core-owned ids resolve from the punkexe moduledoc already
#probed by launcher_punkargs_ready
variable launcher_builtin_docpackages
if {[dict exists $launcher_builtin_docpackages $subject]} {
catch {package require [dict get $launcher_builtin_docpackages $subject]}
}
set id (script)::punkexe::$subject
} else {
#project-registered: guarded require of the declared
@ -1838,7 +1856,14 @@ if {![info exists ::punkboot::launch_defaults]} {
#arguments). Built-in names cannot be shadowed - a colliding declaration
#is reported and ignored. ::punkboot::project_subcommand_info optionally
#carries per-name help/parse metadata (summary/argsid/package/parse - see
#the launcher help section above, G-032).
#the launcher help section above, G-032). The full key contract is the
#registration definition id (script)::punkboot::registration (the
#punk::args::moduledoc::punkboot durable doc, G-169-recorded location),
#with worked examples in the project_main.tcl template. The 'package' key
#may name an app-owned docs package (an execution-gated source of the
#app's own file - the app-punkscript-docs precedent for the built-in
#'script' subcommand) so a subcommand's documentation lives beside its
#parsing code without the help path ever running the app.
#::punkboot::launch_defaults keys (both optional):
# noargs subcommand assumed for a bare launch (default: shell)
# unknownfirst 'script' treats a non-subcommand first argument as a

31
src/project_layouts/vendor/punk/project-0.1/src/vfs/_config/punkboot_core.tcl vendored

@ -55,7 +55,7 @@
namespace eval ::punkboot {
#boot-core identity (G-031; stamped into kits + reported by the G-025
#buildinfo surfaces)
variable core_version 0.5.0
variable core_version 0.6.0
}
if {![info exists ::punkboot::launch_args]} {
namespace eval ::punkboot [list variable launch_args $::argv]
@ -1529,7 +1529,11 @@ if {![info exists ::punkboot::launch_defaults]} {
#Launcher help + declared-parse machinery (G-032; -help flag forms
#withdrawn 2026-08-05 by user direction - single mechanism)
#The launch surface documents itself through punk::args (definition ids
#from the punk::args::moduledoc::punkexe family) via ONE mechanism, the
#from the punk::args::moduledoc::punkexe family for core-owned surfaces,
#plus app-owned docs packages for app-handled built-ins - G-169: 'script'
#lives in app-punkscript, provided docs-only as app-punkscript-docs and
#lazily required on its help path via launcher_builtin_docpackages) via
#ONE mechanism, the
#leading 'help' word (the punkshell 'i <cmd> ...' idiom):
# <punkexe> help tabled top-level usage; the table
# lists built-ins AND project-registered
@ -1571,6 +1575,13 @@ if {![info exists ::punkboot::launch_defaults]} {
buildinfo "Print the kit's build/identity stamp report and exit (G-025)." \
help "Show usage for the launch surface or one of its subcommands (G-032)." \
]
#G-169: app-handled built-ins whose punk::args definitions live in
#their app (docs-only package - an execution-gated source of the
#app's own file, so the require never runs the app). Guarded lazy
#require on the help path only - never on boot/dispatch.
variable launcher_builtin_docpackages [dict create \
script app-punkscript-docs \
]
proc launcher_punkargs_ready {} {
#lazy guarded capability probe, one verdict per process. ok requires
#punk::args AND the moduledoc launch definitions to load and resolve
@ -1774,6 +1785,13 @@ if {![info exists ::punkboot::launch_defaults]} {
} elseif {$subject eq "help"} {
set id [launcher_helpdef_id]
} elseif {$subject in $launch_builtin_subcommands} {
#G-169: app-owned built-in docs load lazily here (guarded);
#core-owned ids resolve from the punkexe moduledoc already
#probed by launcher_punkargs_ready
variable launcher_builtin_docpackages
if {[dict exists $launcher_builtin_docpackages $subject]} {
catch {package require [dict get $launcher_builtin_docpackages $subject]}
}
set id (script)::punkexe::$subject
} else {
#project-registered: guarded require of the declared
@ -1838,7 +1856,14 @@ if {![info exists ::punkboot::launch_defaults]} {
#arguments). Built-in names cannot be shadowed - a colliding declaration
#is reported and ignored. ::punkboot::project_subcommand_info optionally
#carries per-name help/parse metadata (summary/argsid/package/parse - see
#the launcher help section above, G-032).
#the launcher help section above, G-032). The full key contract is the
#registration definition id (script)::punkboot::registration (the
#punk::args::moduledoc::punkboot durable doc, G-169-recorded location),
#with worked examples in the project_main.tcl template. The 'package' key
#may name an app-owned docs package (an execution-gated source of the
#app's own file - the app-punkscript-docs precedent for the built-in
#'script' subcommand) so a subcommand's documentation lives beside its
#parsing code without the help path ever running the app.
#::punkboot::launch_defaults keys (both optional):
# noargs subcommand assumed for a bare launch (default: shell)
# unknownfirst 'script' treats a non-subcommand first argument as a

2
src/tests/modules/AGENTS.md

@ -43,7 +43,7 @@ Unit tests for editable source modules under `src/modules/`, `src/modules_tcl8/`
- `commandstack/` — commandstack (cooperative command renaming) tests (`testsuites/commandstack/commandstack.test`, 2026-08-03 - characterisation suite + the G-160 hygiene-pass contract at commandstack 0.6.0 + the 0.7.0 convenience removal forms + the 0.7.1 reload-contract state guards (G-160 follow-ons, 2026-08-04); usage-driven from punk::packagepreference/packagetrace/packagesuppress/punk-auto_execok/punk::nav::fs-cd): record shape as a contract (token first/renamer second dict key order for the lsearch -index 1/-index 3 convention, trailing `did_rename` 0|1, `{implementation {} did_rename 0}` no-rename signal), COMMANDSTACKNEXT/COMMANDSTACKNEXT_ORIGINAL delegation + the `commandstack::next` helper (caller-context parity with the manual uplevel convention pinned), unique+monotonic per-(renamer,command) tokenids (same-renamer re-renames chain and are removable by exact token, third rename succeeds), multi-renamer stacking with removal in any order (bottom-removal re-linking), builtin renames (next_implementor `original`), remove_rename's three argument forms + unknown-renamer errors, the 0.7.0 convenience removal forms (pop_rename - command form pops topmost-for-renamer and returns the removed record, bare form searches live stacks with a multi-command ambiguity error; remove_renamer - all of a renamer's entries across live stacks with correct re-linking, Rename_stack-parked stacks skipped, removed records returned keyed by command; restore_original - whole-stack unwind to the original returning the record count, deliberately registering stack-evidenced renamers so it survives a known_renamers reset while the renamer-explicit forms keep the gate; all silent with debug off), the token->implementation map get_next_command resolves through (map/stack consistency pinned across rename/remove; parked stacks keep dispatching), channel discipline (silent full cycle with debug off; warnings only under debug), debug argument validation, -renamer misplacement errors, get_stack raw-key-first retrieval of Rename_stack-parked records + Rename_stack 1/0 returns, Delete_stack live-record guard (errors; empty/missing return 1), get_IMPLEMENTOR classification incl builtin-where-cmdtype-exists (dynamic expectation - undetermined on 8.6), lib::split_body round-trip, lib::splitx, show_stack fallback render, the reload contract (a module re-source preserves ALL state - stacks/token counters/token map/known_renamers/debug each info-exists guarded; delegation and exact-token removal keep working after reload), the help overview, and lazy punk::args registration of the PUNKARGS docs. Behavioural tests run in fresh child interps per test (module sourced by path relative to the test file; a ::puts shim captures module output for silence/warning assertions and keeps runner output clean); descriptions are single-line per the tcltestrun banner-parsing style guidance in src/tests/AGENTS.md (a hard contract until G-161 made the parser multi-line tolerant). Green on tclsh90 (9.0.3) and punk86 (8.6)
- `punkcheck/` — punkcheck module tests (install, summarize_install_resultdict, installtrack)
- `punk/ansi/` — punk::ansi tests (`testsuites/ansi/`): ansistrip/ansimerge, plus characterization of the ANSI-at-position mechanisms (`ansistring.test`: INDEX/INDEXCODE/INDEXCHAR/RANGE/INSERT grapheme indexing with SGR-prefix merging, INDEXCOLUMNS/COLUMNINDEX double-wide column mapping, trim/VIEW; extended 2026-08-05 by the G-151 pre-modification coverage survey - VIEW -lf 0/1/2 single-line-ization modes + always-on NUL + silent unknown-option tolerance (load-bearing for overtype's '-nul 1' sites) + C1/zero-width visuals, RANGE end-relative/clamping plus the merged-code-stack re-emission and trailing-code-drop semantics a truncate primitive must respect, COUNT-vs-length combining-accent divergence, NEW object basics, and the KNOWN-DEFICIENCY pin that VIEWCODES/VIEWSTYLE raise invalid-command in a plain punk::ansi interp (bare 'a+'/'a' interactive-alias dependency; pinned in a fresh child interp); plus the TRUNCATE primitive pins landed with G-151 (punk::ansi 0.2.0) - fits-unchanged byte identity incl styled fixtures, grapheme-exact capping with marker budget participation and marker-alone truncation, SGR-reset-before-marker on styled prefixes, CJK/combining-cluster grapheme safety, and strict option errors (deliberately unlike VIEW's pinned silent tolerance)), code splitting invariants (`ta.test`: detect/detectcode distinction, split_codes/split_codes_single/split_at_codes shapes and round-trip) and single-code/effective-state semantics (`codetype.test`: is_sgr_reset/has_sgr_leadingreset, has_any/all_effective, sgr_merge, sequence_type classify), grepstr characterization (`grepstr.test`: return modes incl summarydict (linemap pinned as always-present - the -help says -n-only, reconciliation deferred to the planned hygiene pass), exact highlight SGR wrapping, -n line numbering, invert + empty-highlight strip, -C context/breaks, capture groups, and the tab deficiency: warns once per call on stderr, single-pass tab line survives - the multi-pass mangling is pinned at consumer level in punk/ns corp.test), and untabify characterization (`untabify.test`: -stops int/list/terminal, -with spaces/unicode/custom-pair, multiline, errors, plus the EXPERIMENTAL -plastic elastic-tabstop mode deliberately pinned-as-interim and retained for possible repl editbuf use). Console queries (get_tabstops/get_size + punk::console::tabwidth) are mocked per the overtype renderline.test pattern - they emit live terminal queries that block/error headless. ANSI codes in these tests are literal escape strings so results are colour-state independent
- `punk/args/` — punk::args tests (`testsuites/args/`): parsing, choices/choicegroups, forms (incl the 2026-08-05 candidacy fencing/fall-through primitive pins ahead of G-168: -regexprefail on optional value slots is form-fatal with no re-landing, unknown-option/dangling-option/option-value-fence failures fall through to sibling forms as successful parses, and the documented per-word limit - a complete option+value+file line double-matches a fenced scriptfile form and a dash-tolerant stdin form), registered-namespace lazy definition loading (`docpackages.test`, G-169 pre-work: inert registration/scan-vs-load split, id_exists never triggers loading, real_id/usage lazy resolution incl tag-prefixed script-level id families, duplicate-definer last-loaded-wins characterization), the G-168 launch-definition model parity (`punkexemodel.test`, punkexe moduledoc 0.7.0: script/tclsh selection matrices against the real (script)::punkexe ids with the real-side dispatch/app oracle recorded in goals/archive/G-168, the bare-'-e' viable-incomplete verdict, the ./-e fence message, -encoding fall-through statuses, and the sanctioned complete-'-encoding' multipleformmatches LIMIT pins; the formcheck.test punkexe GAPs flipped to discriminated/sanctioned/selection-sound pins in the same arc), the G-151 landing report (`parsereport.test`, punk::args 0.18.0 parse_report: the canonical flag-like-word-consumed-as-VALUE attribution row, parsed-result vs words+withid entry parity, machine dict shape with declaration-section row order and absent-optional row omission, received xN multiplicity for solos/-multiple opts, type-aware always-marked elision at the default width plus the caller > @cmd -reportvaluewidth > built-in width cascade and never-elided dict returns, VIEW-style single-line-ization of control-bearing values, aliased-optionset storage-key row bridging, bordered-table/tableobject renders, words-form failure errorcode parity with parse, and the registered two-form definition's own render), rendering/indentation characterization, synopsis display characterization (`synopsis.test`: basic italic argname/`<type>` styling, longopt `--x=` alias forms, literal/literalprefix/stringstartswith/stringendswith type-alternates rendering unitalicised, option alternate parenthesization, multi-element clause display incl `?type?` members and argname tail-word hints, `-typesynopsis` value-element lists and option passthrough incl documenter ANSI, and the small-restricted-choice-set literal rule: 1-3 restricted choices render as unitalicised `|`-joined literals in leader/option/value positions with choicegroups counted, >3 or `-choicerestricted 0` falling back to italics, `-typesynopsis` taking precedence), usage-marking characterization (`usagemarking.test`: -parsedargs/-badarg/-parsestatus/-scheme marking primitives plus goodchoice highlighting of selected/default-in-effect choice words, asserted by SGR-parameter subset against the live colour arrays; the G-049 nocolour/colour-leak GAP pins flipped 2026-07-10 to scheme-statelessness assertions), the G-049 parse-status structure (`parsestatus.test`: punk::args::parse_status overall/per-argument statuses, badarg for type/allocation failures, -caller attribution, errorcode -argspecs stripping), -parsekey characterization (`parsekey.test`: result/received/solos/multis keying, shared-key required satisfaction and defaults, mash-path and prefix-abbreviation keying, plus GAP pins for last-defined-member default precedence, cross-member -multiple value loss, parsekey/optname collision conflation, and values/leaders parsekey breakage - desired-behaviour pins disabled behind punkargsKnownBug in `testsuites/dev/parsekey-knownbugs.test`), and tclcore doc/interpreter behavioural parity (`tclcoreparity.test`, G-054, gated on have_tclcoredocs: 'string is' class choices equal the live-harvested set, per-class docids exist, error-vs-ok agreement across the probe matrix, version-note labels conditional on class presence - expectations derived from the running interpreter, green on 8.6/8.7/9.0; under 8.6 run the file directly via a plain tclkit + tcltest driver since runtests' harness needs newer infrastructure)
- `punk/args/` — punk::args tests (`testsuites/args/`): parsing, choices/choicegroups, forms (incl the 2026-08-05 candidacy fencing/fall-through primitive pins ahead of G-168: -regexprefail on optional value slots is form-fatal with no re-landing, unknown-option/dangling-option/option-value-fence failures fall through to sibling forms as successful parses, and the documented per-word limit - a complete option+value+file line double-matches a fenced scriptfile form and a dash-tolerant stdin form), registered-namespace lazy definition loading (`docpackages.test`, G-169 pre-work: inert registration/scan-vs-load split, id_exists never triggers loading, real_id/usage lazy resolution incl tag-prefixed script-level id families, duplicate-definer last-loaded-wins characterization), the G-168 launch-definition model parity (`punkexemodel.test`, punkexe moduledoc 0.7.0, id loading updated for the G-169 handover - the script id resolves via app-punkscript-docs from src/lib (auto_path derived from the test file location) while the moduledoc carries the core-owned ids: script/tclsh selection matrices against the real (script)::punkexe ids with the real-side dispatch/app oracle recorded in goals/archive/G-168, the bare-'-e' viable-incomplete verdict, the ./-e fence message, -encoding fall-through statuses, and the sanctioned complete-'-encoding' multipleformmatches LIMIT pins; the formcheck.test punkexe GAPs flipped to discriminated/sanctioned/selection-sound pins in the same arc), the G-151 landing report (`parsereport.test`, punk::args 0.18.0 parse_report: the canonical flag-like-word-consumed-as-VALUE attribution row, parsed-result vs words+withid entry parity, machine dict shape with declaration-section row order and absent-optional row omission, received xN multiplicity for solos/-multiple opts, type-aware always-marked elision at the default width plus the caller > @cmd -reportvaluewidth > built-in width cascade and never-elided dict returns, VIEW-style single-line-ization of control-bearing values, aliased-optionset storage-key row bridging, bordered-table/tableobject renders, words-form failure errorcode parity with parse, and the registered two-form definition's own render), rendering/indentation characterization, synopsis display characterization (`synopsis.test`: basic italic argname/`<type>` styling, longopt `--x=` alias forms, literal/literalprefix/stringstartswith/stringendswith type-alternates rendering unitalicised, option alternate parenthesization, multi-element clause display incl `?type?` members and argname tail-word hints, `-typesynopsis` value-element lists and option passthrough incl documenter ANSI, and the small-restricted-choice-set literal rule: 1-3 restricted choices render as unitalicised `|`-joined literals in leader/option/value positions with choicegroups counted, >3 or `-choicerestricted 0` falling back to italics, `-typesynopsis` taking precedence), usage-marking characterization (`usagemarking.test`: -parsedargs/-badarg/-parsestatus/-scheme marking primitives plus goodchoice highlighting of selected/default-in-effect choice words, asserted by SGR-parameter subset against the live colour arrays; the G-049 nocolour/colour-leak GAP pins flipped 2026-07-10 to scheme-statelessness assertions), the G-049 parse-status structure (`parsestatus.test`: punk::args::parse_status overall/per-argument statuses, badarg for type/allocation failures, -caller attribution, errorcode -argspecs stripping), -parsekey characterization (`parsekey.test`: result/received/solos/multis keying, shared-key required satisfaction and defaults, mash-path and prefix-abbreviation keying, plus GAP pins for last-defined-member default precedence, cross-member -multiple value loss, parsekey/optname collision conflation, and values/leaders parsekey breakage - desired-behaviour pins disabled behind punkargsKnownBug in `testsuites/dev/parsekey-knownbugs.test`), and tclcore doc/interpreter behavioural parity (`tclcoreparity.test`, G-054, gated on have_tclcoredocs: 'string is' class choices equal the live-harvested set, per-class docids exist, error-vs-ok agreement across the probe matrix, version-note labels conditional on class presence - expectations derived from the running interpreter, green on 8.6/8.7/9.0; under 8.6 run the file directly via a plain tclkit + tcltest driver since runtests' harness needs newer infrastructure)
- `punk/nav/ns/` — punk::nav::ns tests (`testsuites/nav/navns.test`): the n/ n// n/// navigation state machine (ns/ transitions absolute/relative/glob-no-nav, failed-nav state preservation, quad-colon normalization, v-form content selection, ensemble annotation) and the ::punk::nav::ns::ns_current variable contract the repl/codethread/subshell seeding all consume; display content is covered in punk/ns nslist.test
- `punk/repl/` — punk::repl tests (`testsuites/repl/`): opunk console backend integration (`consolebackends.test`) and repl current-namespace retention (`nscurrent.test`: real codethread via repl::init driven by synchronous runscript sends - inscope evaluation of ns_current, retention across submissions, n/-navigation retained, auto-create-with-notice for missing namespaces, the 2026-07-14 stray-namespace seeding fix pinned behaviourally plus a source-text guard on repl::start's inline template; the end-to-end piped subshell session is covered at shell level by shell/testsuites/punkexe/shellnavns.test - which found the first-subshell shared-code-interp asymmetry and the piped-inscope gap recorded there)
- `punk/ns/` — punk::ns tests (`testsuites/ns/`): cmdwhich/cmdinfo/cmd_traverse doc-lookup flow (`cmdflow.test`, G-040 parity), n/ display machinery characterization (`nslist.test`: tier A get_ns_dicts classification buckets as the machine contract for display reworks - incl package tail/prefix derivation, alias edge cases, usageinfo scan-dependence; tier B per-element layout-agnostic marking - underline/underdouble/underdotted namespace package styles, command type tag colours, exported/imported markers, the punkargs doc icon; tier C REWORK-flagged pins of the current hardcoded 2-col/4-col layout and nspath subtables, to flip deliberately with the planned punk-tables/width-responsive rework; plus the flipped nslist_types_default pin - bare nslist without -types displays all member types since punk::ns 0.7.1 fixed the braced-literal -types default), corp proc-retrieval and syntax/untabify interplay (`corp.test`: name edge cases, -ranges/-n line handling, basic-highlight ansistrip equivalence, -untabify spaces/unicode tab-free output, the KNOWN-DEFICIENCY pin for default -untabify none on tabbed bodies - grepstr warns per pass and brace overlays mangle tabbed lines, deterministic under mocked console tabstops - and a ::tcl::CopyDirectory -untabify spaces smoke test; precursor coverage for the planned punk::ns hygiene pass), cmdtrace characterization (`cmdtrace.test`: -pause 0 non-interactive runs, linedict line-mark keys for flat and 2-word-form nested switches as correct-mark guards, and GAP pins for the upstream nested-switch mismark - core.tcl-lang.org tktview 5d5b1052280c976ea3d4, arm bodies whose split-list index lands on a literal switch-command word report container-relative lines; mark tests gated on have_tclcoredocs because cmdtrace's arm-offset correction parses against the ::switch argdoc; plus the fixed-canary asserting punk::lib::check::has_tclbug_nestedswitch_tracelines still reports the bug - a live behavioural probe, so a fixed Tcl release fails the canary first and triggers the documented flip workflow), cmdhelp usage-rendering integration (`cmdhelp.test`: scheme selection, goodarg/badarg marking incl type/allocation failures, goodchoice highlighting of supplied/default choice words, alias path, cmdinfo result shape, queried-command failure attribution, and `-return dict` parse-status returns (G-049 - its GAP pins flipped 2026-07-10); remaining GAP pins for pseudo-command cmdtype + space-form docid prefixes (G-051, real `string is` pins behind the have_tclcoredocs constraint), TclOO undocumented-method fallback (G-052), and synopsis marking absence (G-050); plus the G-150 flag-led form-narrowing + selection-soundness pins against punk::auto_exec::hash - bare-parse selection contract (incl the dash-led-name noformmatch cost), no-word/unknown-flag/ambiguity whole-render fallbacks, and hash runtime-behaviour-unchanged), and name/path primitive characterization (`nsprimitives.test`: string pins for nsparts/nsprefix/nstail/nsjoin/nsjoinall incl weird colon-run (`:::`) splitting, the trailing-colon parse ambiguity (`::x:` + `y` joins to the same string as `::x` + `:y` and reparses leading-colon-greedy), and prefix/tail/join round-trip and its absolutizing exceptions (the original twin-divergence pins for nsparts1/nsprefix1/nsprefix_orig/nstail1/nstail_orig served as safe-deletion evidence and were removed with the twins in the punk::ns 0.7.0 hygiene pass - divergence record in this file's git history, commit 0c7168a1); plus nseval fq-requirement/create-on-eval/evaluator-proc caching, the native-vs-punk `p:::x` resolution divergence (native namespace eval reaches child `x`, nseval creates/reaches literal `:x`), nseval_ifexists no-create + error propagation on plain and genuinely weird namespaces, nsexists/nschildren/nstree_raw weird-ns and relative-resolution pins, globmatchns `*`/`**`/`?` semantics (incl `*` matching a single inner colon - a formerly stale 'should be fixed' comment above nsglob_as_re was corrected in the 0.7.0 hygiene pass), and nspath_to_absolute/nspath_here_absolute caller-resolution pins; the nsjoinall error-message wart pin ('nsjoin:' prefix) flipped when punk::ns 0.7.1 fixed it)

14
src/tests/modules/punk/args/testsuites/args/formcheck.test

@ -19,7 +19,19 @@ namespace eval ::testspace {
}
testConstraint have_tclcoredocs [expr {![catch {package require punk::args::moduledoc::tclcore}]}]
testConstraint have_punkexedocs [expr {![catch {package require punk::args::moduledoc::punkexe}]}]
#G-169: the (script)::punkexe::script definition is app-owned - provided by
#app-punkscript-docs (an execution-gated source of app-punkscript's own
#file; requiring it never runs the app) while the moduledoc carries the
#core-owned ids (tclsh etc). The punkexe pins need both; src/lib is not on
#the harness auto_path so derive it from this file's location.
variable srclibdir [file normalize [file join [file dirname [info script]] .. .. .. .. .. .. lib]]
if {[file isdirectory $srclibdir] && $srclibdir ni $::auto_path} {
lappend ::auto_path $srclibdir
}
testConstraint have_punkexedocs [expr {
![catch {package require punk::args::moduledoc::punkexe}]
&& ![catch {package require app-punkscript-docs}]
}]
#discriminated multiform fixtures - literal/choice/int leading slots (no permissive
#alignment) and arity-window separation. formcheck must report NOTHING for these.

14
src/tests/modules/punk/args/testsuites/args/punkexemodel.test

@ -32,7 +32,19 @@ namespace eval ::testspace {
set result ""
}
testConstraint have_punkexedocs [expr {![catch {package require punk::args::moduledoc::punkexe}]}]
#G-169: the (script)::punkexe::script definition is app-owned - provided by
#app-punkscript-docs (an execution-gated source of app-punkscript's own
#file; requiring it never runs the app) while the moduledoc carries the
#core-owned ids (tclsh etc). Load both; src/lib is not on the harness
#auto_path so derive it from this file's location.
variable srclibdir [file normalize [file join [file dirname [info script]] .. .. .. .. .. .. lib]]
if {[file isdirectory $srclibdir] && $srclibdir ni $::auto_path} {
lappend ::auto_path $srclibdir
}
testConstraint have_punkexedocs [expr {
![catch {package require punk::args::moduledoc::punkexe}]
&& ![catch {package require app-punkscript-docs}]
}]
if {[testConstraint have_punkexedocs]} {
#trigger the docpackages load of the (script)::punkexe id family
catch {punk::args::real_id (script)::punkexe::script}

27
src/tests/shell/testsuites/punkexe/launcherhelp.test

@ -438,6 +438,33 @@ apply { args {
}\
-result {0 1}
# -- G-169 app-owned built-in docs ------------------------------------------------------------
# script's punk::args definition lives in src/lib/app-punkscript/punkscript.tcl (docs-only
# package app-punkscript-docs - an execution-gated source of the app's own file); the punkexe
# moduledoc no longer defines the id (single-definer handover). Proven pre-bake by running the
# fake-main harness in SRC package mode: the source tree serves the nested boot with version
# precedence, its moduledoc carries no (script)::punkexe::script, so a successful tabular
# render bearing the G-168-era scriptfile help text can only come from the app-owned
# definition, lazily required on the help path (punkboot_core launcher_builtin_docpackages).
# Post-bake the kit-internal stack carries the same set and the render is identical.
#added 2026-08-05 (agent, G-169) - markers are line-break-safe substrings (the authored help
#wraps mid-phrase; the oneliner form's rows are not in the default form-0 table)
test launcherhelp_appowned_script_docs {'src help script': the app-owned (script)::punkexe::script definition resolves lazily via app-punkscript-docs and renders; no duplicate-definition notices}\
-constraints {punkexeavailable kit_g032} -body {
variable punkexe
set fm [fake_main_setup]
set r [exe_run $punkexe tclsh $fm src help script]
set result [list]
lappend result [dict get $r exitcode]
lappend result [string match "*punkexe script*" [dict get $r stdout]]
lappend result [string match "*names a file*" [dict get $r stdout]]
lappend result [string match "*selection-soundness*" [dict get $r stdout]]
lappend result [expr {![string match "*clearing existing data*" [dict get $r stderr]]}]
set result
}\
-result {0 1 1 1 1}
# -- metakit-container kit (the non-zipfs acceptance leg) --------------------------------------
test launcherhelp_mkkit_help {metakit kit: 'help' renders the top-level usage, exit 0}\

31
src/vfs/_config/punkboot_core.tcl

@ -55,7 +55,7 @@
namespace eval ::punkboot {
#boot-core identity (G-031; stamped into kits + reported by the G-025
#buildinfo surfaces)
variable core_version 0.5.0
variable core_version 0.6.0
}
if {![info exists ::punkboot::launch_args]} {
namespace eval ::punkboot [list variable launch_args $::argv]
@ -1529,7 +1529,11 @@ if {![info exists ::punkboot::launch_defaults]} {
#Launcher help + declared-parse machinery (G-032; -help flag forms
#withdrawn 2026-08-05 by user direction - single mechanism)
#The launch surface documents itself through punk::args (definition ids
#from the punk::args::moduledoc::punkexe family) via ONE mechanism, the
#from the punk::args::moduledoc::punkexe family for core-owned surfaces,
#plus app-owned docs packages for app-handled built-ins - G-169: 'script'
#lives in app-punkscript, provided docs-only as app-punkscript-docs and
#lazily required on its help path via launcher_builtin_docpackages) via
#ONE mechanism, the
#leading 'help' word (the punkshell 'i <cmd> ...' idiom):
# <punkexe> help tabled top-level usage; the table
# lists built-ins AND project-registered
@ -1571,6 +1575,13 @@ if {![info exists ::punkboot::launch_defaults]} {
buildinfo "Print the kit's build/identity stamp report and exit (G-025)." \
help "Show usage for the launch surface or one of its subcommands (G-032)." \
]
#G-169: app-handled built-ins whose punk::args definitions live in
#their app (docs-only package - an execution-gated source of the
#app's own file, so the require never runs the app). Guarded lazy
#require on the help path only - never on boot/dispatch.
variable launcher_builtin_docpackages [dict create \
script app-punkscript-docs \
]
proc launcher_punkargs_ready {} {
#lazy guarded capability probe, one verdict per process. ok requires
#punk::args AND the moduledoc launch definitions to load and resolve
@ -1774,6 +1785,13 @@ if {![info exists ::punkboot::launch_defaults]} {
} elseif {$subject eq "help"} {
set id [launcher_helpdef_id]
} elseif {$subject in $launch_builtin_subcommands} {
#G-169: app-owned built-in docs load lazily here (guarded);
#core-owned ids resolve from the punkexe moduledoc already
#probed by launcher_punkargs_ready
variable launcher_builtin_docpackages
if {[dict exists $launcher_builtin_docpackages $subject]} {
catch {package require [dict get $launcher_builtin_docpackages $subject]}
}
set id (script)::punkexe::$subject
} else {
#project-registered: guarded require of the declared
@ -1838,7 +1856,14 @@ if {![info exists ::punkboot::launch_defaults]} {
#arguments). Built-in names cannot be shadowed - a colliding declaration
#is reported and ignored. ::punkboot::project_subcommand_info optionally
#carries per-name help/parse metadata (summary/argsid/package/parse - see
#the launcher help section above, G-032).
#the launcher help section above, G-032). The full key contract is the
#registration definition id (script)::punkboot::registration (the
#punk::args::moduledoc::punkboot durable doc, G-169-recorded location),
#with worked examples in the project_main.tcl template. The 'package' key
#may name an app-owned docs package (an execution-gated source of the
#app's own file - the app-punkscript-docs precedent for the built-in
#'script' subcommand) so a subcommand's documentation lives beside its
#parsing code without the help path ever running the app.
#::punkboot::launch_defaults keys (both optional):
# noargs subcommand assumed for a bare launch (default: shell)
# unknownfirst 'script' treats a non-subcommand first argument as a

Loading…
Cancel
Save