From 0032478e4632211ceba88ff3499fd56074809955 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Tue, 14 Jul 2026 13:12:11 +1000 Subject: [PATCH] G-082 increment 1: activate goal; characterize 0.12.x optional-rejection overflow errors Set G-082 active (user-directed). New errorselection.test pins current single-form error selection before the fix: - _GAP pins: an optional choice-restricted leader (make.tcl dispatch shape), an optional choice-restricted value, and an optional typed (int) value each reject a word that then overflows - all report generic {toomanyarguments N index I} with no -badarg/-badval attribution, discarding the specific rejection (regression vs punk::args 0.5.0) - reference pins: the -optional 0 equivalents report pointed choiceviolation with -badarg/-badval and Leading/Trailing argument messages - guards: genuine surplus stays toomanyarguments; a rejected word consumed by a later slot parses cleanly; multiform failures still route through rank_form_failures (noformmatch) Verified: 7/7 pass against the unfixed module under Tcl 9.0.3. Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com --- GOALS.md | 2 +- goals/G-082-punkargs-error-selection.md | 2 +- .../args/testsuites/args/errorselection.test | 178 ++++++++++++++++++ 3 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 src/tests/modules/punk/args/testsuites/args/errorselection.test diff --git a/GOALS.md b/GOALS.md index 4699c406..042fa0c3 100644 --- a/GOALS.md +++ b/GOALS.md @@ -310,7 +310,7 @@ Detail: goals/G-080-ansi2html-cellgrid-mode.md Scope: src/modules/punk/mix/commandset/doc-999999.0a1.0.tm (doc.* commandset - new generation subcommands), src/scriptapps/tools/punkargs_to_doctools.tcl + punkargs_punknative.tcl (prototypes to productize), src/scriptapps/tools/fontprep/ (shared doc-set asset in link mode), src/doc tree conventions, src/tests coverage Detail: goals/G-081-argdoc-build-pipeline.md -### G-082 [proposed] punk::args single-form parse error selection: specific validation failures preferred over generic overflow +### G-082 [active] punk::args single-form parse error selection: specific validation failures preferred over generic overflow Scope: src/modules/punk/args-999999.0a1.0.tm (get_dict/parse error selection, validation-failure capture around the choice/type screens), src/tests/modules/punk/args/testsuites/args/ (characterization + regression suite) Detail: goals/G-082-punkargs-error-selection.md diff --git a/goals/G-082-punkargs-error-selection.md b/goals/G-082-punkargs-error-selection.md index a26ce612..935717c4 100644 --- a/goals/G-082-punkargs-error-selection.md +++ b/goals/G-082-punkargs-error-selection.md @@ -1,6 +1,6 @@ # G-082 punk::args single-form parse error selection: specific validation failures preferred over generic overflow -Status: proposed +Status: active Scope: src/modules/punk/args-999999.0a1.0.tm (get_dict/parse error selection, validation-failure capture around the choice/type screens), src/tests/modules/punk/args/testsuites/args/ (characterization + regression suite) Goal: when a single-form parse fails because a word was rejected by an optional argument's validation (restricted choices, type) and consequently overflowed the argument list, the reported error names the specific rejection (the choiceviolation with the offending word and choice list, or the type failure) instead of the generic "Received more values than can be assigned" overflow - restoring the pointedness punk::args 0.5.0-era parsing had for optional choice-restricted leaders/values, and removing the need for the -optional 0 authoring workaround adopted by make.tcl's dispatch definitions. Acceptance: a definition with an optional choice-restricted leader or value that receives a non-matching word reports a choiceviolation-class error naming the word and the allowed choices (message and -errorcode), not toomanyarguments - pinned for the make.tcl-style dispatch shape (optional subcommand leader with choices) and a values-position equivalent; a word rejected by a typed optional argument (e.g int) that then overflows reports the type failure similarly; genuinely-surplus cases (all optional args satisfied, extra word remains) still report toomanyarguments; multiform error selection (rank_form_failures) is unchanged or its pins deliberately updated; current 0.12.x behaviour is characterized first and the pins flipped deliberately; full punk::args suite passes with no expectations weakened; on completion the G-030 -optional 0 workaround sites are revisited (make.tcl dispatch definitions, the src/modules/AGENTS.md "punk::args definition authoring ergonomics" bullet) and relaxed or re-documented. diff --git a/src/tests/modules/punk/args/testsuites/args/errorselection.test b/src/tests/modules/punk/args/testsuites/args/errorselection.test new file mode 100644 index 00000000..9e51c907 --- /dev/null +++ b/src/tests/modules/punk/args/testsuites/args/errorselection.test @@ -0,0 +1,178 @@ + +package require tcltest + +#added 2026-07-14 (agent, G-082) - single-form parse error selection. +#Characterizes the error reported when a word rejected by an OPTIONAL argument's +#allocation screen (restricted choices, basic type) subsequently overflows the +#argument list. Current 0.12.x behaviour: the specific rejection is discarded and a +#generic toomanyarguments overflow is reported (regression vs punk::args 0.5.0 - +#see goals/G-082-punkargs-error-selection.md). The _GAP pins here encode that +#current behaviour and are to be flipped deliberately by the G-082 fix. +namespace eval ::testspace { + namespace import ::tcltest::* + variable common { + set result "" + } + + #slim an errorcode: classification triple plus -badarg/-badval extras (drop -argspecs) + proc ecinfo {opts} { + set ec [dict get $opts -errorcode] + set extras [dict create] + foreach {k v} [lrange $ec 3 end] { + if {$k eq "-argspecs"} {break} + dict set extras $k $v + } + return [list [lrange $ec 0 2] $extras] + } + + + test errsel_leader_optional_choice_reject_GAP {GAP: optional choice-restricted leader rejecting a word reports generic overflow (make.tcl dispatch shape)}\ + -setup $common -body { + set docid ::testspace::errsel_leader_optional_choice + set docids [list $docid] + punk::args::define [list @id -id $docid] {@leaders -min 0 -max 1} {subcommand -type string -optional 1 -choices {build test help}} + + set err [catch {punk::args::parse {frobnicate} withid $docid} msg opts] + lappend result $err {*}[ecinfo $opts] + }\ + -cleanup { + foreach id $docids { + punk::args::undefine $id 1 + } + }\ + -result [list\ + 1 {PUNKARGS VALIDATION {toomanyarguments 1 index 0}} {} + ] + + + test errsel_value_optional_choice_reject_GAP {GAP: optional choice-restricted value rejecting a word reports generic overflow}\ + -setup $common -body { + set docid ::testspace::errsel_value_optional_choice + set docids [list $docid] + punk::args::define [list @id -id $docid] @values {subject -type string -optional 1 -choices {build test help}} + + set err [catch {punk::args::parse {frobnicate} withid $docid} msg opts] + lappend result $err {*}[ecinfo $opts] + }\ + -cleanup { + foreach id $docids { + punk::args::undefine $id 1 + } + }\ + -result [list\ + 1 {PUNKARGS VALIDATION {toomanyarguments 1 index 0}} {} + ] + + + test errsel_value_optional_type_reject_GAP {GAP: optional typed (int) value rejecting a word reports generic overflow}\ + -setup $common -body { + set docid ::testspace::errsel_value_optional_type + set docids [list $docid] + punk::args::define [list @id -id $docid] @values {num -type integer -optional 1} + + set err [catch {punk::args::parse {notanumber} withid $docid} msg opts] + lappend result $err {*}[ecinfo $opts] + }\ + -cleanup { + foreach id $docids { + punk::args::undefine $id 1 + } + }\ + -result [list\ + 1 {PUNKARGS VALIDATION {toomanyarguments 1 index 0}} {} + ] + + + test errsel_required_choice_reference {Reference: required choice-restricted args report pointed choiceviolation (leader and value)}\ + -setup $common -body { + set docid1 ::testspace::errsel_required_leader + set docid2 ::testspace::errsel_required_value + set docids [list $docid1 $docid2] + punk::args::define [list @id -id $docid1] {@leaders -min 0 -max 1} {subcommand -type string -optional 0 -choices {build test help}} + punk::args::define [list @id -id $docid2] @values {subject -type string -optional 0 -choices {build test help}} + + set err [catch {punk::args::parse {frobnicate} withid $docid1} msg opts] + lappend result $err {*}[ecinfo $opts] + lappend result [string match "Leading argument 'subcommand' for*must be one of the listed values:*Received: 'frobnicate'*" $msg] + + set err [catch {punk::args::parse {frobnicate} withid $docid2} msg opts] + lappend result $err {*}[ecinfo $opts] + lappend result [string match "Trailing argument 'subject' for*must be one of the listed values:*Received: 'frobnicate'*" $msg] + }\ + -cleanup { + foreach id $docids { + punk::args::undefine $id 1 + } + }\ + -result [list\ + 1 {PUNKARGS VALIDATION {choiceviolation frobnicate choices {build test help}}} {-badarg subcommand -badval frobnicate} 1\ + 1 {PUNKARGS VALIDATION {choiceviolation frobnicate choices {build test help}}} {-badarg subject -badval frobnicate} 1 + ] + + + test errsel_genuine_surplus {Genuinely-surplus words still report toomanyarguments (all optionals satisfied or none defined)}\ + -setup $common -body { + set docid1 ::testspace::errsel_surplus_after_choice + set docid2 ::testspace::errsel_surplus_no_names + set docids [list $docid1 $docid2] + punk::args::define [list @id -id $docid1] @values {subject -type string -optional 1 -choices {build test help}} + punk::args::define [list @id -id $docid2] @values + + #subject satisfied by 'build' - 'extra' is genuinely surplus + set err [catch {punk::args::parse {build extra} withid $docid1} msg opts] + lappend result $err {*}[ecinfo $opts] + + #no value names defined at all + set err [catch {punk::args::parse {extra} withid $docid2} msg opts] + lappend result $err {*}[ecinfo $opts] + }\ + -cleanup { + foreach id $docids { + punk::args::undefine $id 1 + } + }\ + -result [list\ + 1 {PUNKARGS VALIDATION {toomanyarguments 2 index 1}} {}\ + 1 {PUNKARGS VALIDATION {toomanyarguments 1 index 0}} {} + ] + + + test errsel_rejected_word_lands_later {A word rejected by an optional choice arg but consumed by a later slot parses cleanly}\ + -setup $common -body { + set docid ::testspace::errsel_word_lands_later + set docids [list $docid] + punk::args::define [list @id -id $docid] @values {subject -type string -optional 1 -choices {build test help}} {other -type string -optional 1} + + set argd [punk::args::parse {frobnicate} withid $docid] + lappend result [dict get $argd values] + }\ + -cleanup { + foreach id $docids { + punk::args::undefine $id 1 + } + }\ + -result [list\ + {other frobnicate} + ] + + + test errsel_multiform_selection {Multiform failures still route through rank_form_failures (noformmatch)}\ + -setup $common -body { + set docid ::testspace::errsel_multiform + set docids [list $docid] + punk::args::define [list @id -id $docid] {@form -form {A B}} {@leaders -form A -min 0 -max 1} {subcommand -form A -type string -optional 1 -choices {build test help}} {@values -form B -min 1 -max 1} {num -form B -type int} + + set err [catch {punk::args::parse {frobnicate} withid $docid} msg opts] + set ec [dict get $opts -errorcode] + lappend result $err [lrange $ec 0 1] [lindex $ec 2 0] + }\ + -cleanup { + foreach id $docids { + punk::args::undefine $id 1 + } + }\ + -result [list\ + 1 {PUNKARGS VALIDATION} noformmatch + ] +} +tcltest::cleanupTests ;#needed to produce test summary line.