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