You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

14 KiB

G-032 Kit launcher dogfoods punk::args: tabled help and parsed subcommands

Status: achieved 2026-08-03 Scope: src/vfs/_config/ (default dispatch); src/lib/app-punkshell and sibling app packages as touched Goal: the default launch dispatch defines its subcommands via punk::args - <punkexe> -help and argument errors render the tabled usage enumerating built-in and project-registered subcommands with summaries, and subcommand options parse through punk::args so projects can declare complex arguments - with the G-030 degradation rules (boot never fails and help degrades to plain text when punk::args or the ANSI rendering stack is unavailable). Acceptance: <punkexe> -help renders tabled usage listing all subcommands including project-registered ones, each with a summary; at least one built-in subcommand's options are declared and parsed via punk::args with tabled usage errors on invalid input (the G-015 script subcommand or the G-025 version-report subcommand are the natural candidates); a project-registered subcommand's help appears by registration alone - no edits to the shared dispatch; with punk::args or the rendering stack unloadable, boot proceeds and help degrades to a plain subcommand list; verified on both a zipfs-based and a non-zipfs kit where both remain supported.

Context

The kit launcher is, alongside make.tcl (G-030), the first surface a user or agent meets - and it should showcase the ecosystem's own argument tooling the way the tomlish cmdline app already does (tabled usage on -help/no-args). Today the dispatch in punk_main.tcl is a bare switch on the first argument with no help output at all: an unknown first argument silently becomes a script attempt, and there is no way to discover the subcommands (tclsh/shellspy/punk/shell/script) or the package-mode tokens without reading the source.

Builds on G-031's registration model (landed - G-031 achieved 2026-08-02, see goals/archive/G-031-componentized-kit-boot.md): the thin project main declares subcommands with optional punk::args definition ids; the shared dispatch renders and parses from those declarations - so a project's subcommands appear in -help by registration alone.

Approach

  • punk::args definitions for the top-level dispatch (subcommand enumeration with @cmd summaries, package-mode leader documented) and per-subcommand options. <punkexe> -help, <punkexe> help <subcommand>, and <subcommand> -help all render tabled usage.
  • The packagemode leader's help text is LIVE as of G-033's achievement (2026-07-28, goals/archive/G-033-proj-mode-cwd-project.md): the drafted text (block semantics, ordering-as-tie-break rule, the proj: scope prefix with the kit-first proj:internal-src canonical example) now sits as the packagemode leader in punk::args::moduledoc::punkexe 0.1.1's (script)::punkexe definition. This goal's remaining lane is the rendering/parse wiring that consumes it.
  • Parse errors on declared subcommands produce punk::args usage errors (tabled), not silent fallthrough. The unknown-first-arg -> script reclassification survives only where it does not swallow obvious mistakes - the exact rule is an implementation decision to record here (candidates: reclassify only when the argument names an existing file, else usage error).
  • Degradation per the G-030 rules: the kit normally bundles punk::args and the rendering stack, but src/dev modes and damaged kits must not brick the launcher - guarded lazy require; parsing degrades to the current switch semantics and help to a plain subcommand list when punk::args (or the ANSI stack, independently) is unavailable.
  • Exit-code and output-cleanliness expectations follow G-015: help to stdout exit 0; usage errors to stderr nonzero; no launcher chatter on the app's channels.

Alternatives considered

  • Help text maintained by hand in the dispatch - rejected: the same hand-maintained-help drift make.tcl has (G-030), and project-registered subcommands would need dispatch edits to become discoverable.
  • Requiring punk::args at boot - rejected: same repair-path reasoning as G-030 and the punkboot::utils guarded-require rule.

Progress

