From 97e4aacd4ff5f9b81758c68e46e759520085c130 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Thu, 6 Aug 2026 00:02:55 +1000 Subject: [PATCH] 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 --- GOALS-archive.md | 4 + GOALS.md | 4 - goals/G-169-app-owned-launch-docs.md | 46 ------ goals/G-170-launcher-help-depth.md | 7 +- goals/archive/G-169-app-owned-launch-docs.md | 153 ++++++++++++++++++ src/lib/AGENTS.md | 2 +- src/lib/app-punkscript/pkgIndex.tcl | 10 +- src/lib/app-punkscript/punkscript.tcl | 120 +++++++++++++- .../args/moduledoc/punkboot-999999.0a1.0.tm | 9 +- .../args/moduledoc/punkboot-buildversion.txt | 3 +- .../args/moduledoc/punkexe-999999.0a1.0.tm | 103 ++++-------- .../args/moduledoc/punkexe-buildversion.txt | 3 +- .../src/vfs/_config/punkboot_core.tcl | 31 +++- .../src/vfs/_config/punkboot_core.tcl | 31 +++- src/tests/modules/AGENTS.md | 2 +- .../punk/args/testsuites/args/formcheck.test | 14 +- .../args/testsuites/args/punkexemodel.test | 14 +- .../testsuites/punkexe/launcherhelp.test | 27 ++++ src/vfs/_config/punkboot_core.tcl | 31 +++- 19 files changed, 469 insertions(+), 145 deletions(-) delete mode 100644 goals/G-169-app-owned-launch-docs.md create mode 100644 goals/archive/G-169-app-owned-launch-docs.md diff --git a/GOALS-archive.md b/GOALS-archive.md index 76b4f922..f30f2fdc 100644 --- a/GOALS-archive.md +++ b/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 ...}, select file for { ...} where 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 ' 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.) diff --git a/GOALS.md b/GOALS.md index 46306909..ced2010e 100644 --- a/GOALS.md +++ b/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 diff --git a/goals/G-169-app-owned-launch-docs.md b/goals/G-169-app-owned-launch-docs.md deleted file mode 100644 index 460da977..00000000 --- a/goals/G-169-app-owned-launch-docs.md +++ /dev/null @@ -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). diff --git a/goals/G-170-launcher-help-depth.md b/goals/G-170-launcher-help-depth.md index 43e58b08..9f1bdc0c 100644 --- a/goals/G-170-launcher-help-depth.md +++ b/goals/G-170-launcher-help-depth.md @@ -20,7 +20,12 @@ builds the destination, then points at it. recorded LIMIT (the complete '-encoding ' 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, diff --git a/goals/archive/G-169-app-owned-launch-docs.md b/goals/archive/G-169-app-owned-launch-docs.md new file mode 100644 index 00000000..9c9c91c5 --- /dev/null +++ b/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 diff --git a/src/lib/AGENTS.md b/src/lib/AGENTS.md index 48ab07a9..c4965658 100644 --- a/src/lib/AGENTS.md +++ b/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:` 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 ` -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:` 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 ` -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`) diff --git a/src/lib/app-punkscript/pkgIndex.tcl b/src/lib/app-punkscript/pkgIndex.tcl index f731b9c1..b114cc49 100644 --- a/src/lib/app-punkscript/pkgIndex.tcl +++ b/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] diff --git a/src/lib/app-punkscript/punkscript.tcl b/src/lib/app-punkscript/punkscript.tcl index ee579c25..16722b4a 100644 --- a/src/lib/app-punkscript/punkscript.tcl +++ b/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 + ' -e