diff --git a/GOALS.md b/GOALS.md index 05ab96b2..4699c406 100644 --- a/GOALS.md +++ b/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 diff --git a/goals/G-083-punkargs-argument-relations.md b/goals/G-083-punkargs-argument-relations.md new file mode 100644 index 00000000..f7833848 --- /dev/null +++ b/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). diff --git a/goals/G-084-punkargs-parsekey-completeness.md b/goals/G-084-punkargs-parsekey-completeness.md new file mode 100644 index 00000000..1060a359 --- /dev/null +++ b/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). diff --git a/src/tests/modules/AGENTS.md b/src/tests/modules/AGENTS.md index 104c6220..97a2b016 100644 --- a/src/tests/modules/AGENTS.md +++ b/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