Browse Source

punk::args: -parsekey characterization tests; goals G-083/G-084 (argument relations, parsekey completeness)

parsekey coverage review outcome:

- tests/args/parsekey.test: pin mash-path last-wins ordering, prefix-abbreviation
  keying and @opts -any passthrough; GAP pins for last-defined-member default
  precedence, cross-member -multiple value loss, parsekey/optname collision
  conflation, and values/leaders parsekey breakage (abort/ignore/reject)
- new tests/dev/parsekey-knownbugs.test: desired-behaviour pins disabled behind
  punkargsKnownBug (value/leader key renaming, cross-member accumulation in
  received order)
- GOALS.md + detail files: G-083 argument relations (per-arg -conflicts checked
  against received args, @opts strict mode for shared-parsekey groups,
  define-time parsekey collision validation, lsearch/clock clicks dogfood);
  G-084 -parsekey completeness (cross-member -multiple collection, leaders/values
  support or define-time rejection, defaulted-member precedence documented)
- src/tests/modules/AGENTS.md: punk/args suite index updated

Verified: full punk/args suite under Tcl 9.0.3 = 221 total / 217 passed /
0 failed / 4 skipped (intended disabled pins); parsekey.test 16/16 under 8.7;
goals_lint clean.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 day ago
parent
commit
a21315a109
  1. 8
      GOALS.md
  2. 86
      goals/G-083-punkargs-argument-relations.md
  3. 50
      goals/G-084-punkargs-parsekey-completeness.md
  4. 2
      src/tests/modules/AGENTS.md
  5. 224
      src/tests/modules/punk/args/testsuites/args/parsekey.test
  6. 80
      src/tests/modules/punk/args/testsuites/dev/parsekey-knownbugs.test

8
GOALS.md

@ -313,3 +313,11 @@ 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
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
### G-083 [proposed] punk::args argument relations: strict mutual exclusivity and parsekey-group integrity
Scope: src/modules/punk/args-999999.0a1.0.tm (resolve/spec compiler, parse paths incl mash, arg_error/synopsis/usage renderers, errorcode vocabulary), src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (lsearch, clock clicks dogfood), src/tests/modules/punk/args/testsuites/args/ + testsuites/dev/ (GAP flips + new characterization)
Detail: goals/G-083-punkargs-argument-relations.md
### G-084 [proposed] punk::args -parsekey completeness: cross-member -multiple collection, leaders/values support, defined default precedence
Scope: src/modules/punk/args-999999.0a1.0.tm (parse paths, resolve directive handling for @leaders/@values), src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (sites carrying '#todo - fix -parsekey for leaders and values'), src/tests/modules/punk/args/testsuites/args/parsekey.test + testsuites/dev/parsekey-knownbugs.test (GAP/known-bug flips)
Detail: goals/G-084-punkargs-parsekey-completeness.md

86
goals/G-083-punkargs-argument-relations.md

