Acceptance: 'i ::punk::auto_exec::hash -t' classes the test form viable (incomplete) while show_or_set/rehash/delete/help remain not viable, and {-d} classes delete viable; no parse RESULT changes anywhere (runtime seating untouched): full args and ns subtrees green apart from the deliberate GAP flip, with flagvalues_plain_then_dash (trailing-flag-as-value) and the runtime 'missing flags' message path pinned unchanged; the GAP pin cmdhelp_GAP_multiform_flagled_testform_false_notviable flips to the viable assertion.
Acceptance: 'i ::punk::auto_exec::hash -r' renders only the rehash form's usage and 'i ::punk::auto_exec::hash -d <name>' only the delete form, driven by the definition (no hash-handler involvement); hash's plain name value carries a flag-excluding constraint (-regexprefail {^-} or equivalent) such that bare punk::args::parse withid selects flag-led forms uniquely, with hash's runtime behaviour otherwise unchanged (a name literally starting with '-' now requires explicit -form selection - the accepted cost); ensembles, single-form commands and no-word invocations render as today (existing cmdhelp behaviour pinned unchanged); the behaviours are pinned in the module testsuites.
@ -93,7 +93,7 @@ arginfo principle: introspection must not run commands to elicit usage).
-parameters handling; the choice semantics by
src/tests/modules/punk/args/testsuites/args/choices.test; forms by forms.test.
- G-152 (achieved 2026-08-02 - goals/archive/G-152-punkargs-form-viability-verdict.md) shipped this goal's core verdict enabler: per-form suffix-viability verdicts ('incomplete' = the form is still satisfiable by appending words, sound by construction) in the noformmatch errorcode classes/-formerrors and the parse_status formstatus records, with the consumer contract documented in the parse/parse_status argdocs. Its Notes record two things a completion consumer may need to extend: the trailing-flag-before-restricted-choice seating nuance (verdict is seating-based; flag-pending-value awareness needs consumer lookahead) and the multi-member-clause conservatism (input ending inside a clause reports invalid).
- G-164 (proposed 2026-08-04) is the probe-layer answer to the G-152 note's "consumer lookahead" for one class: the valmin-reservation false negative (a required-flag word stolen into a required value slot at end-of-input - e.g 'i hash -t', where optionmissing stands and the test form classes not viable although the completing suffix 'hash -t foo' exists). G-164 re-probes with the stolen word consumed as the option it literally names, granting viable-incomplete on pure exhaustion; runtime seating stays unchanged. Current state documented by the 0.51.0 CHANGELOG known-issue text and the GAP pin cmdhelp_GAP_multiform_flagled_testform_false_notviable (flips under G-164).
- G-164 (achieved 2026-08-04 - goals/archive/G-164-viability-alternative-allocation.md) was the probe-layer answer to the G-152 note's "consumer lookahead" for one class: the valmin-reservation false negative (a required-flag word stolen into a required value slot at end-of-input - e.g 'i hash -t', where optionmissing stood and the test form classed not viable although the completing suffix 'hash -t foo' exists). The probe now re-parses with the stolen word consumed as the option it literally names, granting viable-incomplete on pure exhaustion; runtime seating is unchanged. G-164 recorded the value-TAKING required option conservatism as an open follow-on (a completing suffix exists there too - candidate refinement if this goal's completion consumer needs it) - see goals/archive/G-164-viability-alternative-allocation.md.
- Archived-goal references in this file: G-001 achieved 2026-07-11 (goals/archive/G-001-pluggable-console-backends.md); G-040 achieved 2026-07-08 (goals/archive/G-040-punkargs-choicealiases.md); G-041 achieved 2026-07-13 (goals/archive/G-041-punkargs-form-matching.md).
Goal: When the valmin reservation seats a word that literally names a declared required option into a required value slot, and the resulting optionmissing stands in the viability probe (a value word consumed), the probe re-parses with the stolen word(s) consumed as the option they name; pure end-of-input exhaustion after that re-seating grants 'incomplete' (viable). Runtime seating is unchanged - the trailing-flag-as-value rule is load-bearing.
Acceptance: 'i ::punk::auto_exec::hash -t' classes the test form viable (incomplete) while show_or_set/rehash/delete/help remain not viable, and {-d} classes delete viable; no parse RESULT changes anywhere (runtime seating untouched): full args and ns subtrees green apart from the deliberate GAP flip, with flagvalues_plain_then_dash (trailing-flag-as-value) and the runtime 'missing flags' message path pinned unchanged; the GAP pin cmdhelp_GAP_multiform_flagled_testform_false_notviable flips to the viable assertion.
@ -15,3 +15,13 @@ Surfaced by the G-150 batch (2026-08-04, punkshell 0.51.0): removing the help fo
- Related: G-152 (achieved 2026-08-02 - goals/archive/G-152-punkargs-form-viability-verdict.md) - the suffix-viability verdict layer this refines; its 'deliberately seating-based' note already records that a completion consumer wanting flag-pending-value awareness needs its own lookahead - this goal is that lookahead, at the probe layer.
- Related: G-150 (achieved 2026-08-04 - goals/archive/G-150-cmdhelp-form-narrowing.md) - the batch that surfaced the false negative; its cmdhelp.test GAP pin (cmdhelp_GAP_multiform_flagled_testform_false_notviable) flips here.
- Overlap survey 2026-08-04: goals_xref paths src/modules/punk/args-999999.0a1.0.tm src/tests/modules/punk/args/testsuites/args/formviability.test src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test - the args family (G-044 G-050 G-053 G-056 G-069 G-072 G-073 G-075 G-083 G-084 G-088 G-149 G-151) via the ubiquitous args-module anchor and the ns family (G-050 G-052 G-075 G-085) via the shared cmdhelp.test anchor; G-044 named above as the consumer, the rest are unrelated parse/render mechanisms. G-152/G-150 (archived) named above.
- Activation freshness re-run 2026-08-04 (goals_xref score G-164, at user-directed activation same day as drafting): no new related goals - the one-directional args/ns-family pairs are the goals already surveyed above; the unlinked pairs (G-158/G-110/G-055/G-019/G-028 et al) are term noise on runtime/load/plain/required. No new Related lines required.
## Progress
- 2026-08-04 IMPLEMENTED (punk::args 0.15.1, punkshell 0.51.1): the alternative-allocation re-probe landed in private::get_dict_form. The valmin reservation's option-scan seating site now records the words it seats without an option check (reservation_seated); at the optionmissing raise in probe mode, when reservation-seated words literally name missing required options (exact match against the option's declared spellings), the probe re-parses via a {1 reseat_words} viabilitycheck argument with those words consumed as the options they name (the reservation break is bypassed for exactly those words); a re-seated parse failing only on pure end-of-input exhaustion (or clean) passes the probe. Options still missing after the re-seating are ordinary exhaustion in the re-probe (no value words precede them), so {-t} on a two-required-flag form is viable too. A stolen word naming a value-TAKING required option stays conservative: re-seated, it seats as a value again at end-of-input (the trailing-flag-as-value rule) and the optionmissing stands. Runtime seating untouched - the re-probe runs only inside the probe. Verdict contract documentation updated in the parse argdoc 'Multiform failure contract' and the parse_status argdoc formstatus section.
- 2026-08-04 VERIFIED (native tclsh 8.7a6 via runtests.tcl): acceptance pinned - formviability.test +7 tests (17/17: re-probe grants viable ranked-first with siblings invalid, delete-form {-d} viable, other-missing-options exhaustion, unnamed stolen word stays invalid, value-taking conservatism, runtime seating unchanged (optionmissing single-form / noformmatch multiform), single-form formstatus); the cmdhelp.test GAP pin flipped to cmdhelp_multiform_flagled_testform_viable (45/45). 'i ::punk::auto_exec::hash -t' verified live rendering "form 'test': (viable - needs more arguments)" with show_or_set/rehash/delete/help "(not viable)"; 'i hash -d' renders delete viable. args + ns subtrees green apart from the 2 pre-existing nsprimitives environment failures on this box; full source-tree -jobs 16 run at parity with the recorded pre-existing environment failures (exec.test 5, argparsingtest 5, commandstack 1, modpod roundtrip 1, lib compat lpop 1, nsprimitives 2, repl consolebackends/nscurrent SIGILL 4, zipreader 2, offsetstyle 1 - none args-related; flagvalues_plain_then_dash and the runtime optionmissing message path pinned unchanged). make.tcl modules mint clean (args-0.15.1.tm, args-0.15.0.tm pruned); bootsupport/vfscommon promotion left to the next promotion cycle (per the 0.50.1 precedent).
## Follow-ons
Follow-on: a stolen word naming a value-TAKING required option stays conservative (invalid) - re-seated, it seats as a value again at end-of-input (the trailing-flag-as-value rule) although a completing suffix exists (e.g {-dest x foo}); a further probe layer could re-seat with the option's own value also supplied - candidate refinement if the G-044 completion consumer needs it => open
#0.15.1 - G-164 viability-probe alternative-allocation re-probe: when the valmin reservation seats a word that literally names a declared required option into a required value slot at end-of-input, and the resulting optionmissing stands in the G-152 viability probe (a value word consumed, defeating the position guard), the probe now re-parses with the stolen word(s) consumed as the options they name (private::get_dict_form's viabilitycheck argument carries {1 reseat_words} on the re-probe pass) and grants 'incomplete' (viable) on pure end-of-input exhaustion after that re-seating. Cures the 'i hash -t' false negative: the test form classes viable since {-t foo} parses; sibling forms stay invalid. Options still missing after the re-seating are ordinary exhaustion in the re-probe. Runtime seating is unchanged - the re-probe runs only inside the probe, only via reservation-seated words (the trailing-flag-as-value rule is load-bearing; flagvalues_plain_then_dash and the runtime optionmissing path pinned unchanged). A stolen word naming a value-TAKING required option stays conservative (invalid) - re-seated, it seats as a value again at end-of-input. Verdict contract documentation updated in the parse/parse_status argdoc. formviability.test gains 7 pins (re-probe, delete-form, other-missing-options exhaustion, unnamed stolen word, value-taking conservatism, runtime-unchanged, single-form formstatus); the cmdhelp.test GAP pin cmdhelp_GAP_multiform_flagled_testform_false_notviable flips to cmdhelp_multiform_flagled_testform_viable.
#0.15.0 - @values -flagvalues (G-150 follow-on, user-directed): a @values section may declare -flagvalues 1 - an unmatched dash-led word in option position is then consumed as a trailing value instead of raising invalidoption (declared options still match first; the '--' end-of-options marker is unaffected; off by default). Motivation: '--help'-style forms robustly swallowing ignored trailing words of any shape - previously such a word seated as a value only positionally (at end-of-input via the valmin reservation, or within a bounded valmax) and errored 'Unexpected option' otherwise ('{--help -t foo}' on the unconstrained shape). Mechanism: VAL_FLAGVALUES on the form dict (template default false; @values compiler key, boolean-validated at resolve time) short-circuits the unmatched-dash-word fallback in the parse engine. In-tree consumer: punk::auto_exec::hash's help form. New testsuite flagvalues.test (9 tests: seating matrix, declared-option precedence, off-by-default contrast, optionmissing after value seating, key validation). Documented in the module header notes.
#0.14.0 - G-152 suffix-viability verdict: multiform per-form failure statuses (the 'formstatus' records, the noformmatch errorcode's 'classes' list and its -formerrors records) are now SOUND suffix-viability verdicts - a form reports 'incomplete' (viable) if and only if every supplied word was validly consumed as a prefix of that form and the failure is pure exhaustion at end-of-input; anything already supplied contradicting the form reports 'invalid'. Previously the count/missing-required failure classes granted 'incomplete' before final validation ever saw the consumed words, so a wrong action word landed 'incomplete' on a value-carrying sibling form identically to a genuinely-viable prefix ({frobnicate} vs {info} were indistinguishable), and a word rejected by an allocation type screen (missingrequiredvalue) also classed 'incomplete'. Mechanism: private::get_dict_form gains a viability-probe mode (optional 4th arg) suppressing exactly the pure-exhaustion raises - below-minimum leading/trailing count shortfalls and missing-required leader/option/value checks, each position-guarded so appended words could actually reach the missing element (leaders only with no option/value words consumed; options only before value words and any eopts --; values always), plus an adhoc option's value lying beyond the supplied words - while every contradiction raise stands, including final validation of the consumed words (the soundness backstop). Multiform candidacy and parse_status's single-form formstatus record confirm each classify-'incomplete' failure with one probe re-parse before reporting it. Documented conservatism: input ending INSIDE a multi-member type clause reports invalid (clause allocation cannot affirm the partial words). Rendered noformmatch messages mark each form line '(viable - needs more arguments)' or '(not viable)' after the stable "form '<fid>':" anchor; the existing incomplete-first ranking thereby puts still-satisfiable forms first. Contract documented in the parse ('Multiform failure contract' help section) and parse_status (formstatus/status key) argdocs; parse_status's top-level 'status' for single-form parses keeps the engine classification (display-scheme driver) - only formstatus carries the verdict. New testsuite formviability.test (acceptance scenarios, option-position guards, single-form record soundness, clause conservatism, message marking); forms.test verdict pins deliberately updated (ms/idle 'incomplete'->'invalid' for words failing their type screens).
#0.13.0 - G-144: auto-generated synopses for (script)-prefixed constructed ids now lead with the declared @cmd -name (the invocation name - e.g 'make.tcl modules' for (script)::punkboot::modules) instead of the raw id, across punk::args::synopsis full/summary renders and the usage/arg_error synopsis sections (table and string) that funnel through them; explicit @form -synopsis overrides still win, so make.tcl/punkexe displays are unchanged today (their override retirement is a follow-on). The fallback is gated on the (script) prefix only: (autodef) ids are deliberately excluded - their id-minus-tag IS the invocation (arg_error's existing tag strip surfaces it) while their @cmd -name is a display label ('destroy', 'Object: ::x') that must not lead a synopsis line; (shared)/(package)/(default)/(widgetcommand) doc ids declare no invocation-style -name today and render the id as before (a prefixed id with no -name falls back to the id). Real command ids render byte-identical. Retires the 'open generation deficiency' recorded at 0.12.5 (head-render preference for @cmd -name); define's @form -synopsis directive doc and synopsis -help updated. New synopsis.test pins: script-id full/summary/multiform/surfaces, no-name constructed id, autodef unchanged, real-id byte-identical.
name -type string -optional 1 -multiple 1 -regexprefail {^-}
@form -form rehash
@opts -min 1 -max 1
-r -type none -optional 0
@values -min 0 -max 0
@form -form test
@opts
-t -type none -optional 0
@values -min 1 -max -1
name -type string -optional 0 -multiple 1
@form -form delete
@opts
-d -type none -optional 0
@values -min 1 -max -1
name -type string -optional 0 -multiple 1
}
#added 2026-08-02 (agent, G-152)
test formviability_viable_form_leads {a valid literal leader alone: its form is viable (incomplete), ranked first; every sibling form is invalid}\
-setup $common -body {
@ -242,5 +277,137 @@ namespace eval ::testspace {
-cleanup {
}\
-result [list 1 1 1 1 1]
#added 2026-08-04 (agent, G-164) - the valmin reservation seats the lone -t as the
#required name value at end-of-input and the position guard lets optionmissing
#stand; the probe's alternative-allocation re-probe re-parses with the stolen word
#consumed as the option it literally names, granting viable-incomplete on pure
#exhaustion ({-t foo} parses as test). Runtime seating is unchanged - the
#re-probe runs only inside the viability probe.
test formviability_reservation_stolen_flag_reprobed {a flag word the valmin reservation stole into the required value slot is re-probed as the option it literally names: viable (incomplete), ranked first; sibling forms stay invalid}\
-result [list noformmatch test incomplete invalid]
#added 2026-08-04 (agent, G-164)
test formviability_reprobe_delete_form_viable {{-d} classes the delete form viable (incomplete) by the same re-probe; the test form stays invalid because -d names no missing option of it}\
test formviability_reprobe_other_missing_options_are_exhaustion {required options never supplied remain pure exhaustion in the re-probe - only the reservation-stolen words need naming}\
#-t stolen, -x never supplied: both satisfiable by appending ({-t -x foo})
set pstat [punk::args::parse_status {-t} withid ::testspace::vtwoflags]
lappend result [dict get $pstat formstatus go status]
set pstat [punk::args::parse_status {-t -x} withid ::testspace::vtwoflags]
lappend result [dict get $pstat formstatus go status]
#the completing suffix really does parse at runtime
lappend result [dict get [punk::args::parse {-t -x foo} withid ::testspace::vtwoflags] form]
}\
-cleanup {
punk::args::undefine ::testspace::vtwoflags 1
}\
-result [list incomplete incomplete go]
#added 2026-08-04 (agent, G-164)
test formviability_reprobe_unnamed_stolen_word_stays_invalid {a reservation-stolen word naming no missing required option grants no re-probe pass - the form is genuinely not viable}\
-setup $common -body {
set pstat [punk::args::parse_status {-z} withid ::testspace::vhash]
lappend result [dict get $pstat formstatus test status]
}\
-cleanup {
}\
-result [list invalid]
#added 2026-08-04 (agent, G-164)
test formviability_reprobe_value_taking_option_conservative {a stolen word naming a value-TAKING required option stays invalid - re-seated, it seats as a value again at end-of-input (documented conservatism)}\
set pstat [punk::args::parse_status {-dest} withid ::testspace::vdest]
lappend result [dict get $pstat formstatus go status]
}\
-cleanup {
punk::args::undefine ::testspace::vdest 1
}\
-result [list invalid]
#added 2026-08-04 (agent, G-164)
test formviability_reprobe_runtime_seating_unchanged {the re-probe is probe-only: runtime parses of the same words keep the engine's own failure classes (optionmissing single-form, noformmatch multiform)}\
-setup $common -body {
catch {punk::args::parse {-t} -form test withid ::testspace::vhash} msg opts
lappend result [lindex [lindex [dict get $opts -errorcode] 2] 0]
lappend result [lindex [lindex [dict get $opts -errorcode] 2] 0]
}\
-cleanup {
}\
-result [list optionmissing noformmatch]
#added 2026-08-04 (agent, G-164)
test formviability_reprobe_singleform_formstatus {single-form parse_status: the formstatus record gains the re-probe's viable verdict while the top-level status keeps the engine classification and the runtime parse still raises optionmissing}\
#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).
#negative in the G-152 viability verdict) flipped 2026-08-04 under G-164 to
#cmdhelp_multiform_flagled_testform_viable (the probe's alternative-allocation
#re-probe grants the test form viable-incomplete).
#
#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.
@ -989,28 +991,28 @@ bar /bin/bar} 0 1 0 1]
-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
#viability verdict: for 'i hash -t' the test form rendered "(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)}\
#Flipped 2026-08-04 (agent, G-164): the probe's alternative-allocation re-probe
#re-parses with the reservation-stolen word consumed as the option it literally
#names and grants viable-incomplete on pure end-of-input exhaustion. Runtime
#seating is unchanged (flagvalues_plain_then_dash pins the trailing-flag-as-value
#rule; flagvalues_required_option_still_missing pins the runtime optionmissing).
test cmdhelp_multiform_flagled_testform_viable {the test form renders viable for a flag-led incomplete line with a completing suffix - the G-164 alternative-allocation re-probe cures the valmin-reservation false negative}\
-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)
#...and the bare prefix now renders viable for the test form
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]
lappend result [string match {*form 'test': (viable - needs more arguments)*} $out]
}\
-cleanup {
}\
-result [list test 1 0]
-result [list test 0 1]
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')}\