Landed 2026-08-03 (activation day; boot core 0.2.1 -> 0.3.0, moduledoc::punkexe 0.4.0, moduledoc::punkboot 0.2.0, punkshell 0.48.0):

  • Help surface in the shared boot core (G-031 registration model consumer): new built-in subcommand help (help ?subcommand?, subject prefix-resolving), -help first-argument interception, <subcommand> -help per-subcommand usage. Rendered from the punk::args::moduledoc::punkexe ids; the per-kit top-level table is (script)::punkexe.launcher - a punk::args::resolved_def -override clone of the static (script)::punkexe with the subcommand choices/choicelabels replaced by the kit's LIVE surface (built-ins + help + project registrations); (script)::punkexe.launcherhelp is the same clone of the new (script)::punkexe::help for subject validation. Help to stdout exit 0; usage errors to stderr exit 1 (G-015 doctrine).
  • Registration model extension (additive, back-compatible): ::punkboot::project_subcommand_info - per-name optional keys summary (top-level table line), argsid (definition id rendered by help <name> / <name> -help), package (guarded require before resolving argsid), parse (validate arguments through argsid before the handler; tabled usage error exit 1 on failure). project_subcommands keeps its proven name->handler shape untouched (tomlish thin main unaffected). Documented runtime-queryably in moduledoc::punkboot's registration definition.
  • Unknown-first-argument rule (the goal's recorded open decision, adopted with a scriptlib extension): when the effective unknownfirst target is script, a first argument is reclassified to script only when it names an existing file OR is a lib:* scriptlib reference; anything else is refused with the top-level usage on stderr, exit 1 (message + table + a force-script hint). Degrades to the historic always-reclassify semantics when punk::args is unavailable.
  • Parsed built-in: buildinfo enforces its @values -min 0 -max 0 contract through punk::args::parse - argument rejection prints the historic buildinfo: takes no arguments line (buildinfo.test pin) PLUS the tabled usage on stderr, exit 2 unchanged; the empty-argument fast path never loads punk::args. The help subcommand's subject is also genuinely punk::args-parsed (tabled choice errors).
  • Deliberate carve-outs (all recorded in the moduledoc definitions): tool-style kits (unknownfirst naming a project subcommand, e.g the tomlish kits) keep help FLAGS routed to their processor - the help word is their launch-surface entry (tomlish -help parity preserved); tclsh intercepts a help flag only as its SOLE argument (every other leading-dash form keeps stock all-args-to-::argv parity - tclshcmd.test pins untouched); a registration without argsid keeps <name> -help full passthrough to its handler (never shadows an app's own -help); script/shell/punk/shellspy stay declared passthrough surfaces (their arguments belong to the launched script/app).
  • Degradation (G-030 doctrine): all wiring lazy + guarded - normal boot/dispatch paths never load punk::args; one capability probe per process (punk::args + moduledoc ids resolving), PUNKBOOT_PLAIN=1 forces degraded mode (same env hook as make.tcl), rendering degrades independently of parsing (errorstyle standard/minimal on punk::ansi+textblock availability). Degraded help = plain subcommand list (names + summaries incl registration summaries) on stdout exit 0; boot never fails on any help/parse path.
  • Characterization: src/tests/shell/testsuites/punkexe/launcherhelp.test (22 tests: help forms, prefix resolution, refusal rule, tclsh carve-out, buildinfo tabled rejection, PUNKBOOT_PLAIN degradation, fake-thin-main registration harness proving help-by-registration-alone + declared parse, and a metakit-kit leg).

Verification at the achieved flip (2026-08-03):

  • Kits (deployed, internal package mode, freshly baked with core 0.3.0 + moduledoc::punkexe 0.4.0): zipfs punk902z + punk91 AND metakit punk86 all render the tabled top-level usage on help/-help (choices table incl summaries), per-subcommand usage on help <sub> / <sub> -help, refuse an unknown non-file first argument (stderr usage, exit 1, stdout empty), reject buildinfo extra with the pinned one-liner + usage table (exit 2, stdout empty), and keep normal buildinfo clean (stamp reports boot_core_version=0.3.0, project_version=0.48.0). Registration-alone help + declared parse proven twice: interactive fake-thin-main harness runs and the committed launcherhelp.test registration tests (both via <kit> tclsh <fakemain> with a copied core - the G-031 bake-free harness shape).
  • Degradation proven three ways: raw tclsh90 plain-script run (no punk::args anywhere) boots and renders the plain subcommand list exit 0; PUNKBOOT_PLAIN=1 on a healthy kit forces the plain list (pinned by test); punk::args-present-but-render-broken falls through per-path catches (errorstyle split).
  • Suites: launcherhelp.test 22/22; full punkexe subtree 140 total / 136 passed / 4 skipped (pre-existing env-gated console skips) / 0 failed - includes tclshcmd.test (leading-dash parity pins untouched), scriptexec.test, buildinfo.test (exit-2 + one-liner pins intact, test unchanged), kitmountpoint.test (plain-script main shape under raw runtimes incl the 8.6 backport family).
  • Traps found and recorded: (a) punk::args' choices-grid render path hits bare lpop on Tcl 8.6 unless punk::lib's forward-compat shims are loaded - the known punk::args/punk::lib module-web gap surfacing in the new lean launcher context; fixed with a guarded package require punk::lib after punk::args in the capability probe (memory + core comment). (b) punk::args choice tables ANSI-highlight the unambiguous PREFIX of each choice name mid-word ('b'+'uildinfo'), so literal substring assertions on choice names must ansistrip first (launcherhelp.test stripansi helper). (c) The tcl87 PATH tclsh mkimg file-relative bake trap re-confirmed (G-134 warnings on every zip kit); re-driven with native Tcl903 per the recorded recipe - final bake clean.
  • Remaining manual item (cross-repo, user-paced): the tomlish checkout's hand-seeded core copy is still 0.2.1 - a re-seed with the 0.3.0 master gives its kits the help word surface (its -help routing to the tomlish processor is preserved by design via the tool-style-kit carve-out); optionally its thin main can then declare project_subcommand_info (summary/argsid) for a richer table.

Notes

  • Activation-freshness overlap survey (goals_xref score G-032, run at activation 2026-08-03), relationships recorded per the GOALS.md activation rule:
    • Related: G-038 (piped-to-interactive session continuity) - shares the src/lib/app-punkshell surface this goal's Scope names "as touched"; G-038 already points here (back-pointer added at activation). Different lanes: G-038 owns eof-restart/session state, this goal owns launch help/parse.
    • Related: G-077 (punkexe -e one-liner) - same top-level dispatch surface (src/vfs/_config/punk_main.tcl and the boot core it sources since G-031). The unknown-first-arg reclassification rule this goal settles is the surface G-077's -e affordance will extend; G-077 should follow this goal's shipped rule rather than re-deciding it.
    • Related: G-131 (boot payload autodetection) - sibling work in the same layout-owned boot core file (punkboot_core.tcl); its container-detection arm lands beside this goal's dispatch wiring. No contract overlap.
    • Related: G-089 (scriptlib in kits + packagemode-aware lib: resolution) - sibling consumer of the boot core/thin-main surface (boot_state, punk_main.tcl); the script subcommand's lib: resolution documented in moduledoc::punkexe is its lane.
  • Related: G-031 (achieved 2026-08-02 - see goals/archive/G-031-componentized-kit-boot.md; the registration model this renders from is live: ::punkboot::project_subcommands / launch_defaults in boot core 0.2.x, documented runtime-queryably by punk::args::moduledoc::punkboot - render from those), G-030 (make.tcl counterpart and the shared degradation doctrine), G-015 (script subcommand options are a natural first parsed subcommand; exit-code semantics), G-025 (achieved 2026-08-02 - see goals/archive/G-025-exe-selfreport.md; the buildinfo subcommand landed with a moduledoc::punkexe 0.3.0 definition - a natural first parsed subcommand), G-013 (the debugrepl activation review mentions proper-command conversions that would slot into the same pattern).
  • G-118 item 10 relationship (recorded 2026-07-24 after overlap review; G-118 achieved 2026-07-25 - see goals/archive/G-118-tclsh-subcommand-review.md): G-118 DELIVERED the launch-subcommand-family documentation as G-032-COMPATIBLE punk::args definitions - this goal's source material now exists in punk::args::moduledoc::punkexe (src/modules/punk/args/moduledoc/punkexe-999999.0a1.0.tm): (script)::punkexe top-level (subcommand enumeration with choicelabels, package-mode leader documented in help text, default reclassification described) + per-subcommand (script)::punkexe::tclsh (full contract, multi-form) and summary-level script/shell/punk/shellspy. The -help rendering, subcommand option parsing, registration integration and degradation wiring stay here - this goal wires the dispatch to render/parse from those ids (punk::args 0.12.7+ resolves the (script)::* id family through update_definitions/usage). Also: G-118 item 4 shipped a file-exists pre-check in the tclsh subcommand (clean not-found + exit 1)
    • precedent for this goal's open unknown-first-arg reclassification rule (reclassify-only-when-file-exists candidate).
  • Archived-goal references in this file: G-015 achieved 2026-07-07 (goals/archive/G-015-script-subcommand-piped-stdin.md); G-030 achieved 2026-07-14 (goals/archive/G-030-maketcl-punkargs.md) - the make.tcl counterpart shipped: follow its shipped decisions (-confirm 0|1 prompt policy, explicit @form -synopsis, track-parsing-degrade-rendering split, required-choice leaders for pointed choiceviolation errors).