Browse Source

G-082 follow-on landed: required typed value/leader misreport -> pointed typemismatch (punk::args 0.15.2)

A REQUIRED typed value or leader whose present word fails the basic-type
allocation screen (int/double/bool/number/dict) now reports the pointed
typemismatch naming the word and argument (-badarg/-badval, same shape
as the G-082 overflow-site selection) instead of the generic
missingrequiredvalue/missingrequiredleader shortfall. The in-loop
required-arg raise sites consult the rejection record returned by
get_dict_can_assign_value; the leaders-loop edit mirrors the values
loop per the MAINTENANCE contract. Unchanged by design: end-of-input
shortages (valuemissing/count classes), literal-typed members,
multi-member clause partials (no rejection record - conservatism
preserved), and required-arg choice words (never screened - G-071
asymmetry, choiceviolation already pointed).

parse_status: the shape now classifies invalid/typemismatch (the
formstatus viability verdict was already invalid; one probe re-parse
saved); argdoc status-key note updated. Pins deliberately flipped:
parsestatus.test (renamed parsestatus_typed_value_screen_typemismatch),
formviability.test (single-form top-status/formstatus split demo
re-anchored on a required-option shape), punk/ns cmdhelp.test message
match. New errorselection.test coverage: required value + leader
typemismatch pins plus unchanged-shape guards.

Verified tcl 9.0.3: punk/args subtree 280 total / 277 passed / 3
intended skips / 0 failed; punk/ns only the pre-existing nslist(1)
box-baseline failure (stash-verified unrelated). tcl 8.7: touched args
suites 38/38, cmdhelp 45/45.

Follow-on disposition flipped => landed in
goals/archive/G-082-punkargs-error-selection.md (goals_lint clean;
ledger 23 open / 20 goal / 15 landed / 2 declined). punkshell 0.53.1 +
CHANGELOG entry. Bootsupport/vfscommon args copies stay 0.15.1 until
the next promotion + bake.

Claude-Session: https://claude.ai/code/session_01QgaxV27VZkmEec7oNbEVFc
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 6 days ago
parent
commit
d2094364a3
  1. 13
      CHANGELOG.md
  2. 2
      goals/archive/G-082-punkargs-error-selection.md
  3. 2
      punkproject.toml
  4. 36
      src/modules/punk/args-999999.0a1.0.tm
  5. 3
      src/modules/punk/args-buildversion.txt
  6. 63
      src/tests/modules/punk/args/testsuites/args/errorselection.test
  7. 26
      src/tests/modules/punk/args/testsuites/args/formviability.test
  8. 7
      src/tests/modules/punk/args/testsuites/args/parsestatus.test
  9. 8
      src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test

13
CHANGELOG.md

