- auto_exec 0.1.1: hash's show_or_set name value carries -regexprefail {^-} - a bare punk::args::parse of a flag-led line now selects its flag form uniquely instead of 'Ambiguous arguments ... show_or_set, <flag form>' (show_or_set swallowed flag-like words as plain names), so cmdhelp 'i hash -r' narrows to the rehash form with no handler involvement; runtime behaviour unchanged (the handler's first-word switch already rejected dash-led names; at the declaration level they now need an explicit flag form - accepted cost). Help form tightened (user direction): -anyopts dropped so --help must lead, and @values -flagvalues 1 (punk::args 0.15.0) makes trailing words of any shape consume as ignored values - a flag-led line no longer marks the help form viable via adhoc-option seating. Handler fix (user-landed): the multi-name -t missing-name branch printed "$hash: nm: not found" (undefined variable) - now "hash: $nm: not found"
- ns 0.9.2: documentation-only - cmdhelp's subcommand argdoc now states the multi-form narrowing contract explicitly (no code change; the G-041 advisory-parse candidacy already narrowed on a unique selection)
- cmdhelp.test: G-150 pins - flag-led narrowing on -r/-d, bare-parse selection incl -t/plain/empty lines, the dash-led-name noformmatch cost, no-word/unknown-flag/ambiguity whole-render fallbacks, runtime-unchanged hash behaviour, help-form narrowing and dashword swallow, and the no-false-viable-help pin; plus a GAP pin of the valmin-reservation false negative in the G-152 viability verdict (owned by G-164, proposed) - 45/45 green
- src/tests/modules/AGENTS.md: index clause for the new coverage
Assisted-by: harness=opencode; primary-model=openrouter/moonshotai/kimi-k3; api-location=openrouter.ai
#-l (build a list of hash -p <path> <name> entries for all autoexec commands that can be used in a script to pre-populate the hash table without needing to call auto_execok for each command at runtime)
#---------------------
@form -form {help}
@opts -min 1 -max 1 -anyopts 1
@opts -min 1 -max 1
--help -type none -optional 0 -help\
"Display usage information for this command."
"Display usage information for this command.
Must be the first word after 'hash'; trailing words are
consumed as ignored values."
@values -min 0 -max -1
@values -min 0 -max -1 -flagvalues 1
ignored -type any -multiple 1 -optional 1 -help\
"Additional arguments that are ignored when --help is used"
"Additional arguments that are ignored when --help is used.
Dash-led words are consumed as values here (not treated
#0.1.1 - G-150: hash's show_or_set name value carries -regexprefail {^-}, making the declaration selection-sound: a bare punk::args::parse of a flag-led line now selects its flag form uniquely instead of reporting 'Ambiguous arguments ... show_or_set, <flag form>' (show_or_set swallowed flag-like words as plain names). Unblocks declaration-driven consumers - cmdhelp 'i hash -r' narrows to the rehash form with no handler involvement. Runtime behaviour unchanged: the handler's first-word switch already rejected dash-led names ('invalid option'); at the declaration level such a name now requires an explicit flag form (accepted cost). Same-day refinements (user direction): (a) help form simplified - -anyopts dropped so --help must lead, and @values -flagvalues 1 (the new punk::args 0.15.0 capability, added for this) makes trailing words of any shape - dash-led included - consume as ignored values (a flag-led line no longer marks the help form viable via adhoc-option seating; the arbitrary-flag doc row is gone); (b) handler fix (user-landed) - the multi-name -t missing-name branch printed "$hash: nm: not found" (undefined variable $hash, literal 'nm:') - now "hash: $nm: not found".
#0.9.2 - documentation-only PUNKARGS update: cmdhelp's 'subcommand' argument help now states the multi-form narrowing contract explicitly (argument words resolving to exactly one form narrow the usage display to that form's argument table, marked in the synopsis; no words, an ambiguous match, or words no form accepts keep the whole-command render). No behaviour change - the G-041 advisory-parse candidacy already implements it; G-150 pins the contract in cmdhelp.test against the now selection-sound punk::auto_exec::hash declaration (auto_exec 0.1.1).
#0.9.1 - get_ns_dicts fix: packagetails/packageprefixes initialization moved inside the per-namespace loop - with multiple matched namespaces (path globs, trailing **) every dict after the first carried the union of all earlier namespaces' entries, mis-marking non-package children in nslist/n// underline styling and in the returned data; a child with several deeper packages was also appended to packageprefixes once per matching package (now once). Single-namespace queries (the nslist.test tier A pins) unaffected; the multi-namespace reset is pinned in nslist.test (nslist_classification_multins_package_buckets_reset). Documentation-only PUNKARGS added for get_ns_dicts (documents the returned per-namespace dict keys and -allbelow/-nspathcommands; parsing stays manual) and nslist_dict (caller-relative wrapper). Motivated by exposing the dict data layer to agent tooling: scriptlib/developer/nslist.tcl backing the new tcl-nslist agent skill.
#0.9.0 - cmdhelp -return text: plain-text renderer for machine/LLM consumption - no ANSI codes, no table layout, and the argument section of EVERY command form (or of each form in an explicit -form selection) rendered in a single output, unlike the interactive displays which render only the G-041 best-matching form's argument table ('i -return text after' shows all six after forms' argument sections under one common header). Implementation: resolution is delegated to the existing -return dict recursion (aliases/ensembles/docid/form candidacy all inherited); each form's argument section renders via arg_error -return string -scheme nocolour restricted with -form, is ansistripraw'd, and multiform outputs join the per-form sections (headed ' --ARGUMENTS (form <name>)-- ') below the common header+synopsis emitted once. Argument words still restrict the rendered forms per G-041 semantics ('i -return text after cancel xx' renders the matched cancelscript form only); no advisory-parse error preamble is included (the text form is documentation, not validation - use -return dict for parse status). -grepstr applies to the final text as for other return forms. Supported by the punk::args 0.12.6 arg_error changes (message below usage once; single-line 'name TYPE:... ?DEFAULT:...? ?MULTI:yes?' argument rows in the string renderer). Tests: cmdhelp.test cmdhelp_return_text_all_forms / _argword_form_selection / _single_form_plain_marker.
#0.8.1 - synopsis: fixed 'list element in braces followed by "?" instead of space' error when rendering a command whose definition carries a custom @form -synopsis that is not a valid Tcl list (e.g Tcl manpage style '?-form {int|<formname>...}?' as ::punk::args::parse formerly used - 's parse' errored). The command-head replacement treated each synopsis line as a Tcl list (lreplace); non-list lines now take a textual fallback that replaces the leading word(s) via index walking and passes the tail through verbatim. Pinned in punk/args synopsis.test (synopsis_form_custom_override_nonlist) since that suite already exercises the punk::ns::synopsis path.
@ -46,7 +46,7 @@ Unit tests for editable source modules under `src/modules/`, `src/modules_tcl8/`
- `punk/args/` — punk::args tests (`testsuites/args/`): parsing, choices/choicegroups, forms, rendering/indentation characterization, synopsis display characterization (`synopsis.test`: basic italic argname/`<type>` styling, longopt `--x=` alias forms, literal/literalprefix/stringstartswith/stringendswith type-alternates rendering unitalicised, option alternate parenthesization, multi-element clause display incl `?type?` members and argname tail-word hints, `-typesynopsis` value-element lists and option passthrough incl documenter ANSI, and the small-restricted-choice-set literal rule: 1-3 restricted choices render as unitalicised `|`-joined literals in leader/option/value positions with choicegroups counted, >3 or `-choicerestricted 0` falling back to italics, `-typesynopsis` taking precedence), usage-marking characterization (`usagemarking.test`: -parsedargs/-badarg/-parsestatus/-scheme marking primitives plus goodchoice highlighting of selected/default-in-effect choice words, asserted by SGR-parameter subset against the live colour arrays; the G-049 nocolour/colour-leak GAP pins flipped 2026-07-10 to scheme-statelessness assertions), the G-049 parse-status structure (`parsestatus.test`: punk::args::parse_status overall/per-argument statuses, badarg for type/allocation failures, -caller attribution, errorcode -argspecs stripping), -parsekey characterization (`parsekey.test`: result/received/solos/multis keying, shared-key required satisfaction and defaults, mash-path and prefix-abbreviation keying, plus GAP pins for last-defined-member default precedence, cross-member -multiple value loss, parsekey/optname collision conflation, and values/leaders parsekey breakage - desired-behaviour pins disabled behind punkargsKnownBug in `testsuites/dev/parsekey-knownbugs.test`), and tclcore doc/interpreter behavioural parity (`tclcoreparity.test`, G-054, gated on have_tclcoredocs: 'string is' class choices equal the live-harvested set, per-class docids exist, error-vs-ok agreement across the probe matrix, version-note labels conditional on class presence - expectations derived from the running interpreter, green on 8.6/8.7/9.0; under 8.6 run the file directly via a plain tclkit + tcltest driver since runtests' harness needs newer infrastructure)
- `punk/nav/ns/` — punk::nav::ns tests (`testsuites/nav/navns.test`): the n/ n// n/// navigation state machine (ns/ transitions absolute/relative/glob-no-nav, failed-nav state preservation, quad-colon normalization, v-form content selection, ensemble annotation) and the ::punk::nav::ns::ns_current variable contract the repl/codethread/subshell seeding all consume; display content is covered in punk/ns nslist.test
- `punk/repl/` — punk::repl tests (`testsuites/repl/`): opunk console backend integration (`consolebackends.test`) and repl current-namespace retention (`nscurrent.test`: real codethread via repl::init driven by synchronous runscript sends - inscope evaluation of ns_current, retention across submissions, n/-navigation retained, auto-create-with-notice for missing namespaces, the 2026-07-14 stray-namespace seeding fix pinned behaviourally plus a source-text guard on repl::start's inline template; the end-to-end piped subshell session is covered at shell level by shell/testsuites/punkexe/shellnavns.test - which found the first-subshell shared-code-interp asymmetry and the piped-inscope gap recorded there)
- `punk/ns/` — punk::ns tests (`testsuites/ns/`): cmdwhich/cmdinfo/cmd_traverse doc-lookup flow (`cmdflow.test`, G-040 parity), n/ display machinery characterization (`nslist.test`: tier A get_ns_dicts classification buckets as the machine contract for display reworks - incl package tail/prefix derivation, alias edge cases, usageinfo scan-dependence; tier B per-element layout-agnostic marking - underline/underdouble/underdotted namespace package styles, command type tag colours, exported/imported markers, the punkargs doc icon; tier C REWORK-flagged pins of the current hardcoded 2-col/4-col layout and nspath subtables, to flip deliberately with the planned punk-tables/width-responsive rework; plus the flipped nslist_types_default pin - bare nslist without -types displays all member types since punk::ns 0.7.1 fixed the braced-literal -types default), corp proc-retrieval and syntax/untabify interplay (`corp.test`: name edge cases, -ranges/-n line handling, basic-highlight ansistrip equivalence, -untabify spaces/unicode tab-free output, the KNOWN-DEFICIENCY pin for default -untabify none on tabbed bodies - grepstr warns per pass and brace overlays mangle tabbed lines, deterministic under mocked console tabstops - and a ::tcl::CopyDirectory -untabify spaces smoke test; precursor coverage for the planned punk::ns hygiene pass), cmdtrace characterization (`cmdtrace.test`: -pause 0 non-interactive runs, linedict line-mark keys for flat and 2-word-form nested switches as correct-mark guards, and GAP pins for the upstream nested-switch mismark - core.tcl-lang.org tktview 5d5b1052280c976ea3d4, arm bodies whose split-list index lands on a literal switch-command word report container-relative lines; mark tests gated on have_tclcoredocs because cmdtrace's arm-offset correction parses against the ::switch argdoc; plus the fixed-canary asserting punk::lib::check::has_tclbug_nestedswitch_tracelines still reports the bug - a live behavioural probe, so a fixed Tcl release fails the canary first and triggers the documented flip workflow), cmdhelp usage-rendering integration (`cmdhelp.test`: scheme selection, goodarg/badarg marking incl type/allocation failures, goodchoice highlighting of supplied/default choice words, alias path, cmdinfo result shape, queried-command failure attribution, and `-return dict` parse-status returns (G-049 - its GAP pins flipped 2026-07-10); remaining GAP pins for pseudo-command cmdtype + space-form docid prefixes (G-051, real `string is` pins behind the have_tclcoredocs constraint), TclOO undocumented-method fallback (G-052), and synopsis marking absence (G-050)), and name/path primitive characterization (`nsprimitives.test`: string pins for nsparts/nsprefix/nstail/nsjoin/nsjoinall incl weird colon-run (`:::`) splitting, the trailing-colon parse ambiguity (`::x:` + `y` joins to the same string as `::x` + `:y` and reparses leading-colon-greedy), and prefix/tail/join round-trip and its absolutizing exceptions (the original twin-divergence pins for nsparts1/nsprefix1/nsprefix_orig/nstail1/nstail_orig served as safe-deletion evidence and were removed with the twins in the punk::ns 0.7.0 hygiene pass - divergence record in this file's git history, commit 0c7168a1); plus nseval fq-requirement/create-on-eval/evaluator-proc caching, the native-vs-punk `p:::x` resolution divergence (native namespace eval reaches child `x`, nseval creates/reaches literal `:x`), nseval_ifexists no-create + error propagation on plain and genuinely weird namespaces, nsexists/nschildren/nstree_raw weird-ns and relative-resolution pins, globmatchns `*`/`**`/`?` semantics (incl `*` matching a single inner colon - a formerly stale 'should be fixed' comment above nsglob_as_re was corrected in the 0.7.0 hygiene pass), and nspath_to_absolute/nspath_here_absolute caller-resolution pins; the nsjoinall error-message wart pin ('nsjoin:' prefix) flipped when punk::ns 0.7.1 fixed it)
- `punk/ns/` — punk::ns tests (`testsuites/ns/`): cmdwhich/cmdinfo/cmd_traverse doc-lookup flow (`cmdflow.test`, G-040 parity), n/ display machinery characterization (`nslist.test`: tier A get_ns_dicts classification buckets as the machine contract for display reworks - incl package tail/prefix derivation, alias edge cases, usageinfo scan-dependence; tier B per-element layout-agnostic marking - underline/underdouble/underdotted namespace package styles, command type tag colours, exported/imported markers, the punkargs doc icon; tier C REWORK-flagged pins of the current hardcoded 2-col/4-col layout and nspath subtables, to flip deliberately with the planned punk-tables/width-responsive rework; plus the flipped nslist_types_default pin - bare nslist without -types displays all member types since punk::ns 0.7.1 fixed the braced-literal -types default), corp proc-retrieval and syntax/untabify interplay (`corp.test`: name edge cases, -ranges/-n line handling, basic-highlight ansistrip equivalence, -untabify spaces/unicode tab-free output, the KNOWN-DEFICIENCY pin for default -untabify none on tabbed bodies - grepstr warns per pass and brace overlays mangle tabbed lines, deterministic under mocked console tabstops - and a ::tcl::CopyDirectory -untabify spaces smoke test; precursor coverage for the planned punk::ns hygiene pass), cmdtrace characterization (`cmdtrace.test`: -pause 0 non-interactive runs, linedict line-mark keys for flat and 2-word-form nested switches as correct-mark guards, and GAP pins for the upstream nested-switch mismark - core.tcl-lang.org tktview 5d5b1052280c976ea3d4, arm bodies whose split-list index lands on a literal switch-command word report container-relative lines; mark tests gated on have_tclcoredocs because cmdtrace's arm-offset correction parses against the ::switch argdoc; plus the fixed-canary asserting punk::lib::check::has_tclbug_nestedswitch_tracelines still reports the bug - a live behavioural probe, so a fixed Tcl release fails the canary first and triggers the documented flip workflow), cmdhelp usage-rendering integration (`cmdhelp.test`: scheme selection, goodarg/badarg marking incl type/allocation failures, goodchoice highlighting of supplied/default choice words, alias path, cmdinfo result shape, queried-command failure attribution, and `-return dict` parse-status returns (G-049 - its GAP pins flipped 2026-07-10); remaining GAP pins for pseudo-command cmdtype + space-form docid prefixes (G-051, real `string is` pins behind the have_tclcoredocs constraint), TclOO undocumented-method fallback (G-052), and synopsis marking absence (G-050); plus the G-150 flag-led form-narrowing + selection-soundness pins against punk::auto_exec::hash - bare-parse selection contract (incl the dash-led-name noformmatch cost), no-word/unknown-flag/ambiguity whole-render fallbacks, and hash runtime-behaviour-unchanged), and name/path primitive characterization (`nsprimitives.test`: string pins for nsparts/nsprefix/nstail/nsjoin/nsjoinall incl weird colon-run (`:::`) splitting, the trailing-colon parse ambiguity (`::x:` + `y` joins to the same string as `::x` + `:y` and reparses leading-colon-greedy), and prefix/tail/join round-trip and its absolutizing exceptions (the original twin-divergence pins for nsparts1/nsprefix1/nsprefix_orig/nstail1/nstail_orig served as safe-deletion evidence and were removed with the twins in the punk::ns 0.7.0 hygiene pass - divergence record in this file's git history, commit 0c7168a1); plus nseval fq-requirement/create-on-eval/evaluator-proc caching, the native-vs-punk `p:::x` resolution divergence (native namespace eval reaches child `x`, nseval creates/reaches literal `:x`), nseval_ifexists no-create + error propagation on plain and genuinely weird namespaces, nsexists/nschildren/nstree_raw weird-ns and relative-resolution pins, globmatchns `*`/`**`/`?` semantics (incl `*` matching a single inner colon - a formerly stale 'should be fixed' comment above nsglob_as_re was corrected in the 0.7.0 hygiene pass), and nspath_to_absolute/nspath_here_absolute caller-resolution pins; the nsjoinall error-message wart pin ('nsjoin:' prefix) flipped when punk::ns 0.7.1 fixed it)
- `punk/mix/` — punk::mix::cli tests (prune helpers, punkcheck virtual sources), punk::mix::commandset::repo fossil move/rename characterization tests (`testsuites/repo/`, FOSSIL_HOME-isolated; GAP-marked tests pin behaviour G-022 will change), punk::mix::commandset::loadedlib tests (`testsuites/loadedlib/libsearch.test`: 'dev lib.search' match semantics via -return list — wrap-glob default, =exact prefix, case rules, explicit globs, version aggregation — plus the loadedlib 0.2.0 contract: deep discovery by default (deep .tm modules found without -refresh, registration persists), -refresh = genuine re-scan (epoch incr + rediscovery picks up .tm files added to already-scanned dirs), and highlight working without the shell-global a+ alias; shared provisioned child interp sourcing the source-tree libunknown directly — see the file's ORDERING NOTE), and the MULTISHELL polyglot build machinery (`testsuites/scriptwrap/`, split 2026-07-19 per G-092 so no single file dominates the -jobs parallel floor - tests moved verbatim: `multishell.test` = scriptset wrap via the punk.multishell.cmd template with structure/LF-only pins plus platform-gated execution smoke (cmd.exe→powershell payload on windows, sh payload on unix or via the `wsllinux` capability constraint from `src/tests/testsupport/wslprobe.tcl` - staged to the WSL distro's native filesystem, G-059); `multishell_wrapverify.test` = checkfile 512-byte label validation of a fresh wrap; `multishell_wrapdeterminism.test` = byte-identical re-wrap pin; `runtimecmd_checkfile.test` = checkfile + LF contract of the committed bin/runtime.cmd; `runtimecmd_roundtrip.test` = the runtime scriptset round-trip byte-identity pin)
- `punk/lib/` — punk::lib tests (`testsuites/lib/`): range/index/parse/compat/interp_sync utilities, G-058 static-baseline seeding (`staticseed.test`: interp_sync_package_paths/snapshot_package_paths propagate a simulated ::punkboot static baseline and seed `load {} <prefix>` ifneeded mappings; no-op without a baseline), and the repl command-completeness engine (`commandcomplete.test`: punk::lib::system::incomplete pending-opener stacks - the info-complete quoting quirk progression (`set x "{*}{"` standalone vs in-proc-body), single openers, tabs, escapes, incomplete<->info-complete parity property; pre-repl-refactor characterization, see goals/G-044 detail preserve-list)
- `punk/packagepreference/` — punk::packagepreference tests (`testsuites/packagepreference/`): G-058 static-vs-bundled policy (`staticpolicy.test`: require of a baseline package triggers the index scan before resolution so a newer bundled copy wins, static beats older bundled, exact requires of bundled versions stay reachable, missing static mappings get seeded), and the install re-install guard (`installguard.test`, G-160: second install returns 0 silently via the record `renamer` key - the historical `rename` read threw; uninstall/re-install cycle gets the next unique tokenid; fresh child interps source the SOURCE-TREE commandstack + packagepreference by path so no bootsupport/kit snapshot can answer)
#are pinned in src/tests/modules/punk/args/testsuites/args/usagemarking.test - this file
#pins that cmdhelp drives them correctly.
#G-150 (2026-08-04): flag-led form narrowing on the selection-sound punk::auto_exec::hash
#declaration (its show_or_set name value carries -regexprefail {^-}), the bare-parse
#selection contract, the whole-render fallbacks (no words / unknown flag / ambiguity) and
#hash's unchanged runtime behaviour. The GAP pin
#cmdhelp_GAP_multiform_flagled_testform_false_notviable (the valmin-reservation false
#negative in the G-152 viability verdict) is owned by G-164 (proposed).
#
#Assertion approach (as usagemarking.test): expected SGR styles are read from the live
#colour arrays and matched by SGR parameter subset - renderers may merge/reorder codes.
@ -781,5 +788,248 @@ namespace eval ::testspace {
-cleanup {
}\
-result [list 1 1 0]
#--- G-150: flag-led form narrowing on a selection-sound definition ----------------------
#added 2026-08-04 (agent, G-150) - auto_exec::hash's show_or_set name value carries
#-regexprefail {^-}, so a bare punk::args parse of a flag-led line selects the flag form
#uniquely (previously multipleformmatches show_or_set+<flag form>) and cmdhelp's G-041
#candidacy narrows the render to that form with no hash-handler involvement; no-word,
#unknown-flag and ambiguous lines keep the whole-command render, and hash's runtime
#behaviour is unchanged (a dash-led name now needs an explicit flag form - accepted cost).
test cmdhelp_multiform_flagled_narrowing_rehash {a flag-led line narrows the usage display to the flag's form (definition-driven, no handler involvement)}\
-setup $common -body {
set d [punk::ns::cmdhelp -return dict ::punk::auto_exec::hash -r]
lappend result [dict get $d parsestatus ok] [dict get $d parsestatus form]
set out [punk::ansi::ansistrip [punk::ns::cmdhelp -return string ::punk::auto_exec::hash -r]]
lappend result [string match {*Clear autoexec commands*} $out]
lappend result [string match {*One or more autoexec command names to set*} $out]
lappend result [string match {*Ambiguous*} $out]
set txt [punk::ns::cmdhelp -return text ::punk::auto_exec::hash -r]
lappend result [string match {*--ARGUMENTS (form 'rehash')--*} $txt]
lappend result [string match {*--ARGUMENTS (form 'show_or_set')--*} $txt]
}\
-cleanup {
}\
-result [list 1 rehash 1 0 0 1 0]
test cmdhelp_multiform_flagled_narrowing_delete {a flag-led line with a name value narrows to the delete form only}\
-setup $common -body {
set d [punk::ns::cmdhelp -return dict ::punk::auto_exec::hash -d foo]
lappend result [dict get $d parsestatus ok] [dict get $d parsestatus form]
set txt [punk::ns::cmdhelp -return text ::punk::auto_exec::hash -d foo]
lappend result [string match {*--ARGUMENTS (form 'delete')--*} $txt]
lappend result [string match {*--ARGUMENTS (form 'show_or_set')--*} $txt]
set out [punk::ansi::ansistrip [punk::ns::cmdhelp -return string ::punk::auto_exec::hash -d foo]]
lappend result [string match {*Delete specified autoexec commands*} $out]
lappend result [string match {*One or more autoexec command names to set*} $out]
}\
-cleanup {
}\
-result [list 1 delete 1 0 1 0]
test auto_exec_hash_bare_parse_selects_flag_forms {bare punk::args::parse withid selects the flag-led forms uniquely (definition-driven, no hash-handler involvement)}\
-setup $common -body {
lappend result [dict get [punk::args::parse {-r} withid ::punk::auto_exec::hash] form]
lappend result [dict get [punk::args::parse {-d foo} withid ::punk::auto_exec::hash] form]
lappend result [dict get [punk::args::parse {-t foo} withid ::punk::auto_exec::hash] form]
lappend result [dict get [punk::args::parse {plainname} withid ::punk::auto_exec::hash] form]
lappend result [dict get [punk::args::parse {} withid ::punk::auto_exec::hash] form]
}\
-cleanup {
}\
-result [list rehash delete test show_or_set show_or_set]
test auto_exec_hash_dashled_name_no_longer_plain {a dash-led word no longer seats as a plain show_or_set name - bare parse reports noformmatch with the show_or_set failure classed regexprefail (the accepted cost: such names need an explicit flag form)}\
lappend result [string match {*show_or_set*regexprefail*} $ec]
}\
-cleanup {
}\
-result [list 1 1]
test cmdhelp_multiform_nowords_whole_render_retained {a no-word invocation keeps the whole-command render: every form's synopsis line and no failure or ambiguity message}\
-setup $common -body {
set d [punk::ns::cmdhelp -return dict ::punk::auto_exec::hash]
lappend result [dict get $d parsestatus ok] [dict get $d parsestatus form]
set out [punk::ansi::ansistrip [punk::ns::cmdhelp -return string ::punk::auto_exec::hash]]
lappend result [string match {*hash -r*} $out]
lappend result [string match {*hash -t *} $out]
lappend result [string match {*hash -d *} $out]
lappend result [string match {*hash --help *} $out]
lappend result [string match {*hash \[name\]*} $out]
lappend result [string match {*Ambiguous*} $out]
lappend result [string match {*No form of the command matches*} $out]
}\
-cleanup {
}\
-result [list 1 show_or_set 1 1 1 1 1 0 0]
test cmdhelp_multiform_unknown_flag_noformmatch_whole_render {an unknown flag-led line keeps the whole-command render with the noformmatch diagnosis (the constraint stopped it seating as a plain name)}\
-setup $common -body {
set d [punk::ns::cmdhelp -return dict ::punk::auto_exec::hash -bogus]
lappend result [dict get $d parsestatus ok] [dict get $d parsestatus failureclass]
set out [punk::ansi::ansistrip [punk::ns::cmdhelp -return string ::punk::auto_exec::hash -bogus]]
lappend result [string match {*No form of the command matches*} $out]
lappend result [string match {*Ambiguous*} $out]
}\
-cleanup {
}\
-result [list 0 noformmatch 1 0]
#ambiguous-by-design multiform fixture: {-x} fully matches both forms
test cmdhelp_multiform_ambiguity_whole_render {a line matching more than one form keeps the whole-command render with the ambiguity named (multipleformmatches route)}\
-setup $common -body {
set d [punk::ns::cmdhelp -return dict ::testspace::ambfix -x]
lappend result [dict get $d parsestatus ok] [dict get $d parsestatus failureclass]
lappend result [lsort [dict keys [dict get $d parsestatus formstatus]]]
set out [punk::ansi::ansistrip [punk::ns::cmdhelp -return string ::testspace::ambfix -x]]
lappend result [string match {*Ambiguous*} $out]
lappend result [expr {[string match {*flagged*} $out] && [string match {*plain*} $out]}]
test auto_exec_hash_runtime_behaviour_unchanged {hash runtime behaviour is unchanged by the declaration constraint: bare show, -t single and multi, -d, -r, and the dash-led invalid-option error}\
-setup {
set result ""
#reseed the process-global hash table with deterministic entries (saved and
#restored - the testinterp caches real lookups in ::auto_execs as it runs).
#NOTE: qualify ::auto_execs explicitly - this script runs at namespace level
#in ::testspace where 'global' is a no-op.
set saved_auto_execs [array get ::auto_execs]
unset -nocomplain ::auto_execs
array set ::auto_execs [list foo /bin/foo bar /bin/bar neg {}]
}\
-body {
lappend result [lsort [split [punk::auto_exec::hash] \n]]
lappend result [punk::auto_exec::hash -t foo]
lappend result [punk::auto_exec::hash -t foo bar]
punk::auto_exec::hash -d foo
lappend result [info exists ::auto_execs(foo)] [info exists ::auto_execs(bar)]
punk::auto_exec::hash -r
lappend result [array exists ::auto_execs]
#a dash-led first word still hits the handler's invalid-option dispatch (also
#emits the expected one-line stderr note 'hash: -bogus: invalid option') - the
#show_or_set constraint did not move the runtime behaviour
catch {punk::auto_exec::hash -bogus} msg
lappend result [string match {hash: usage:*} $msg]
#from the help form, --help must lead, trailing words consumed as ignored values; a
#flag-led line no longer marks the help form viable (was the only "(viable" form for
#'i hash -t' via the adhoc-option seating); plus the regression pin for the user-landed
#fix of the handler's multi-name -t missing-name message (was: can't read "hash").
test cmdhelp_multiform_help_form_narrowing {--help leads and selects the help form uniquely; trailing words are consumed as ignored values and the arbitrary-flag doc row is gone}\
-setup $common -body {
set d [punk::ns::cmdhelp -return dict ::punk::auto_exec::hash --help foo bar]
lappend result [dict get $d parsestatus ok] [dict get $d parsestatus form]
set txt [punk::ns::cmdhelp -return text ::punk::auto_exec::hash --help]
lappend result [string match {*--ARGUMENTS (form 'help')--*} $txt]
lappend result [string match {*--ARGUMENTS (form 'show_or_set')--*} $txt]
lappend result [string match {*<arbitrary-flag>*} $txt]
}\
-cleanup {
}\
-result [list 1 help 1 0 0]
#added 2026-08-04 (agent, G-150) - the help form's @values -flagvalues 1 (punk::args
#0.15.0) makes dash-led trailing words after --help seat as ignored values
test cmdhelp_multiform_help_form_dashwords_swallowed {dash-led trailing words after --help are consumed as ignored values via @values -flagvalues (no invalidoption)}\
-setup $common -body {
set d [punk::ns::cmdhelp -return dict ::punk::auto_exec::hash --help -t foo]
lappend result [dict get $d parsestatus ok] [dict get $d parsestatus form]
set txt [punk::ns::cmdhelp -return text ::punk::auto_exec::hash --help -t foo]
lappend result [string match {*--ARGUMENTS (form 'help')--*} $txt]
lappend result [string match {*--ARGUMENTS (form 'show_or_set')--*} $txt]
}\
-cleanup {
}\
-result [list 1 help 1 0]
test cmdhelp_multiform_flagled_line_help_not_viable {a flag-led incomplete line no longer marks the help form viable once it stops accepting adhoc options (the old adhoc-viable pointer was wrong); show_or_set/rehash/delete are genuinely not viable}\
-setup $common -body {
set d [punk::ns::cmdhelp -return dict ::punk::auto_exec::hash -t]
lappend result [dict get $d parsestatus ok] [dict get $d parsestatus failureclass]
set out [punk::ansi::ansistrip [punk::ns::cmdhelp -return string ::punk::auto_exec::hash -t]]
lappend result [string match {*No form of the command matches*} $out]
foreach f {show_or_set rehash delete help} {
lappend result [string match "*form '$f': (not viable)*" $out]
}
set result
}\
-cleanup {
}\
-result [list 0 noformmatch 1 1 1 1 1]
#added 2026-08-04 (agent, G-150) - GAP pin of a KNOWN FALSE NEGATIVE in the G-152
#viability verdict: for 'i hash -t' the test form renders "(not viable)" although
#{-t} has a completing suffix ({-t foo} parses as test, pinned below). Mechanism:
#the valmin reservation seats the lone -t as the required name value (never compared
#to the declared -t option), after which the G-152 position guard (missing-required-
#option suppressed only with no value words consumed) lets optionmissing stand.
#Not one of G-152's documented conservatisms. Desired end state: an alternative-
#allocation re-probe in the probe layer (stolen word(s) literally matching the
#missing declared option -> re-probe with them consumed as options, grant
#incomplete on pure exhaustion) - NOT a runtime reservation change, which would
#flip real parses (flagvalues_plain_then_dash pins the trailing-flag-as-value rule).
test cmdhelp_GAP_multiform_flagled_testform_false_notviable {KNOWN FALSE NEGATIVE: the test form renders "(not viable)" for a flag-led incomplete line that has a completing suffix (valmin reservation steals the flag word into the required value slot)}\
-setup $common -body {
#the completing suffix exists: {-t foo} selects the test form
lappend result [dict get [punk::args::parse {-t foo} withid ::punk::auto_exec::hash] form]
#...yet the bare prefix renders "(not viable)" today (the pinned-wrong state)
set out [punk::ansi::ansistrip [punk::ns::cmdhelp -return string ::punk::auto_exec::hash -t]]
lappend result [string match {*form 'test': (not viable)*} $out]
lappend result [string match {*form 'test': (viable -*} $out]
}\
-cleanup {
}\
-result [list test 1 0]
test auto_exec_hash_test_multi_missing_name_message {a multi-name -t with a missing name returns the found entries and does not error (regression pin for the fixed handler message; emits the expected one-line stderr note 'hash: missingname: not found')}\
-setup {
set result ""
#deterministic reseed of the process-global hash table (see the
#runtime_behaviour_unchanged test for the ::auto_execs qualification note)
set saved_auto_execs [array get ::auto_execs]
unset -nocomplain ::auto_execs
array set ::auto_execs [list foo /bin/foo]
}\
-body {
lappend result [punk::auto_exec::hash -t foo missingname]
lappend result [info exists ::auto_execs(foo)]
set result
}\
-cleanup {
unset -nocomplain ::auto_execs
array set ::auto_execs $saved_auto_execs
}\
-result [list {foo /bin/foo} 1]
}
tcltest::cleanupTests ;#needed to produce test summary line.