@ -0,0 +1,86 @@
# G-083 punk::args argument relations: strict mutual exclusivity and parsekey-group integrity
Status: proposed
Scope: src/modules/punk/args-999999.0a1.0.tm (resolve/spec compiler, parse paths incl mash, arg_error/synopsis/usage renderers, errorcode vocabulary), src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (lsearch, clock clicks dogfood), src/tests/modules/punk/args/testsuites/args/ + testsuites/dev/ (GAP flips + new characterization)
Goal: punk::args definitions can express hard mutual exclusivity - a per-argument -conflicts relation (keyed on parsekeys, checked only against received arguments so defaults never conflict) and an @opts-level strict mode making distinct-member co-occurrence within a shared-parsekey group an error - while legacy last-wins remains the default, so lsearch-class commands (grouped styles with pairwise incompatibilities like -sorted vs -glob/-regexp and -bisect vs -all/-not) parse and document faithfully without combinatorial @form explosion.
Acceptance: parse raises a usage-style error with a structured errorcode (PUNKARGS VALIDATION optionconflict shape) naming both offending received arguments for -conflicts violations and strict-group co-occurrence, through both the ordinary and mash option paths with prefix abbreviations resolved before the check; define time rejects a -parsekey colliding with a distinct defined argument's name (parsekey_collides_with_defined_optname_GAP flips); legacy behaviour is untouched by default (full existing punk::args suite passes, including the pinned last-wins/prepend-defaults override idiom); the lsearch moduledoc models its documented incompatibilities with the new vocabulary and drops its "punk::args fixes required for grouped mutually exclusive options" caveat; usage/synopsis output for conflict rules is pinned by characterization tests.
## Context
Review finding (2026-07-14): punk::args has no way to express "error if these
arguments are combined". What exists today:
- `-parsekey` shared keys give SOFT exclusivity by overwrite: last received
member wins under the common key. That semantics is deliberate and pinned
(parsekey_repeat_ordering: overriding earlier non -multiple options must
always be possible - it protects the prepend-defaults idiom
`punk::args::parse [list -flag default {*}$userargs]`).
- `-group`/`-grouphelp` associate options for usage display only; no parse
semantics.
- Multiple `@form`s are the only strict mechanism - structural, combinatorially
explosive, and they fork the documentation.
- The `namespace which` moduledoc entry works around the gap by modelling
`-command`/`-variable` as one positional with `literalprefix(...)` choices
(exactly-one-of, but only for a fixed position, not free-order flags) and
carries `##todo - make mutually exclusive - (separate forms)`.
Evidence of need in the tclcore moduledoc alone:
- The lsearch definition states: "(documentation incomplete - punk::args fixes
required for grouped mutually exclusive options and prefix calculation)".
- lsearch `-sorted` is "mutually exclusive with -glob and -regexp" per the man
page - yet it shares the -MATCHSTYLE parsekey group with them, whose members
are otherwise last-wins. Both semantics coexist within ONE group, so a
per-group boolean alone cannot model lsearch.
- lsearch `-bisect` "cannot be used with either -all or -not" - a cross-group
conflict with no shared value key.
- `clock clicks` `-milliseconds`/`-microseconds` carry a definition comment
noting they are mutually exclusive and unmodelled.
A define-time integrity hole was found in the same review: nothing prevents a
`-parsekey` value colliding with a DIFFERENT defined option's name (e.g.
`{-a -parsekey -b} {-b}`); the two silently conflate into an implicit
shared-key group (pinned by parsekey_collides_with_defined_optname_GAP).
## Approach
Design leans (settle and record here during the work):
- Per-argument `-conflicts {parsekey-or-flag ...}`: violation checked against
RECEIVED arguments only, after prefix/abbreviation resolution, never against
defaults. Applies to any pair regardless of grouping.
- `@opts`-level strict mode for a shared-parsekey group (name TBD, e.g.
`-parsekeymode override|error` or `-groupexclusive`): distinct-member
co-occurrence errors; same-member repetition stays G-053 territory
(`-multiple {0 1}`), and legacy last-wins remains the default everywhere.
- The mash (short-flag bundling) parse path implements its own parsekey logic
and must share the conflict check - covered explicitly by acceptance.
- Errorcode vocabulary: extend PUNKARGS VALIDATION with an `optionconflict`
member mirroring the existing `optionmissing` shape, designed so a later
requires-relation slots in alongside.
- Usage/synopsis rendering: the group rendering machinery (OPT_GROUPS headers,
parsekey hints) is most of the display substrate; conflict rules need a
representation there.
## Alternatives considered
- Per-group strict boolean only - insufficient: lsearch mixes last-wins and
error semantics within one group (see Context).
- Modelling exclusivity via `@form` explosion - rejected: combinatorial and
forks documentation; the goal exists to avoid this.
- Folding into G-053 range-valued -multiple - rejected: G-053 is occurrence
arity of ONE argument; this goal is relations BETWEEN arguments. They
compose (shared error vocabulary, different axes).
## Notes
- `-requires`/`-implies` (dependency relations, e.g. lsearch `-subindices`
with `-index`, `-bisect` implying `-sorted`) deliberately deferred - the man
page semantics there are mostly soft ("has no effect unless"); design the
errorcode vocabulary so they can be added without reshaping it.
- Test assets already in place (2026-07-14): characterization pins in
src/tests/modules/punk/args/testsuites/args/parsekey.test (mash ordering,
prefix abbreviation, collision conflation GAP) ready to flip/extend.
- Related: G-053 (occurrence arity), G-072 (compound clause types - orthogonal),
G-084 (parsekey completeness fixes; its cross-member -multiple design
decision should be made with this goal's vocabulary in mind).

50
goals/G-084-punkargs-parsekey-completeness.md

@ -0,0 +1,50 @@
# G-084 punk::args -parsekey completeness: cross-member -multiple collection, leaders/values support, defined default precedence
Status: proposed
Scope: src/modules/punk/args-999999.0a1.0.tm (parse paths, resolve directive handling for @leaders/@values), src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (sites carrying '#todo - fix -parsekey for leaders and values'), src/tests/modules/punk/args/testsuites/args/parsekey.test + testsuites/dev/parsekey-knownbugs.test (GAP/known-bug flips)
Goal: -parsekey behaves as a complete, position-independent result-keying mechanism: distinct -multiple members sharing a parsekey accumulate under the shared key in received order (no silent value loss), -parsekey on leaders and values renames their result/received keys the same way it does for options (or is rejected at define time everywhere it is unsupported - no silent ignore, no parse-time abort), and the precedence rule for multiple defaulted members of one group is documented behaviour rather than an in-code '? review'.
Acceptance: the three punkargsKnownBug tests in testsuites/dev/parsekey-knownbugs.test are enabled and pass (or are amended to the settled design and pass); the corresponding _GAP pins in parsekey.test are flipped or retired; the '#todo - fix -parsekey for leaders and values' moduledoc comment is resolved; the full existing punk::args suite passes untouched.
## Context
Probe findings from the 2026-07-14 parsekey coverage review, each pinned by a
_GAP test in src/tests/modules/punk/args/testsuites/args/parsekey.test:
- Cross-member -multiple value loss: with `-add` and `-sub` both `-multiple 1`
sharing parsekey `-op`, parsing `-add a -sub b -add c` returns `-op b` - the
collected `-add` occurrences `{a c}` vanish. Occurrences are collected per
member flag and the shared-key resolution then returns only one member's
list (parsekey_shared_key_multiple_collection_GAP).
- `-parsekey` on a value is accepted at define time but ABORTS parsing with an
internal error; on a leader it is silently ignored; the `@values` directive
line rejects it as an unrecognised key (parsekey_values_leaders_GAP). The
tclcore moduledoc carries `#todo - fix -parsekey for leaders and values`
next to a definition using it, and choices.test notes a leaders/values
parsekey mechanism would help multi-element clause choice grouping.
- When more than one member of a shared-parsekey group declares `-default`,
the last-defined member's default wins - the ordered_opts default handling
in the source carries a literal `? review`
(parsekey_shared_key_member_defaults_GAP).
## Approach
- Accumulate-in-received-order is the lean for cross-member -multiple
collection, pinned (disabled) in dev/parsekey-knownbugs.test as
parsekey_shared_key_multiple_accumulation. Open design decision: once
G-083's relations vocabulary exists, "error on cross-member combination
unless permitted" is a defensible alternative - decide before activating.
- Leaders/values support should reuse the option path's keying rules
(result + received dicts, required-list satisfaction) rather than a parallel
mechanism; where support is deliberately not implemented, define time must
reject the key with a clear message instead of deferring failure to parse.
- The defaulted-members precedence rule (whatever is settled) gets stated in
the -parsekey documentation and the `? review` comment removed.
## Notes
- Desired-behaviour pins (disabled behind punkargsKnownBug):
src/tests/modules/punk/args/testsuites/dev/parsekey-knownbugs.test - enable
and flip the parsekey.test _GAP pins as fixes land.
- Related: G-083 (argument relations - shared design context for the
cross-member collection decision), G-053 (occurrence arity of a single
argument).

2
src/tests/modules/AGENTS.md

@ -40,7 +40,7 @@ Unit tests for editable source modules under `src/modules/`, `src/modules_tcl8/`
- `opunk/console/` — ::opunk::Console backend subclass tests (`testsuites/console/backends.test`, G-001): virtual dispatch of subclass overrides through base-class calls and punk::console::console_spec_resolve (both unchanged), TestConsole determinism + probe-free at_eof, SshConsole capability/eof + the flagship size-via-ANSI-query-over-socket case (a scripted remote terminal answers CSI 6n), TkConsole widget size/eof (gated behind env PUNK_TEST_TK=1 - Tk in the shared testinterp has side effects; also verifiable standalone under a tk-capable kit e.g `punk91 src <script>`)
- `punkcheck/` — punkcheck module tests (install, summarize_install_resultdict, installtrack)
- `punk/ansi/` — punk::ansi tests (`testsuites/ansi/`): ansistrip/ansimerge, plus characterization of the ANSI-at-position mechanisms (`ansistring.test`: INDEX/INDEXCODE/INDEXCHAR/RANGE/INSERT grapheme indexing with SGR-prefix merging, INDEXCOLUMNS/COLUMNINDEX double-wide column mapping, trim/VIEW), code splitting invariants (`ta.test`: detect/detectcode distinction, split_codes/split_codes_single/split_at_codes shapes and round-trip) and single-code/effective-state semantics (`codetype.test`: is_sgr_reset/has_sgr_leadingreset, has_any/all_effective, sgr_merge, sequence_type classify). ANSI codes in these tests are literal escape strings so results are colour-state independent
- `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), 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/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/ns/` — punk::ns tests (`testsuites/ns/`): cmdwhich/cmdinfo/cmd_traverse doc-lookup flow (`cmdflow.test`, G-040 parity) and 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))
- `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/multishell.test`: scriptset wrap via the punk.multishell.cmd template - structure/LF-only/determinism, checkfile 512-byte label validation of fresh wraps AND the committed bin/runtime.cmd, the runtime scriptset round-trip byte-identity pin, and 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)
- `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)

224
src/tests/modules/punk/args/testsuites/args/parsekey.test

@ -203,5 +203,229 @@ namespace eval ::testspace {
{-coord {1 2}}\
{-coord {1 2}}
]
#added 2026-07-14 (agent) - parsekey coverage review.
#The mash (short-flag bundling) parse path implements its own parsekey ident logic
#separately from the ordinary option path - keep both pinned.
test parsekey_mash_last_wins {Ensure last flag wins within and after a mash for members sharing a -parsekey}\
-setup $common -body {
set docids [list]
set argd [punk::args::parse {-ud} withdef {@opts -mash 1 -type none -parsekey -direction} {-u -typedefaults u} {-d -typedefaults d}]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
lappend result [dict get $argd received]
lappend result [dict get $argd solos]
#reverse order within the mash token
set argd [punk::args::parse {-du} withdef {@opts -mash 1 -type none -parsekey -direction} {-u -typedefaults u} {-d -typedefaults d}]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
#a separate flag after the mash must override the mash members
set argd [punk::args::parse {-ud -u} withdef {@opts -mash 1 -type none -parsekey -direction} {-u -typedefaults u} {-d -typedefaults d}]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
}\
-cleanup {
foreach id $docids {
punk::args::undefine $id 1
}
}\
-result [list\
{-direction d}\
{-direction 0 -direction 1}\
{-direction -direction}\
{-direction u}\
{-direction u}
]
#added 2026-07-14 (agent) - parsekey coverage review.
test parsekey_prefix_abbreviation {Ensure abbreviated member flags resolve and store under the group -parsekey}\
-setup $common -body {
set docids [list]
set argd [punk::args::parse {-exa} withdef {@opts -type none -parsekey -MATCH} {-exact -typedefaults -exact} {-glob -typedefaults -glob -default -glob}]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
lappend result [dict get $argd received]
#default-in-effect from the defaulted member when nothing received
set argd [punk::args::parse {} withdef {@opts -type none -parsekey -MATCH} {-exact -typedefaults -exact} {-glob -typedefaults -glob -default -glob}]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
}\
-cleanup {
foreach id $docids {
punk::args::undefine $id 1
}
}\
-result [list\
{-MATCH -exact}\
{-MATCH 0}\
{-MATCH -glob}
]
#added 2026-07-14 (agent) - parsekey coverage review.
test parsekey_arbitrary_opts_passthrough {Ensure undeclared options pass through alongside a -parsekey group when @opts -any 1}\
-setup $common -body {
set docids [list]
set argd [punk::args::parse {-incr -other x} withdef {@opts -any 1} {-incr -type none -parsekey -dir -typedefaults u} {-decr -type none -parsekey -dir -typedefaults d}]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
}\
-cleanup {
foreach id $docids {
punk::args::undefine $id 1
}
}\
-result [list\
{-dir u -other x}
]
#added 2026-07-14 (agent) - parsekey coverage review.
#GAP: behaviour unsettled in source (args tm ordered_opts default handling carries a '? review').
#Current behaviour: when more than one member of a shared-parsekey group declares -default,
#the default of the LAST-DEFINED defaulted member is returned under the shared key.
test parsekey_shared_key_member_defaults_GAP {GAP: last-defined member default wins when multiple members of a shared-parsekey group have -default}\
-setup $common -body {
set docids [list]
#both members defaulted - last defined wins
set argd [punk::args::parse {} withdef @opts {-xy -parsekey -coord -type list -default {0 0}} {-xyz -parsekey -coord -type list -default {0 0 0}}]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
#only last member defaulted
set argd [punk::args::parse {} withdef @opts {-xy -parsekey -coord -type list} {-xyz -parsekey -coord -type list -default {0 0 0}}]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
#only first member defaulted
set argd [punk::args::parse {} withdef @opts {-xy -parsekey -coord -type list -default {0 0}} {-xyz -parsekey -coord -type list}]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
}\
-cleanup {
foreach id $docids {
punk::args::undefine $id 1
}
}\
-result [list\
{-coord {0 0 0}}\
{-coord {0 0 0}}\
{-coord {0 0}}
]
#added 2026-07-14 (agent) - parsekey coverage review.
#GAP: -multiple 1 collection does NOT accumulate across distinct members sharing a -parsekey.
#Occurrences are collected per member flag, and the shared-key resolution then returns only
#one member's collected list - the values of other members are silently lost
#(here -add received {a c} but the result under -op is just the -sub list {b}).
#Desired accumulate-in-received-order semantics pinned (disabled) in dev/parsekey-knownbugs.test.
test parsekey_shared_key_multiple_collection_GAP {GAP: shared-parsekey members with -multiple 1 lose values of all but one member}\
-setup $common -body {
set docids [list]
set argd [punk::args::parse {-incr -decr -incr} withdef {@opts -type none -parsekey -dir -multiple 1} {-incr -typedefaults u} {-decr -typedefaults d}]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
lappend result [dict get $argd multis]
lappend result [dict get $argd received]
#value-taking members - the earlier member's collected values {a c} are lost entirely
set argd [punk::args::parse {-add a -sub b -add c} withdef {@opts -type string -parsekey -op -multiple 1} -add -sub]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
}\
-cleanup {
foreach id $docids {
punk::args::undefine $id 1
}
}\
-result [list\
{-dir d}\
{-dir}\
{-dir 0 -dir 1 -dir 2}\
{-op b}
]
#added 2026-07-14 (agent) - parsekey coverage review.
#GAP: no define-time validation when a -parsekey value collides with another defined
#option's name. The two options silently conflate into an implicit shared-key group
#(last received wins under the common key).
test parsekey_collides_with_defined_optname_GAP {GAP: -parsekey matching another option's name silently forms an implicit group}\
-setup $common -body {
set docids [list]
set argd [punk::args::parse {-a 1 -b 2} withdef @opts {-a -parsekey -b -type string} {-b -type string}]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
lappend result [dict get $argd received]
set argd [punk::args::parse {-a 1} withdef @opts {-a -parsekey -b -type string} {-b -type string}]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
set argd [punk::args::parse {-b 2} withdef @opts {-a -parsekey -b -type string} {-b -type string}]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
}\
-cleanup {
foreach id $docids {
punk::args::undefine $id 1
}
}\
-result [list\
{-b 2}\
{-b 0 -b 1}\
{-b 1}\
{-b 2}
]
#added 2026-07-14 (agent) - parsekey coverage review.
#GAP: -parsekey outside @opts. tclcore moduledoc carries '#todo - fix -parsekey for leaders and values'.
#Current behaviour:
# - per-arg -parsekey on a value is accepted at define time but ABORTS parsing with an internal error
# - the @values directive line rejects -parsekey outright (unrecognised key)
# - per-arg -parsekey on a leader is silently ignored (result keyed by the leader name)
#Desired rename semantics pinned (disabled) in dev/parsekey-knownbugs.test.
test parsekey_values_leaders_GAP {GAP: -parsekey on a value aborts parse, on a leader is ignored, on @values line is rejected}\
-setup $common -body {
set docid ::testspace::parsekey_values_leaders_GAP
set docids [list $docid]
punk::args::define [list @id -id $docid] @values {v1 -parsekey renamed -type string}
set err [catch {punk::args::parse {hello} withid $docid} msg]
lappend result $err
set err [catch {punk::args::parse {hello} withdef {@values -parsekey renamed} {v1 -type string}} msg]
lappend result $err
lappend result [string match "*unrecognised key '-parsekey' in @values line*" $msg]
set argd [punk::args::parse {hello there} withdef {@leaders -min 1 -max 1} {l1 -parsekey renamed -type string} @values {v1 -type string}]
lappend docids [dict get $argd id]
lappend result [dict get $argd leaders]
lappend result [dict get $argd received]
}\
-cleanup {
foreach id $docids {
punk::args::undefine $id 1
}
}\
-result [list\
1\
1 1\
{l1 hello}\
{l1 0 v1 1}
]
}
tcltest::cleanupTests ;#needed to produce test summary line.

80
src/tests/modules/punk/args/testsuites/dev/parsekey-knownbugs.test

@ -0,0 +1,80 @@
package require tcltest
#added 2026-07-14 (agent) - parsekey coverage review.
#Desired-behaviour pins for known parsekey gaps. Each has a corresponding _GAP test in
#testsuites/args/parsekey.test pinning today's behaviour; when a fix lands, enable the
#test here and flip/remove the GAP pin there.
namespace eval ::testspace {
namespace import ::tcltest::*
tcltest::testConstraint punkargsKnownBug 0
variable common {
set result ""
}
test parsekey_value_result_key {Known bug: -parsekey on a value should rename the values result key (currently aborts parse)}\
-constraints punkargsKnownBug\
-setup $common -body {
set docids [list]
set argd [punk::args::parse {hello} withdef @values {v1 -parsekey renamed -type string}]
lappend docids [dict get $argd id]
lappend result [dict get $argd values]
lappend result [dict exists [dict get $argd received] renamed]
lappend result [dict exists [dict get $argd received] v1]
}\
-cleanup {
foreach id $docids {
punk::args::undefine $id 1
}
}\
-result [list\
{renamed hello} 1 0
]
test parsekey_leader_result_key {Known bug: -parsekey on a leader should rename the leaders result key (currently ignored)}\
-constraints punkargsKnownBug\
-setup $common -body {
set docids [list]
set argd [punk::args::parse {hello there} withdef {@leaders -min 1 -max 1} {l1 -parsekey renamed -type string} @values {v1 -type string}]
lappend docids [dict get $argd id]
lappend result [dict get $argd leaders]
lappend result [dict exists [dict get $argd received] renamed]
}\
-cleanup {
foreach id $docids {
punk::args::undefine $id 1
}
}\
-result [list\
{renamed hello} 1
]
#Design-pending rather than settled: the lean is that -multiple 1 members sharing a
#-parsekey accumulate under the shared key in received order (today only one member's
#collected list survives - see parsekey_shared_key_multiple_collection_GAP).
test parsekey_shared_key_multiple_accumulation {Design-pending: shared-parsekey members with -multiple 1 should accumulate in received order}\
-constraints punkargsKnownBug\
-setup $common -body {
set docids [list]
set argd [punk::args::parse {-incr -decr -incr} withdef {@opts -type none -parsekey -dir -multiple 1} {-incr -typedefaults u} {-decr -typedefaults d}]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
set argd [punk::args::parse {-add a -sub b -add c} withdef {@opts -type string -parsekey -op -multiple 1} -add -sub]
lappend docids [dict get $argd id]
lappend result [dict get $argd opts]
}\
-cleanup {
foreach id $docids {
punk::args::undefine $id 1
}
}\
-result [list\
{-dir {u d u}}\
{-op {a b c}}
]
}
tcltest::cleanupTests ;#needed to produce test summary line.
Loading…
Cancel
Save