diff --git a/CHANGELOG.md b/CHANGELOG.md index ad27d6b8..d32f2c47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.to Entries are newest-first; one bullet per notable change. See the root `AGENTS.md` "Project Versioning" section for the bump policy. +## [0.66.2] - 2026-08-09 + +- make.tcl dry-run help (G-151 follow-through): an accepted 'make.tcl help + ' line now stacks punk::args::parse_report's + landing table under the usage render - Argument|Source|Value rows showing + where each received word landed plus defaults-in-effect - retiring the + interim one-line received-args clue (kept only as the degrade fallback for + a stale pre-0.18.0 bootsupport punk::args or broken render stack). The + 'dry-run: line accepted (form N)' confirmation line is unchanged. + ## [0.66.1] - 2026-08-09 - punk::args validation errors now display-bound received values (punk::args diff --git a/goals/archive/G-151-punkargs-annotated-success-render.md b/goals/archive/G-151-punkargs-annotated-success-render.md index 4ed89261..dce216b5 100644 --- a/goals/archive/G-151-punkargs-annotated-success-render.md +++ b/goals/archive/G-151-punkargs-annotated-success-render.md @@ -168,7 +168,7 @@ Verification evidence: ## Follow-ons -Follow-on: make.tcl retires its interim one-line 'dry-run: line accepted ...' report in favour of parse_report (stacked under the usage render per lean 1) after a bootsupport promotion carrying punk::args 0.18.0- and punk::ansi 0.2.0- => open +Follow-on: make.tcl retires its interim one-line 'dry-run: line accepted ...' report in favour of parse_report (stacked under the usage render per lean 1) after a bootsupport promotion carrying punk::args 0.18.0- and punk::ansi 0.2.0- => landed 2026-08-09 (project 0.66.2 - the gating bootsupport promotion carried args 0.25.2/ansi 0.2.1; make.tcl stacks the parse_report landing table under the dry-run usage render, keeping the one-liner solely as the stale-snapshot/broken-render degrade fallback; layout + modpod copies via the thin-layout sync; maketclhelp.test pins updated) Follow-on: serve arg_error's failure-side value-truncation todo ('truncate/summarize values in error messages', args-999999.0a1.0.tm arg_error region) with the same report_display_value/TRUNCATE helpers (lean 5 - explicitly excluded from this goal's acceptance) => landed 2026-08-09 (punk::args 0.26.0 private::error_display_value - all received-word message sites incl unexpected-option words; '@cmd -reportvaluewidth' cascade; raw -badarg/-badval retained; validation.test pins) Follow-on: engine-level rationalization of parse-result storage keys vs definition argument names (aliased optionset last-|-member fold, -parsekey renames) so value-in-effect lookups need no display-level bridging => goal G-084 Follow-on: column-aware (display-width) TRUNCATE variant for CJK-heavy values (the grapheme-count cap under-counts double-wide content; noted in the TRUNCATE argdoc) => open diff --git a/punkproject.toml b/punkproject.toml index cdc8d381..51661ba2 100644 --- a/punkproject.toml +++ b/punkproject.toml @@ -1,6 +1,6 @@ [project] name = "punkshell" -version = "0.66.1" +version = "0.66.2" license = "BSD-2-Clause" url = "https://www.gitea1.intx.com.au/jn/punkshell" #packager: declared identity for published artifacts (declarative, not proof - diff --git a/src/AGENTS.md b/src/AGENTS.md index dad3863d..5a923727 100644 --- a/src/AGENTS.md +++ b/src/AGENTS.md @@ -70,7 +70,7 @@ Recovery after a wrong path guess: - Provenance warnings (dirty-src gate + vendorupdate source-project check) print with a plain column-0 `PROVENANCE-WARNING:` token (grep for it in captured make.tcl output) and are recapped at the end of the run via make.tcl's wrapped `::exit`. Interactive terminal runs get a 4-second ctrl-c grace countdown before a dirty run proceeds; piped/agent runs skip it. `tclsh src/make.tcl check` reports the current src provenance status and what the producing commands would do. - Use `tclsh src/make.tcl vfscommonupdate` to rebuild `_vfscommon.vfs`. The REPLACE confirmation prompts on an interactive terminal; unattended/agent runs must pass `-confirm 0` (with the default `-confirm 1` and a piped/closed stdin, make.tcl aborts fast with guidance instead of reading stdin - do not pipe `y`). - `tclsh src/make.tcl workflow` prints an embedded ASCII data-flow overview of the release workflow (the TERMINOLOGY stage-verb key, release-ready command sequence, source→outputs folder flow, kit assembly, keyed notes). The text lives in `::punkboot::workflow_text` in `src/make.tcl` - embedded rather than a data file so it travels with the make.tcl copies seeded into generated projects. **Update contract (agents):** whenever make.tcl data flow changes - a make.tcl subcommand added/removed/repurposed, a source or output folder added or rerouted, a propagation target added, a gate (staleness/provenance/confirm) or deploy behaviour changed - update the workflow text in the same change-set. Keep it plain ASCII, max line width 100, and preserve the TERMINOLOGY / RELEASE SEQUENCE / DIAGRAM / KEY-NOTES / MAINTENANCE structure; verify by running `tclsh src/make.tcl workflow` (and once under `PUNKBOOT_PLAIN=1`). Adding/changing a subcommand also means: SUMMARIES + HELPTEXTS + its braced `punk::args::define` block (+ SUBGROUPS) in `::punkboot::argdoc`, `known_commands`, the plain `punkboot_gethelp` text, and a project-version bump (make.tcl interface is product surface per root AGENTS.md). Layout make.tcl copies pick the change up via the thin-layout sync step in any `make.tcl modules`/`libs`/`packages`/`bakehouse` run - do not hand-sync them. -- make.tcl subcommands and flags are punk::args-declared (G-030; help depth G-143, achieved; single-mechanism revision 2026-08-05, user direction): `tclsh src/make.tcl help ?subcommand? ?arg ...?` shows tabular usage - the leading `help` word is the ONLY help entry; help FLAGS (-help/--help/-h//?) are ordinary arguments that fail whatever gate they meet (top level: the subcommand choice violation; tool option position: noformmatch; a value position with restricted choices: the choice gate; degraded scan: Unknown flag + help exit 1), with the usage error itself carrying the documentation. Invalid arguments produce punk::args usage errors. Bare `make.tcl help` (and bare `make.tcl`) renders a rich top-level overview (2026-08-02, the `i info` style): per-subcommand cells carrying the one-line summary plus that subcommand's own auto-generated synopsis line(s) - per-form lines for the multi-form subcommands - grouped by SUBGROUPS (rich `-choicelabels` built by `::punkboot::argdoc::overview_labels` from `punk::args::synopsis` per id, hung on the separate `(script)::punkboot.overview` id so dispatch never pays the label cost; the lean `(script)::punkboot` id remains the dispatch surface). `-choicecolumns` is 1 for now - the long one-line summaries make a 2-column layout too wide without table CELL WRAPPING; revisit `-choicecolumns 2` when that lands (user decision 2026-08-02; the `## FORM` header lines are likewise deliberately dropped from the cells). tool and buildsuite are multi-form definitions (one @form per action with a literal action leader), so whole-subcommand help renders one synopsis line per action, and help accepts the subcommand's own COMMAND LINE after the subject and DRY-RUNS it through that subcommand's declaration: `make.tcl help tool build -test 0 punkzip` parses via punk::args form auto-selection and renders the build form's usage, while a line the subcommand would reject (unknown action word, option-first line, unknown flag in option position) gets the same pointed punk::args noformmatch diagnosis dispatch gives - synopsis plus per-form reasons - on stderr with exit 1, never a plain-help fallback. There is deliberately no bare-action carve-out: an action word alone is dry-run too, so a form with required values reports them via the diagnosis (`help buildsuite build` exits 1 naming the missing suitename - the error table carries the form's argument rows, so it IS the form documentation). An accepted line is confirmed with a one-line received-args report after the table (`dry-run: line accepted (form build) - action = build | -test = 0 | toolname = punkzip`); punk::args' positional model consumes flag-like words at/after the first value position as VALUES where the value arg is unconstrained (tool's toolname), and the report makes such swallows visible - value args with restricted choices (bake/bakelist kitnames as of 2026-08-02) instead reject the flag-like word at the choice gate, exactly as dispatch does. Declaration-level passthroughs (shell args, buildsuite driver args) always parse clean; numeric form indexes are deliberately not an interface. tool DISPATCH parses through its definition: unknown actions/flags are punk::args usage errors (exit 1), and the declared positional model puts options before the tool names (`make.tcl tool build -test 0 ...`, matching bake's flags-before-names convention; the historic flag-anywhere order is deliberately not accepted - a flag-shaped tool name earns a stderr hint). buildsuite keeps its passthrough driver-args contract - its forms serve help/synopsis accuracy only. The dispatch degrades to plain scan/help when the bootsupport punk::args (or its rendering stack) is stale or unavailable; `PUNKBOOT_PLAIN=1` forces the degraded mode for troubleshooting (the degraded tool handler keeps the historic manual tail parse and exit-2 surface). Piped characterization: `src/tests/shell/testsuites/punkexe/maketclhelp.test`. The `::punkboot::argdoc` definitions are braced file-style blocks using the G-045 authoring mechanisms — `-&` record continuations, tstr placeholders pulling SUMMARIES/HELPTEXTS and the shared `OPT_*` option fragments — with `-help` bodies expanding as deferred display fields so the HELPTEXTS block indentation deliberately renders as a centred Description (no `@normalize` re-basing). Synopses are the automatic punk::args bracket notation via the G-144 `@cmd -name` fallback; explicit `@form -synopsis` overrides were retired 2026-08-01 except the top-level `make.tcl ?subcommand? ?flags?` line (it states the bare-invocation/flags reality the auto render cannot) - do not reintroduce per-subcommand overrides without cause. See src/modules/AGENTS.md "punk::args definition authoring ergonomics" before editing them or authoring similar definitions. +- make.tcl subcommands and flags are punk::args-declared (G-030; help depth G-143, achieved; single-mechanism revision 2026-08-05, user direction): `tclsh src/make.tcl help ?subcommand? ?arg ...?` shows tabular usage - the leading `help` word is the ONLY help entry; help FLAGS (-help/--help/-h//?) are ordinary arguments that fail whatever gate they meet (top level: the subcommand choice violation; tool option position: noformmatch; a value position with restricted choices: the choice gate; degraded scan: Unknown flag + help exit 1), with the usage error itself carrying the documentation. Invalid arguments produce punk::args usage errors. Bare `make.tcl help` (and bare `make.tcl`) renders a rich top-level overview (2026-08-02, the `i info` style): per-subcommand cells carrying the one-line summary plus that subcommand's own auto-generated synopsis line(s) - per-form lines for the multi-form subcommands - grouped by SUBGROUPS (rich `-choicelabels` built by `::punkboot::argdoc::overview_labels` from `punk::args::synopsis` per id, hung on the separate `(script)::punkboot.overview` id so dispatch never pays the label cost; the lean `(script)::punkboot` id remains the dispatch surface). `-choicecolumns` is 1 for now - the long one-line summaries make a 2-column layout too wide without table CELL WRAPPING; revisit `-choicecolumns 2` when that lands (user decision 2026-08-02; the `## FORM` header lines are likewise deliberately dropped from the cells). tool and buildsuite are multi-form definitions (one @form per action with a literal action leader), so whole-subcommand help renders one synopsis line per action, and help accepts the subcommand's own COMMAND LINE after the subject and DRY-RUNS it through that subcommand's declaration: `make.tcl help tool build -test 0 punkzip` parses via punk::args form auto-selection and renders the build form's usage, while a line the subcommand would reject (unknown action word, option-first line, unknown flag in option position) gets the same pointed punk::args noformmatch diagnosis dispatch gives - synopsis plus per-form reasons - on stderr with exit 1, never a plain-help fallback. There is deliberately no bare-action carve-out: an action word alone is dry-run too, so a form with required values reports them via the diagnosis (`help buildsuite build` exits 1 naming the missing suitename - the error table carries the form's argument rows, so it IS the form documentation). An accepted line is confirmed with `dry-run: line accepted (form build)` plus the `punk::args::parse_report` landing table stacked under the usage render (G-151 follow-through 2026-08-09: Argument|Source|Value rows covering received args and defaults-in-effect; a stale pre-0.18.0 punk::args snapshot or broken render stack degrades to the historic one-line received-args clue); punk::args' positional model consumes flag-like words at/after the first value position as VALUES where the value arg is unconstrained (tool's toolname), and the landing row makes such swallows visible - value args with restricted choices (bake/bakelist kitnames as of 2026-08-02) instead reject the flag-like word at the choice gate, exactly as dispatch does. Declaration-level passthroughs (shell args, buildsuite driver args) always parse clean; numeric form indexes are deliberately not an interface. tool DISPATCH parses through its definition: unknown actions/flags are punk::args usage errors (exit 1), and the declared positional model puts options before the tool names (`make.tcl tool build -test 0 ...`, matching bake's flags-before-names convention; the historic flag-anywhere order is deliberately not accepted - a flag-shaped tool name earns a stderr hint). buildsuite keeps its passthrough driver-args contract - its forms serve help/synopsis accuracy only. The dispatch degrades to plain scan/help when the bootsupport punk::args (or its rendering stack) is stale or unavailable; `PUNKBOOT_PLAIN=1` forces the degraded mode for troubleshooting (the degraded tool handler keeps the historic manual tail parse and exit-2 surface). Piped characterization: `src/tests/shell/testsuites/punkexe/maketclhelp.test`. The `::punkboot::argdoc` definitions are braced file-style blocks using the G-045 authoring mechanisms — `-&` record continuations, tstr placeholders pulling SUMMARIES/HELPTEXTS and the shared `OPT_*` option fragments — with `-help` bodies expanding as deferred display fields so the HELPTEXTS block indentation deliberately renders as a centred Description (no `@normalize` re-basing). Synopses are the automatic punk::args bracket notation via the G-144 `@cmd -name` fallback; explicit `@form -synopsis` overrides were retired 2026-08-01 except the top-level `make.tcl ?subcommand? ?flags?` line (it states the bare-invocation/flags reality the auto render cannot) - do not reintroduce per-subcommand overrides without cause. See src/modules/AGENTS.md "punk::args definition authoring ergonomics" before editing them or authoring similar definitions. - Uncommitted `make.tcl`-generated outputs may be batched into one commit, regardless of how many `make.tcl` invocations produced them. This covers punkcheck-managed build outputs that are VCS-tracked: `src/bootsupport/`, `_vfscommon.vfs/modules` + `/lib`, declared per-kit `*.vfs/lib_tcl/` subfolders, the thin-layout sync copies (`src/project_layouts/vendor/punk//src/{make.tcl,build.tcl}` + bootsupport manifest copies + the inert `gitignore.in` payload copies refreshed from the root `.gitignore` - G-012), and the module-shipped layout payload under `src/modules/punk/mix/#modpod-templates-*/templates/project_layouts/` (G-087: synced from `src/project_layouts` - never hand-edit the modpod copy). (Root `modules/`, `lib/`, `modules_tcl/`, `lib_tcl/` are gitignored and never committed.) Each module's old→new version rename is atomic; a regeneration reflects one build-output refresh, not independent hand-edits. This is a carve-out from generic "split aggressively" commit guidance for punkcheck-managed build outputs only — it does not cover user-curated VFS content (`_config/`, per-kit `*.vfs/` outside declared `lib_tcl` subfolders, `_aside/`, `mkzipfix.vfs`, `_vfscommon.vfs/doc`), which commits separately per its own concerns. - **End-of-run `BAKE SUMMARY`.** `bake`/`bakehouse` end with a counts headline (`BAKE SUMMARY: deployed=N unchanged=N not-built=N failed=N`, plus the number of `BAKE-WARNING`s recapped after it) followed by one line per kit under `NOT BUILT` / `UNCHANGED` / `INSTALLED KITS` / `FAILED KITS` — the answer to "what actually baked?" once the per-kit output has scrolled out of reach. Rows are keyed by the kit's path relative to `bin/` and `src/_bake/` (`kits//` for cross-target outputs), so same-named kits for different targets stay distinct. It covers the WHOLE configured matrix, not just the kits the run touched: an absent runtime, `bake_default=false`, a release-gated scheme output or a missing `.vfs` folder each keep a kit out of the kit machinery entirely, and on a part-populated runtime store those are most of the rows — each is listed with its reason rather than omitted. A selective bake summarises only the kits it was asked for. The block is stdout so it stays contiguous under redirection; failures are echoed to stderr as well, from the same ledger (`::punkboot::bake_outcomes`, fed by `::punkboot::record_bake_outcome` at every point that settles a kit's fate), so the two renderings cannot drift apart. **Exit status:** a run that completed with one or more FAILED kits exits **4**, distinct from make.tcl's refuse-to-run codes (1 usage/gate refusal, 2 structural abort, 3 config error) so "could not run" stays distinguishable from "ran, produced failures". The `notbuilt` bucket never affects the exit code — an absent runtime, `bake_default=false` or a release-gated output are normal states, and a part-populated runtime store is the common case. This matters because `punk::mix::cli::make` (`dev make `) branches on the exit code: before this it printed `OK make finished` and returned true for a bake in which every kit failed. - **Boot-precondition gate (G-125).** A bake refuses any kit whose merged `.vfs` supplies no tcl library: the kit is listed under `FAILED KITS` with a reason naming the cause, and NOTHING is written - no `src/_bake/`, no deploy, and the previously deployed `bin/` is left byte-identical. This is deliberately a failure rather than a warning: the deploy step deletes the old kit before copying the new one, so the pre-gate behaviour replaced a working shell with an artifact that died at startup with `application-specific initialization failed: Cannot find a usable init.tcl`. The check is structural and non-executing (so it covers cross-target kits too): `init.tcl` in `tcl_library/`, `lib/tcl./` or `tcl./` - the third for runtimes whose archive mounts at the executable's own path rather than `//zipfs:/app`, so `[info library]` is `/tcl8.6` (the androwish/undroidwish zipfs backport for 8.6 does this) - plus one companion file a real Tcl library carries beside it (`tm.tcl`, `package.tcl`, `auto.tcl`, `clock.tcl`, `history.tcl`, `word.tcl`) or an `encoding/` directory - without that second test the `lib/BWidget1.10.1/init.tcl` every punkshell kit carries would answer for a tcl library it cannot provide. It reads the MERGED tree rather than asking whether extraction ran, so a `.vfs` that legitimately supplies its own tcl library (`src/vfs/punk8_statictwapi.vfs`, `src/vfs/punk9test.vfs`) bakes and deploys unchanged. **Remedy when a kit is refused:** the usual cause is that the runtime's own payload could not be extracted - read the recapped `BAKE-WARNING` naming what was tried, then either fix extraction (a driving tclsh with no zipfs needs punk::zip >= 0.2.0 in bootsupport - see the extraction bullet above), point the kit at a runtime that carries a payload, or have the kit's `.vfs` supply a tcl library itself. The predicate is `punkboot::utils::vfs_boot_library_report` called through a guarded require, exactly as the dirty-src provenance check is: a stale or missing bootsupport snapshot degrades the gate to a `NOTE` rather than failing every kit. `tclsh src/make.tcl check` reports whether the gate is `ACTIVE` or `UNAVAILABLE`. diff --git a/src/make.tcl b/src/make.tcl index 70a5a801..51ccc1a6 100644 --- a/src/make.tcl +++ b/src/make.tcl @@ -4252,18 +4252,18 @@ if {$do_help} { #dispatch would parse them (punk::args form auto-selection - literal #action leaders, choice prefixes). A line the subcommand would accept #renders the matched form's single-form usage (whole usage for - #single-form subjects) plus a one-line report of where each received - #word landed; a line it would reject gets the same pointed punk::args - #diagnosis dispatch would give on stderr with exit 1 - the error table - #carries the all-forms synopsis, per-form reasons and argument rows, - #so it IS the form documentation. Deliberately no bare-action - #carve-out: 'help buildsuite build' shows the build form via the - #diagnosis that a suitename is still required. Note punk::args' - #positional model: flag-like words at or after the first value - #position are consumed as VALUES - the accepted-line report makes - #that visible ('kitname = punk91 -confirm 0'); declaration-level - #passthroughs (shell args, buildsuite driver args) parse clean by - #design. + #single-form subjects) plus the punk::args::parse_report landing table + #of where each received word landed; a line it would reject gets the + #same pointed punk::args diagnosis dispatch would give on stderr with + #exit 1 - the error table carries the all-forms synopsis, per-form + #reasons and argument rows, so it IS the form documentation. + #Deliberately no bare-action carve-out: 'help buildsuite build' shows + #the build form via the diagnosis that a suitename is still required. + #Note punk::args' positional model: flag-like words at or after the + #first value position are consumed as VALUES - the landing report + #makes that visible (a swallowed flag-like word is attributed to the + #value argument that consumed it); declaration-level passthroughs + #(shell args, buildsuite driver args) parse clean by design. if {[catch {punk::args::parse $help_words -errorstyle $::punkboot::errstyle withid $usage_id} argd]} { puts stderr $argd exit 1 @@ -4272,26 +4272,34 @@ if {$do_help} { if {[llength $form_names] > 1} { lappend usage_args -form [dict get $argd form] } - #interim 'where did my words land' clue (punk::args has no annotated - #success render yet): name = value pairs for RECEIVED args only - #(defaulted opts omitted), so a swallowed flag-like word shows up - #against the argument that consumed it. - set recnames [list] - foreach {rn ri} [dict get $argd received] { - if {$rn ni $recnames} {lappend recnames $rn} - } - set parts [list] - foreach section {leaders opts values} { - foreach {an av} [dict get $argd $section] { - if {$an in $recnames} {lappend parts "$an = $av"} - } - } set accepted_note "dry-run: line accepted" if {[llength $form_names] > 1} { append accepted_note " (form [dict get $argd form])" } - if {[llength $parts]} { - append accepted_note " - [join $parts { | }]" + #G-151 follow-through: the landing report is punk::args::parse_report's + #annotated success render (Argument|Source|Value rows: received args + #plus defaults-in-effect, elided values marked) stacked under the + #usage render. Catch-guarded per the G-030 degrade rule - a stale + #punk::args snapshot (pre-0.18.0, no parse_report) or a broken render + #stack falls back to the pre-G-151 one-line received-args clue. + if {![catch {punk::args::parse_report $argd} landing_report]} { + if {$landing_report ne ""} { + append accepted_note \n$landing_report + } + } else { + set recnames [list] + foreach {rn ri} [dict get $argd received] { + if {$rn ni $recnames} {lappend recnames $rn} + } + set parts [list] + foreach section {leaders opts values} { + foreach {an av} [dict get $argd $section] { + if {$an in $recnames} {lappend parts "$an = $av"} + } + } + if {[llength $parts]} { + append accepted_note " - [join $parts { | }]" + } } } if {![catch {punk::args::usage {*}$usage_args $usage_id} usage_out]} { diff --git a/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl b/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl index 70a5a801..51ccc1a6 100644 --- a/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl +++ b/src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl @@ -4252,18 +4252,18 @@ if {$do_help} { #dispatch would parse them (punk::args form auto-selection - literal #action leaders, choice prefixes). A line the subcommand would accept #renders the matched form's single-form usage (whole usage for - #single-form subjects) plus a one-line report of where each received - #word landed; a line it would reject gets the same pointed punk::args - #diagnosis dispatch would give on stderr with exit 1 - the error table - #carries the all-forms synopsis, per-form reasons and argument rows, - #so it IS the form documentation. Deliberately no bare-action - #carve-out: 'help buildsuite build' shows the build form via the - #diagnosis that a suitename is still required. Note punk::args' - #positional model: flag-like words at or after the first value - #position are consumed as VALUES - the accepted-line report makes - #that visible ('kitname = punk91 -confirm 0'); declaration-level - #passthroughs (shell args, buildsuite driver args) parse clean by - #design. + #single-form subjects) plus the punk::args::parse_report landing table + #of where each received word landed; a line it would reject gets the + #same pointed punk::args diagnosis dispatch would give on stderr with + #exit 1 - the error table carries the all-forms synopsis, per-form + #reasons and argument rows, so it IS the form documentation. + #Deliberately no bare-action carve-out: 'help buildsuite build' shows + #the build form via the diagnosis that a suitename is still required. + #Note punk::args' positional model: flag-like words at or after the + #first value position are consumed as VALUES - the landing report + #makes that visible (a swallowed flag-like word is attributed to the + #value argument that consumed it); declaration-level passthroughs + #(shell args, buildsuite driver args) parse clean by design. if {[catch {punk::args::parse $help_words -errorstyle $::punkboot::errstyle withid $usage_id} argd]} { puts stderr $argd exit 1 @@ -4272,26 +4272,34 @@ if {$do_help} { if {[llength $form_names] > 1} { lappend usage_args -form [dict get $argd form] } - #interim 'where did my words land' clue (punk::args has no annotated - #success render yet): name = value pairs for RECEIVED args only - #(defaulted opts omitted), so a swallowed flag-like word shows up - #against the argument that consumed it. - set recnames [list] - foreach {rn ri} [dict get $argd received] { - if {$rn ni $recnames} {lappend recnames $rn} - } - set parts [list] - foreach section {leaders opts values} { - foreach {an av} [dict get $argd $section] { - if {$an in $recnames} {lappend parts "$an = $av"} - } - } set accepted_note "dry-run: line accepted" if {[llength $form_names] > 1} { append accepted_note " (form [dict get $argd form])" } - if {[llength $parts]} { - append accepted_note " - [join $parts { | }]" + #G-151 follow-through: the landing report is punk::args::parse_report's + #annotated success render (Argument|Source|Value rows: received args + #plus defaults-in-effect, elided values marked) stacked under the + #usage render. Catch-guarded per the G-030 degrade rule - a stale + #punk::args snapshot (pre-0.18.0, no parse_report) or a broken render + #stack falls back to the pre-G-151 one-line received-args clue. + if {![catch {punk::args::parse_report $argd} landing_report]} { + if {$landing_report ne ""} { + append accepted_note \n$landing_report + } + } else { + set recnames [list] + foreach {rn ri} [dict get $argd received] { + if {$rn ni $recnames} {lappend recnames $rn} + } + set parts [list] + foreach section {leaders opts values} { + foreach {an av} [dict get $argd $section] { + if {$an in $recnames} {lappend parts "$an = $av"} + } + } + if {[llength $parts]} { + append accepted_note " - [join $parts { | }]" + } } } if {![catch {punk::args::usage {*}$usage_args $usage_id} usage_out]} { diff --git a/src/project_layouts/vendor/punk/basic/src/make.tcl b/src/project_layouts/vendor/punk/basic/src/make.tcl index 70a5a801..51ccc1a6 100644 --- a/src/project_layouts/vendor/punk/basic/src/make.tcl +++ b/src/project_layouts/vendor/punk/basic/src/make.tcl @@ -4252,18 +4252,18 @@ if {$do_help} { #dispatch would parse them (punk::args form auto-selection - literal #action leaders, choice prefixes). A line the subcommand would accept #renders the matched form's single-form usage (whole usage for - #single-form subjects) plus a one-line report of where each received - #word landed; a line it would reject gets the same pointed punk::args - #diagnosis dispatch would give on stderr with exit 1 - the error table - #carries the all-forms synopsis, per-form reasons and argument rows, - #so it IS the form documentation. Deliberately no bare-action - #carve-out: 'help buildsuite build' shows the build form via the - #diagnosis that a suitename is still required. Note punk::args' - #positional model: flag-like words at or after the first value - #position are consumed as VALUES - the accepted-line report makes - #that visible ('kitname = punk91 -confirm 0'); declaration-level - #passthroughs (shell args, buildsuite driver args) parse clean by - #design. + #single-form subjects) plus the punk::args::parse_report landing table + #of where each received word landed; a line it would reject gets the + #same pointed punk::args diagnosis dispatch would give on stderr with + #exit 1 - the error table carries the all-forms synopsis, per-form + #reasons and argument rows, so it IS the form documentation. + #Deliberately no bare-action carve-out: 'help buildsuite build' shows + #the build form via the diagnosis that a suitename is still required. + #Note punk::args' positional model: flag-like words at or after the + #first value position are consumed as VALUES - the landing report + #makes that visible (a swallowed flag-like word is attributed to the + #value argument that consumed it); declaration-level passthroughs + #(shell args, buildsuite driver args) parse clean by design. if {[catch {punk::args::parse $help_words -errorstyle $::punkboot::errstyle withid $usage_id} argd]} { puts stderr $argd exit 1 @@ -4272,26 +4272,34 @@ if {$do_help} { if {[llength $form_names] > 1} { lappend usage_args -form [dict get $argd form] } - #interim 'where did my words land' clue (punk::args has no annotated - #success render yet): name = value pairs for RECEIVED args only - #(defaulted opts omitted), so a swallowed flag-like word shows up - #against the argument that consumed it. - set recnames [list] - foreach {rn ri} [dict get $argd received] { - if {$rn ni $recnames} {lappend recnames $rn} - } - set parts [list] - foreach section {leaders opts values} { - foreach {an av} [dict get $argd $section] { - if {$an in $recnames} {lappend parts "$an = $av"} - } - } set accepted_note "dry-run: line accepted" if {[llength $form_names] > 1} { append accepted_note " (form [dict get $argd form])" } - if {[llength $parts]} { - append accepted_note " - [join $parts { | }]" + #G-151 follow-through: the landing report is punk::args::parse_report's + #annotated success render (Argument|Source|Value rows: received args + #plus defaults-in-effect, elided values marked) stacked under the + #usage render. Catch-guarded per the G-030 degrade rule - a stale + #punk::args snapshot (pre-0.18.0, no parse_report) or a broken render + #stack falls back to the pre-G-151 one-line received-args clue. + if {![catch {punk::args::parse_report $argd} landing_report]} { + if {$landing_report ne ""} { + append accepted_note \n$landing_report + } + } else { + set recnames [list] + foreach {rn ri} [dict get $argd received] { + if {$rn ni $recnames} {lappend recnames $rn} + } + set parts [list] + foreach section {leaders opts values} { + foreach {an av} [dict get $argd $section] { + if {$an in $recnames} {lappend parts "$an = $av"} + } + } + if {[llength $parts]} { + append accepted_note " - [join $parts { | }]" + } } } if {![catch {punk::args::usage {*}$usage_args $usage_id} usage_out]} { diff --git a/src/project_layouts/vendor/punk/project-0.1/src/make.tcl b/src/project_layouts/vendor/punk/project-0.1/src/make.tcl index 70a5a801..51ccc1a6 100644 --- a/src/project_layouts/vendor/punk/project-0.1/src/make.tcl +++ b/src/project_layouts/vendor/punk/project-0.1/src/make.tcl @@ -4252,18 +4252,18 @@ if {$do_help} { #dispatch would parse them (punk::args form auto-selection - literal #action leaders, choice prefixes). A line the subcommand would accept #renders the matched form's single-form usage (whole usage for - #single-form subjects) plus a one-line report of where each received - #word landed; a line it would reject gets the same pointed punk::args - #diagnosis dispatch would give on stderr with exit 1 - the error table - #carries the all-forms synopsis, per-form reasons and argument rows, - #so it IS the form documentation. Deliberately no bare-action - #carve-out: 'help buildsuite build' shows the build form via the - #diagnosis that a suitename is still required. Note punk::args' - #positional model: flag-like words at or after the first value - #position are consumed as VALUES - the accepted-line report makes - #that visible ('kitname = punk91 -confirm 0'); declaration-level - #passthroughs (shell args, buildsuite driver args) parse clean by - #design. + #single-form subjects) plus the punk::args::parse_report landing table + #of where each received word landed; a line it would reject gets the + #same pointed punk::args diagnosis dispatch would give on stderr with + #exit 1 - the error table carries the all-forms synopsis, per-form + #reasons and argument rows, so it IS the form documentation. + #Deliberately no bare-action carve-out: 'help buildsuite build' shows + #the build form via the diagnosis that a suitename is still required. + #Note punk::args' positional model: flag-like words at or after the + #first value position are consumed as VALUES - the landing report + #makes that visible (a swallowed flag-like word is attributed to the + #value argument that consumed it); declaration-level passthroughs + #(shell args, buildsuite driver args) parse clean by design. if {[catch {punk::args::parse $help_words -errorstyle $::punkboot::errstyle withid $usage_id} argd]} { puts stderr $argd exit 1 @@ -4272,26 +4272,34 @@ if {$do_help} { if {[llength $form_names] > 1} { lappend usage_args -form [dict get $argd form] } - #interim 'where did my words land' clue (punk::args has no annotated - #success render yet): name = value pairs for RECEIVED args only - #(defaulted opts omitted), so a swallowed flag-like word shows up - #against the argument that consumed it. - set recnames [list] - foreach {rn ri} [dict get $argd received] { - if {$rn ni $recnames} {lappend recnames $rn} - } - set parts [list] - foreach section {leaders opts values} { - foreach {an av} [dict get $argd $section] { - if {$an in $recnames} {lappend parts "$an = $av"} - } - } set accepted_note "dry-run: line accepted" if {[llength $form_names] > 1} { append accepted_note " (form [dict get $argd form])" } - if {[llength $parts]} { - append accepted_note " - [join $parts { | }]" + #G-151 follow-through: the landing report is punk::args::parse_report's + #annotated success render (Argument|Source|Value rows: received args + #plus defaults-in-effect, elided values marked) stacked under the + #usage render. Catch-guarded per the G-030 degrade rule - a stale + #punk::args snapshot (pre-0.18.0, no parse_report) or a broken render + #stack falls back to the pre-G-151 one-line received-args clue. + if {![catch {punk::args::parse_report $argd} landing_report]} { + if {$landing_report ne ""} { + append accepted_note \n$landing_report + } + } else { + set recnames [list] + foreach {rn ri} [dict get $argd received] { + if {$rn ni $recnames} {lappend recnames $rn} + } + set parts [list] + foreach section {leaders opts values} { + foreach {an av} [dict get $argd $section] { + if {$an in $recnames} {lappend parts "$an = $av"} + } + } + if {[llength $parts]} { + append accepted_note " - [join $parts { | }]" + } } } if {![catch {punk::args::usage {*}$usage_args $usage_id} usage_out]} { diff --git a/src/tests/shell/testsuites/punkexe/maketclhelp.test b/src/tests/shell/testsuites/punkexe/maketclhelp.test index 16c618a1..61c8360a 100644 --- a/src/tests/shell/testsuites/punkexe/maketclhelp.test +++ b/src/tests/shell/testsuites/punkexe/maketclhelp.test @@ -36,11 +36,14 @@ package require tcltest # itself usage documentation); no bare-action carve-out: 'help buildsuite # build' (suitename required) exits 1 with that same diagnosis, whose table # still carries the build form's argument rows; accepted lines are confirmed -# with a received-args report ('dry-run: line accepted (form build) - action = -# build | ...'); bake kitnames are -choicerestricted (2026-08-02), so the +# with 'dry-run: line accepted (form N)' plus punk::args::parse_report's +# landing table stacked under the usage render (G-151 follow-through +# 2026-08-09: Argument|Source|Value rows, received distinguished from +# default-in-effect); bake kitnames are -choicerestricted (2026-08-02), so the # out-of-order 'help bake punk91 -confirm 0' is REJECTED at the choice gate # exactly as dispatch would reject it (Received: '-confirm'), while -# 'help bake punk91' is accepted with 'kitname = punk91' reported +# 'help bake punk91' is accepted with a kitname/received/punk91 landing row +# (defaulted opts get default rows) # - 'help tool frobnicate' / 'help buildsuite frobnicate': exit 1 with the # punk::args noformmatch diagnosis naming the candidate forms # - 'tool build -help' / 'make.tcl -help': help flags are ordinary arguments now - @@ -200,25 +203,29 @@ namespace eval ::testspace { set result } -result {timedout 0 exitcode 0 esc 0 testopt 1 toolname 1 choicelabel 1} - #added 2026-08-01 (agent, G-143 refinement); updated 2026-08-02 (agent) - help - #dry-runs the subject's own command line through its declaration: accepted - #lines render the matched form's usage plus a received-args report (flag-like - #words at/after the first value position parse as VALUES where the value arg - #is unconstrained - tool's toolname - and the report reveals the swallow); + #added 2026-08-01 (agent, G-143 refinement); updated 2026-08-02 (agent); + #updated 2026-08-09 (agent, G-151 follow-through) - help dry-runs the + #subject's own command line through its declaration: accepted lines render + #the matched form's usage plus punk::args::parse_report's landing table + #(Argument|Source|Value rows; flag-like words at/after the first value + #position parse as VALUES where the value arg is unconstrained - tool's + #toolname - and the landing row reveals the swallow; defaulted opts get + #default rows, the received/default distinction being the Source column); #rejected lines (option-first, numeric form index, unknown flags in option #position, and - since kitnames became -choicerestricted 2026-08-02 - the #out-of-order 'bake punk91 -confirm 0', whose '-confirm' fails the kitname #choice gate exactly as dispatch rejects it) exit 1 with the punk::args #diagnosis, whose table carries the synopsis - the error is itself usage #documentation - test maketcl_help_cmdline_selection {'make.tcl help tool build -test 0 punkzip' renders the build form + received-args report; 'help bake punk91' accepted/reported; rejected lines (incl. the kitname choice gate) exit 1 with the self-documenting diagnosis} -constraints {tclshavailable} -body { + test maketcl_help_cmdline_selection {'make.tcl help tool build -test 0 punkzip' renders the build form + parse_report landing table; 'help bake punk91' accepted with landing rows; rejected lines (incl. the kitname choice gate) exit 1 with the self-documenting diagnosis} -constraints {tclshavailable} -body { set r [maketcl_run {help tool build -test 0 punkzip}] set out [dict get $r output] set result [list] lappend result timedout [dict get $r timedout] exitcode [dict get $r exitcode] lappend result synbuild [regexp {make\.tcl tool build \[-test \] \[toolname\]\.\.\.} $out] lappend result accepted [regexp {dry-run: line accepted \(form build\)} $out] - lappend result landed [regexp {toolname = punkzip} $out] + lappend result reporthdr [regexp {Argument[^\n]*Source[^\n]*Value} $out] + lappend result landed [regexp {toolname[^\n]*received[^\n]*punkzip} $out] set r2 [maketcl_run {help bake punk91 -confirm 0}] set out2 [dict get $r2 output] lappend result timedout2 [dict get $r2 timedout] exitcode2 [dict get $r2 exitcode] @@ -226,7 +233,8 @@ namespace eval ::testspace { set r2b [maketcl_run {help bake punk91}] set out2b [dict get $r2b output] lappend result timedout2b [dict get $r2b timedout] exitcode2b [dict get $r2b exitcode] - lappend result accepted2b [regexp {kitname = punk91} $out2b] + lappend result accepted2b [regexp {kitname[^\n]*received[^\n]*punk91} $out2b] + lappend result default2b [regexp -- {-confirm[^\n]*default[^\n]*1} $out2b] set r3 [maketcl_run {help tool -test 0 build}] set out3 [dict get $r3 output] lappend result timedout3 [dict get $r3 timedout] exitcode3 [dict get $r3 exitcode] @@ -237,7 +245,7 @@ namespace eval ::testspace { lappend result timedout4 [dict get $r4 timedout] exitcode4 [dict get $r4 exitcode] lappend result noform4 [regexp {No form of the command matches the supplied arguments} $out4] set result - } -result {timedout 0 exitcode 0 synbuild 1 accepted 1 landed 1 timedout2 0 exitcode2 1 gate2 1 timedout2b 0 exitcode2b 0 accepted2b 1 timedout3 0 exitcode3 1 noform3 1 synindiag3 1 timedout4 0 exitcode4 1 noform4 1} + } -result {timedout 0 exitcode 0 synbuild 1 accepted 1 reporthdr 1 landed 1 timedout2 0 exitcode2 1 gate2 1 timedout2b 0 exitcode2b 0 accepted2b 1 default2b 1 timedout3 0 exitcode3 1 noform3 1 synindiag3 1 timedout4 0 exitcode4 1 noform4 1} #added 2026-08-01 (agent, G-143) #formhints pin added 2026-08-05 (agent, G-149): buildsuite adopts