@ -5,6 +5,19 @@ 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` Entries are newest-first; one bullet per notable change. See the root `AGENTS.md`
"Project Versioning" section for the bump policy. "Project Versioning" section for the bump policy.
## [0.53.1] - 2026-08-05
- G-082 follow-on landed (punk::args 0.15.2, source module): a REQUIRED typed
value or leader whose supplied word fails the basic-type allocation screen
(int/double/bool/number/dict) now reports the pointed typemismatch naming
the word and argument (with -badarg/-badval) instead of the generic
"Not enough remaining values" missingrequiredvalue/missingrequiredleader
shortfall. Genuine shortfalls (end-of-input, literal-typed members,
multi-member clause partials) report unchanged. parse_status for the
affected shape now classifies invalid/typemismatch (the formstatus
viability verdict was already invalid). Kits pick the fix up at the next
bootsupport/vfscommon refresh and bake.
## [0.53.0] - 2026-08-05 ## [0.53.0] - 2026-08-05
- Single help mechanism (user direction; boot core 0.5.0, moduledoc::punkexe - Single help mechanism (user direction; boot core 0.5.0, moduledoc::punkexe

2
goals/archive/G-082-punkargs-error-selection.md

@ -84,4 +84,4 @@ multiform rank_form_failures selection.
## Follow-ons ## Follow-ons
Follow-on: a REQUIRED typed value failing the basic-type screen reports missingrequiredvalue rather than a typemismatch naming the word (the rejection-record seam would cover it) => open Follow-on: a REQUIRED typed value failing the basic-type screen reports missingrequiredvalue rather than a typemismatch naming the word (the rejection-record seam would cover it) => landed 2026-08-05 (punk::args 0.15.2 - the required-arg raise sites in the values AND leaders loops consult the rejection record and report the pointed typemismatch; end-of-input/literal/clause-partial shortfalls unchanged; pins flipped in parsestatus.test/formviability.test/cmdhelp.test, new errorselection.test required-position coverage)

2
punkproject.toml

@ -1,6 +1,6 @@
[project] [project]
name = "punkshell" name = "punkshell"
version = "0.53.0" version = "0.53.1"
license = "BSD-2-Clause" license = "BSD-2-Clause"
url = "https://www.gitea1.intx.com.au/jn/punkshell" url = "https://www.gitea1.intx.com.au/jn/punkshell"
#packager: declared identity for published artifacts (declarative, not proof - #packager: declared identity for published artifacts (declarative, not proof -

36
src/modules/punk/args-999999.0a1.0.tm

@ -7724,11 +7724,15 @@ tcl::namespace::eval punk::args {
status valid | invalid | incomplete status valid | invalid | incomplete
invalid - a supplied word failed validation invalid - a supplied word failed validation
incomplete - required arguments missing (a count or incomplete - required arguments missing (a count or
allocation shortfall). Note a supplied word allocation shortfall). A supplied word
failing its -type check can also surface as failing a required argument's basic -type
an allocation shortfall (missingrequiredvalue) screen reports typemismatch (invalid) as of
- badarg and the per-argument statuses carry 0.15.2; a word leaving a multi-member
the specifics in both situations. For a clause short, or failing a literal-typed
member, still surfaces as an allocation
shortfall (missingrequiredvalue) - badarg
and the per-argument statuses carry the
specifics in both situations. For a
single-form parse this is the engine failure single-form parse this is the engine failure
classification, NOT a viability verdict - classification, NOT a viability verdict -
read formstatus for that. For a multiform read formstatus for that. For a multiform
@ -10971,6 +10975,15 @@ tcl::namespace::eval punk::args {
} else { } else {
#required named arg #required named arg
if {$leadername ni $leadernames_received} { if {$leadername ni $leadernames_received} {
#G-082 follow-on (0.15.2): mirrored from the values loop below
#(per the MAINTENANCE contract above) - a present word that
#failed this required leader's basic-type screen reports the
#pointed typemismatch instead of the generic shortfall.
if {$rj ne "" && [dict get $rj kind] eq "type" && [dict get $rj argname] eq $leadername} {
set rj_type [dict get $rj type]
set msg "Leading argument '$leadername' for %caller% requires type '$rj_type'. Received: '$ldr'"
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list typemismatch $rj_type] -badarg $leadername -badval $ldr -argspecs $argspecs]] $msg
}
#puts stderr "private::get_dict_can_assign_value $ldridx $values $nameidx $VAL_NAMES" #puts stderr "private::get_dict_can_assign_value $ldridx $values $nameidx $VAL_NAMES"
set msg "Bad number of leaders for %caller%. Not enough remaining values to assign to required arguments (fail on $leadername)." set msg "Bad number of leaders for %caller%. Not enough remaining values to assign to required arguments (fail on $leadername)."
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredleader $leadername ] -badarg $leadername -argspecs $argspecs]] $msg return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredleader $leadername ] -badarg $leadername -argspecs $argspecs]] $msg
@ -11213,6 +11226,19 @@ tcl::namespace::eval punk::args {
} else { } else {
#required named arg #required named arg
if {$valname ni $valnames_received} { if {$valname ni $valnames_received} {
#G-082 follow-on (0.15.2): a word IS present but failed this
#required argument's basic-type allocation screen - report the
#pointed typemismatch (same message/errorcode shape as the
#overflow-site selection below) instead of the generic
#missingrequiredvalue shortfall. Only kind 'type' can arrive
#here (choice screens never apply to required args - G-071
#asymmetry); literal mismatches and multi-member clauses record
#no rejection, so those shapes keep the shortfall report.
if {$rj ne "" && [dict get $rj kind] eq "type" && [dict get $rj argname] eq $valname} {
set rj_type [dict get $rj type]
set msg "Trailing argument '$valname' for %caller% requires type '$rj_type'. Received: '$val'"
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list typemismatch $rj_type] -badarg $valname -badval $val -argspecs $argspecs]] $msg
}
#puts stderr "private::get_dict_can_assign_value $validx $values $nameidx $VAL_NAMES" #puts stderr "private::get_dict_can_assign_value $validx $values $nameidx $VAL_NAMES"
set msg "Bad number of values for %caller%. Not enough remaining values to assign to required arguments (fail on $valname)." set msg "Bad number of values for %caller%. Not enough remaining values to assign to required arguments (fail on $valname)."
return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredvalue $valname ] -badarg $valname -argspecs $argspecs]] $msg return -options [list -code error -errorcode [list PUNKARGS VALIDATION [list missingrequiredvalue $valname ] -badarg $valname -argspecs $argspecs]] $msg

3
src/modules/punk/args-buildversion.txt

@ -1,6 +1,7 @@
0.15.1 0.15.2
#First line must be a semantic version number #First line must be a semantic version number
#all other lines are ignored. #all other lines are ignored.
#0.15.2 - G-082 follow-on: a REQUIRED typed value or leader whose present word fails the basic-type allocation screen (int/double/bool/number/dict) now reports the pointed typemismatch (message + errorcode with -badarg/-badval, same shape as the G-082 overflow-site selection) instead of the generic missingrequiredvalue/missingrequiredleader shortfall - the in-loop required-arg raise sites consult the rejection record returned by get_dict_can_assign_value for the current word. Unchanged by design: end-of-input shortages (valuemissing/count classes), a word failing a required literal-typed member, and multi-member clause partials (no rejection record - conservatism preserved); required-arg choice words were never screened (G-071 asymmetry) so choiceviolation reporting is untouched. parse_status consequences: this shape's top-level status flips incomplete->invalid with failureclass typemismatch (the formstatus verdict was already 'invalid' - the G-152 probe confirmed each such 'incomplete' away before reporting; one probe re-parse saved); multiform noformmatch classes unchanged (already invalid), per-form failureclass/message now the pointed typemismatch. parse_status argdoc status-key note updated. Pins flipped deliberately: parsestatus.test typed-value allocation failure, formviability.test single-form record (top-status/formstatus divergence demonstration re-anchored on a required-option shape), punk/ns cmdhelp.test badarg message match; new errorselection.test required-position coverage (value + leader) with unchanged-shape guards (end-of-input, literal-typed, clause partial).
#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.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.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.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).

63
src/tests/modules/punk/args/testsuites/args/errorselection.test

@ -127,6 +127,69 @@ namespace eval ::testspace {
] ]
#added 2026-08-05 (agent, G-082 follow-on) - required-position basic-type screen rejections.
#The goal's recorded residue: a REQUIRED typed value/leader whose PRESENT word failed the
#basic-type screen reported the generic missingrequiredvalue/missingrequiredleader
#shortfall ("Not enough remaining values...") - punk::args 0.15.2 reports the pointed
#typemismatch from the same rejection-record seam the optional-overflow selection uses.
test errsel_required_type_reject {Required typed (int) value and leader rejecting a present word report pointed typemismatch (punk::args 0.15.2 - was missingrequiredvalue/missingrequiredleader)}\
-setup $common -body {
set docid1 ::testspace::errsel_required_type_value
set docid2 ::testspace::errsel_required_type_leader
set docids [list $docid1 $docid2]
punk::args::define [list @id -id $docid1] {@values -min 1 -max 1} {num -type int -optional 0}
punk::args::define [list @id -id $docid2] {@leaders -min 1 -max 1} {count -type int -optional 0} {@values -min 0 -max -1} {extra -type string -optional 1 -multiple 1}
set err [catch {punk::args::parse {notanumber} withid $docid1} msg opts]
lappend result $err {*}[ecinfo $opts]
lappend result [string match "*Trailing argument 'num' for*requires type 'int'. Received: 'notanumber'*" $msg]
set err [catch {punk::args::parse {notanumber} withid $docid2} msg opts]
lappend result $err {*}[ecinfo $opts]
lappend result [string match "*Leading argument 'count' for*requires type 'int'. Received: 'notanumber'*" $msg]
}\
-cleanup {
foreach id $docids {
punk::args::undefine $id 1
}
}\
-result [list\
1 {PUNKARGS VALIDATION {typemismatch int}} {-badarg num -badval notanumber} 1\
1 {PUNKARGS VALIDATION {typemismatch int}} {-badarg count -badval notanumber} 1
]
#added 2026-08-05 (agent, G-082 follow-on)
test errsel_required_shortfall_shapes_unchanged {Genuine shortfalls keep their missing-class reports: end-of-input, literal-typed required member, multi-member clause partial}\
-setup $common -body {
set docid1 ::testspace::errsel_shortfall_endofinput
set docid2 ::testspace::errsel_shortfall_literal
set docid3 ::testspace::errsel_shortfall_clause
set docids [list $docid1 $docid2 $docid3]
punk::args::define [list @id -id $docid1] {@values -min 0 -max -1} {num -type int -optional 0} {name -type string -optional 0}
punk::args::define [list @id -id $docid2] {@values -min 1 -max 1} {reset -type literal(reset) -optional 0}
punk::args::define [list @id -id $docid3] {@values -min 0 -max -1} {coord -type {int int} -optional 0 -multiple 1}
#no word present for name - the post-loop required check reports it
set err [catch {punk::args::parse {5} withid $docid1} msg opts]
lappend result $err [lindex [lindex [dict get $opts -errorcode] 2] 0]
#literal-category rejections are deliberately unrecorded (syntax-word shapes) - shortfall stays
set err [catch {punk::args::parse {other} withid $docid2} msg opts]
lappend result $err [lindex [lindex [dict get $opts -errorcode] 2] 0]
#end-of-input inside a multi-member clause - allocation cannot affirm the partial words
set err [catch {punk::args::parse {5} withid $docid3} msg opts]
lappend result $err [lindex [lindex [dict get $opts -errorcode] 2] 0]
}\
-cleanup {
foreach id $docids {
punk::args::undefine $id 1
}
}\
-result [list\
1 valuemissing 1 missingrequiredvalue 1 missingrequiredvalue
]
test errsel_genuine_surplus {Genuinely-surplus words still report toomanyarguments (all optionals satisfied or none defined)}\ test errsel_genuine_surplus {Genuinely-surplus words still report toomanyarguments (all optionals satisfied or none defined)}\
-setup $common -body { -setup $common -body {
set docid1 ::testspace::errsel_surplus_after_choice set docid1 ::testspace::errsel_surplus_after_choice

26
src/tests/modules/punk/args/testsuites/args/formviability.test

@ -215,6 +215,12 @@ namespace eval ::testspace {
-result [list incomplete invalid valid] -result [list incomplete invalid valid]
#added 2026-08-02 (agent, G-152) #added 2026-08-02 (agent, G-152)
#2026-08-05 (agent, G-082 follow-on): the type-screen probe formerly demonstrated the
#documented top-status/formstatus split (engine 'incomplete' via missingrequiredvalue
#vs verdict 'invalid') - punk::args 0.15.2 reports that shape as the pointed
#typemismatch (engine 'invalid', agreeing with the verdict), so the split
#demonstration moved to the required-option probe (optionmissing classifies
#'incomplete' while the consumed value word defeats the probe's position guard).
test formviability_singleform_formstatus_sound {single-form parse_status: the formstatus record carries the viability verdict while the top-level status keeps the engine classification}\ test formviability_singleform_formstatus_sound {single-form parse_status: the formstatus record carries the viability verdict while the top-level status keeps the engine classification}\
-setup $common -body { -setup $common -body {
punk::args::define { punk::args::define {
@ -224,18 +230,32 @@ namespace eval ::testspace {
num -type int -optional 0 num -type int -optional 0
name -type string -optional 0 name -type string -optional 0
} }
punk::args::define {
@id -id ::testspace::vsingleopt
@cmd -name testspace::vsingleopt -summary "required-option fixture" -help "required-option fixture"
@opts
-dest -type string -optional 0
@values -min 0 -max -1
extra -type string -optional 1 -multiple 1
}
#valid prefix (5 satisfies num) ending in exhaustion: viable #valid prefix (5 satisfies num) ending in exhaustion: viable
set pstat [punk::args::parse_status {5} withid ::testspace::vsingle] set pstat [punk::args::parse_status {5} withid ::testspace::vsingle]
lappend result [dict get $pstat status] [dict get $pstat formstatus _default status] lappend result [dict get $pstat status] [dict get $pstat formstatus _default status]
#word contradicts num's int type: engine classifies the allocation #word contradicts num's int type: pointed typemismatch (0.15.2) - engine
#shortfall incomplete (documented) but the form is NOT viable #classification and viability verdict agree on invalid
set pstat [punk::args::parse_status {notanint} withid ::testspace::vsingle] set pstat [punk::args::parse_status {notanint} withid ::testspace::vsingle]
lappend result [dict get $pstat status] [dict get $pstat failureclass] [dict get $pstat formstatus _default status] lappend result [dict get $pstat status] [dict get $pstat failureclass] [dict get $pstat formstatus _default status]
#required option missing after a value word has begun: the engine classifies
#optionmissing 'incomplete' but the form is NOT viable (appended flag words
#would be consumed as values) - the documented split, single-form
set pstat [punk::args::parse_status {sometarget} withid ::testspace::vsingleopt]
lappend result [dict get $pstat status] [dict get $pstat failureclass] [dict get $pstat formstatus _default status]
}\ }\
-cleanup { -cleanup {
punk::args::undefine ::testspace::vsingle 1 punk::args::undefine ::testspace::vsingle 1
punk::args::undefine ::testspace::vsingleopt 1
}\ }\
-result [list incomplete incomplete incomplete missingrequiredvalue invalid] -result [list incomplete incomplete invalid typemismatch invalid incomplete optionmissing invalid]
#added 2026-08-02 (agent, G-152) #added 2026-08-02 (agent, G-152)
test formviability_clause_partial_conservative {input ending inside a multi-member type clause reports invalid - documented conservatism of the verdict}\ test formviability_clause_partial_conservative {input ending inside a multi-member type clause reports invalid - documented conservatism of the verdict}\

7
src/tests/modules/punk/args/testsuites/args/parsestatus.test

@ -103,7 +103,10 @@ namespace eval ::testspace {
}\ }\
-result [list 0 invalid error animal choiceviolation bad choiceviolation unparsed 1] -result [list 0 invalid error animal choiceviolation bad choiceviolation unparsed 1]
test parsestatus_typed_value_allocation_failure {a supplied word failing its -type check surfaces as an allocation shortfall WITH the unfillable argument as badarg}\ #flipped 2026-08-05 (agent, G-082 follow-on) - was parsestatus_typed_value_allocation_failure
#pinning failureclass missingrequiredvalue: punk::args 0.15.2 reports a required typed
#value's basic-type screen rejection as the pointed typemismatch (badarg unchanged)
test parsestatus_typed_value_screen_typemismatch {a supplied word failing a required value's -type screen reports typemismatch WITH the unfillable argument as badarg}\
-setup $common -body { -setup $common -body {
set ps [punk::args::parse_status {v1 x x} withid ::testspace::psfix] set ps [punk::args::parse_status {v1 x x} withid ::testspace::psfix]
lappend result [dict get $ps ok] [dict get $ps failureclass] [dict get $ps badarg] lappend result [dict get $ps ok] [dict get $ps failureclass] [dict get $ps badarg]
@ -112,7 +115,7 @@ namespace eval ::testspace {
}\ }\
-cleanup { -cleanup {
}\ }\
-result [list 0 missingrequiredvalue firstval bad unparsed] -result [list 0 typemismatch firstval bad unparsed]
test parsestatus_count_shortfall_incomplete {too few values reports incomplete with no offending argument}\ test parsestatus_count_shortfall_incomplete {too few values reports incomplete with no offending argument}\
-setup $common -body { -setup $common -body {

8
src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test

@ -349,13 +349,17 @@ namespace eval ::testspace {
#the failure message names the queried command via parse -caller instead of whatever #the failure message names the queried command via parse -caller instead of whatever
#the call-frame walk found. #the call-frame walk found.
test cmdhelp_badarg_marking_failed_typed_value {a value failing its -type check (surfacing as an allocation shortfall) badarg-marks the unfillable argument's row}\ #2026-08-05 (agent, G-082 follow-on): message pin updated - punk::args 0.15.2 reports a
#required typed value's screen rejection as the pointed typemismatch ("Trailing argument
#'firstval' ... requires type 'int'") instead of the missingrequiredvalue shortfall
#("fail on firstval"); the badarg marking derivation is unchanged
test cmdhelp_badarg_marking_failed_typed_value {a value failing its -type check (typemismatch since punk::args 0.15.2) badarg-marks the failing argument's row}\
-setup $common -body { -setup $common -body {
variable BAD variable BAD
set out [punk::ns::cmdhelp ::testspace::helpfix v1 x x] set out [punk::ns::cmdhelp ::testspace::helpfix v1 x x]
#the message names the failing argument and its row is badarg-marked #the message names the failing argument and its row is badarg-marked
#punk::args 0.12.6: the message renders once BELOW the usage display - sample the last line #punk::args 0.12.6: the message renders once BELOW the usage display - sample the last line
lappend result [string match "*fail on firstval*" [punk::ansi::ansistrip [lindex [split $out \n] end]]] lappend result [string match "*Trailing argument 'firstval'*requires type 'int'. Received: 'x'*" [punk::ansi::ansistrip [lindex [split $out \n] end]]]
lappend result [any_line_has_sgr $out firstval $BAD] lappend result [any_line_has_sgr $out firstval $BAD]
lappend result [any_line_has_sgr $out lastval $BAD] lappend result [any_line_has_sgr $out lastval $BAD]
}\ }\

Loading…
Cancel
Save