Browse Source

G-083 increment 3 achieved: punk::args argument-relations usage rendering + lsearch moduledoc adoption (punk::args 0.21.0, project 0.59.0)

The -conflicts and -parsekeymode error vocabulary (inc 1, 0.19.0) and its
parse-time optionconflict enforcement (inc 2, 0.20.0) now surface in
usage/arg_error and synopsis output, and the lsearch tclcore moduledoc
models its documented option incompatibilities with the new vocabulary.

Rendering:
- A per-arg -conflicts list appends a "conflicts with: <targets>" hint to
  the argument's help text in the usage table (targets resolved to display
  names via lookup_optset, so a parsekey target shows its member flag, not
  the raw parsekey).
- A named @opts group marked -parsekeymode error is annotated "mutually
  exclusive (distinct members may not be combined)" in its group header;
  override (default) groups carry no such annotation.
- The synopsis one-line form carries no conflict detail.

lsearch moduledoc (tclcore):
- -sorted gains -conflicts {-glob -regexp} and -bisect gains -conflicts
  {-all -not} (per-arg conflicts; the whole group stays -parsekeymode
  override so -glob/-regexp remain last-wins).
- The "(documentation incomplete - punk::args fixes required for grouped
  mutually exclusive options and prefix calculation)" caveat is dropped
  from the @cmd -help.

clock clicks left unchanged - its active positional-choice definition
already models exclusivity (exactly-one-of) and the acceptance clause names
lsearch only.

relations.test extended with 8 increment-3 pins: 4 rendering
(conflicts_usage_hint, parsekeymode_error_usage_group_header,
parsekeymode_override_no_header_annotation, conflicts_synopsis_no_hint)
and 4 lsearch adoption (lsearch_sorted_conflicts_glob +
lsearch_bisect_conflicts_all raise optionconflict via errorcode substring
match, lsearch_glob_regexp_last_wins stays last-wins, lsearch_caveat_dropped).

Legacy untouched by default (no -conflicts/parsekeymode-error definition
carries no new rendering). Full punk/args suite 371/0 (3 skipped);
tclcoreparity 10/10; punk/ns 125/125. All G-083 acceptance criteria met;
goal flipped to achieved 2026-08-08 and archived (detail -> goals/archive/,
index -> GOALS-archive.md, reference sweep applied to G-053/G-084, deferred
-requires/-implies dependency relations recorded as an open Follow-on).

Project version 0.58.0 -> 0.59.0 (minor: backward-compatible behaviour
additions, following the G-173 punk::path precedent). punk::args module
0.20.0 -> 0.21.0.

Assisted-by: harness=pi; primary-model=huggingface/zai-org/GLM-5.2; api-location=huggingface.co
master
Julian Noble 5 days ago
parent
commit
9125d56237
  1. 24
      CHANGELOG.md
  2. 4
      GOALS-archive.md
  3. 3
      GOALS.md
  4. 8
      goals/G-053-punkargs-multiple-ranges.md
  5. 13
      goals/G-084-punkargs-parsekey-completeness.md
  6. 31
      goals/archive/G-083-punkargs-argument-relations.md
  7. 2
      punkproject.toml
  8. 24
      src/modules/punk/args-999999.0a1.0.tm
  9. 3
      src/modules/punk/args-buildversion.txt
  10. 13
      src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm
  11. 2
      src/tests/modules/AGENTS.md
  12. 105
      src/tests/modules/punk/args/testsuites/args/relations.test

24
CHANGELOG.md

@ -5,6 +5,30 @@ 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`
"Project Versioning" section for the bump policy.
## [0.59.0] - 2026-08-08
- `punk::args` gains argument-relation vocabulary and parse-time
enforcement (G-083): per-argument `-conflicts <list>` declares that
named arguments (parsekeys or flag names) must not be received together
with this argument, and `@opts -parsekeymode override|error` (per named
group) makes a shared-parsekey group's distinct-member co-occurrence a
parse-time `optionconflict` (override, the legacy last-wins default, or
error). Both are cross-validated at definition resolve; a colliding
`-parsekey` is now a resolve error. `optionconflict` is a new
`PUNKARGS VALIDATION` failure class, checked against received arguments
only (defaults never conflict), over both the ordinary and mash
(short-flag bundling) parse paths, and classified as `invalid` (not
`incomplete`). Usage/arg_error rendering surfaces the rules: a
`conflicts with: <targets>` hint on each conflicted argument's help row
and a `mutually exclusive (distinct members may not be combined)` group
header annotation for `-parsekeymode error` groups; the synopsis
one-line form carries no conflict detail. The `lsearch` tclcore
moduledoc models its documented `-sorted`/`-glob`/`-regexp` and
`-bisect`/`-all`/`-not` incompatibilities via per-arg `-conflicts` (the
group stays `override` so `-glob`/`-regexp` remain last-wins) and drops
its `punk::args fixes required` caveat. Legacy behaviour is untouched by
default (no `-conflicts`, default `override`, no colliding parsekey).
## [0.58.0] - 2026-08-07
- `punk::path` gains machine-parsable return modes and a stable conflict verdict

4
GOALS-archive.md

@ -20,6 +20,10 @@ Do not edit archived records here except to fix a broken path. Archived detail f
## Archived goals
### G-083 [achieved 2026-08-08] punk::args argument relations: strict mutual exclusivity and parsekey-group integrity → detail: goals/archive/G-083-punkargs-argument-relations.md
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)
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.
### G-173 [achieved 2026-08-07] punk::path machine-parsable returns (text/dict/json) with stable conflict verdict → detail: goals/archive/G-173-punkpath-machine-returns.md
Scope: src/modules/punk-999999.0a1.0.tm (punk::path proc - add -return modes + -context); src/tests/modules/punk/path/testsuites/tests/ (return-shape + round-trip + ansi-freeness tests)
Acceptance: -return gains text, dict and json choices whose output contains no ansi escapes regardless of -highlight; the returned structure is documented in the command's punk::args definition; the default table output is byte-unchanged for existing users (existing path.test cases stay green); -return json round-trips the same data as -return dict (decode via tcllib json, recompare); the conflict verdict is stable independent of how the consumer invoked the proc via a -context <namespace> option (default the caller's namespace); new tests pin dict/json shape, ansi-freeness, and dict/json round-trip parity; a duplicate-path entry and an overshadowed executable each appear in the dict/json with the fields a machine consumer needs (idx, is_duplicate, duplicate_of, overshadowed, overshadowed_by, tcl_conflicts with match=exact|nocase).

3
GOALS.md

@ -287,9 +287,6 @@ 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-083 [active] 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)

8
goals/G-053-punkargs-multiple-ranges.md

@ -68,6 +68,8 @@ rendering gain meaningful bounded-repetition forms (e.g. "0-1", "2-4").
- The runtests `-include-paths` fix (repeatable, accumulate, single-list form still
accepted) shipped independently on 2026-07-10 and does not depend on this goal.
- Archived-goal references in this file: G-046 achieved 2026-07-10 (goals/archive/G-046-punkargs-deferred-help-and-fixes.md).
- Referenced by G-083 (argument relations - "folding into G-053" considered and
rejected there) and G-084 (-parsekey cross-member -multiple collection rides this
goal's occurrence arity) - recorded 2026-07-24 after overlap review.
- Referenced by G-083 (argument relations - "folding into G-053"
considered and rejected there; achieved - see
goals/archive/G-083-punkargs-argument-relations.md) and G-084 (-parsekey
cross-member -multiple collection rides this goal's occurrence arity) -
recorded 2026-07-24 after overlap review.

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

@ -30,9 +30,11 @@ _GAP test in src/tests/modules/punk/args/testsuites/args/parsekey.test:
- 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.
parsekey_shared_key_multiple_accumulation. Open design decision: with
G-083's relations vocabulary now landed (achieved - see
goals/archive/G-083-punkargs-argument-relations.md), "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
@ -46,8 +48,9 @@ _GAP test in src/tests/modules/punk/args/testsuites/args/parsekey.test:
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).
cross-member collection decision; achieved - see
goals/archive/G-083-punkargs-argument-relations.md), G-053 (occurrence arity
of a single argument).
- Related: G-151 (achieved 2026-08-05 - goals/archive/G-151-punkargs-annotated-success-render.md) -
its parse_report landing report bridges the parse-result storage-key folds
AT DISPLAY LEVEL only (an argument's -parsekey, else an aliased optionset's

31
goals/G-083-punkargs-argument-relations.md → goals/archive/G-083-punkargs-argument-relations.md

@ -1,6 +1,6 @@
# G-083 punk::args argument relations: strict mutual exclusivity and parsekey-group integrity
Status: active
Status: achieved 2026-08-08
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.
@ -131,3 +131,32 @@ Design leans (settle and record here during the work):
- Bug found and fixed during increment 2: the group-co-occurrence dedup check
initially used `ni` (not-in) where `in` was meant - `ni` returns true on an empty
list, so it skipped every member and the check never fired. Corrected to `in`.
- 2026-08-08 increment 3 (usage rendering + lsearch moduledoc adoption, punk::args 0.21.0,
project 0.59.0): the conflict rules now surface in usage/arg_error and synopsis
output, and the lsearch tclcore moduledoc models its documented incompatibilities.
Rendering: (a) a per-arg `-conflicts` list appends a 'conflicts with: <targets>'
hint to the argument's help text in the usage table (targets resolved to display
names via lookup_optset, so a parsekey target shows its member flag, not the raw
parsekey); (b) a named @opts group marked -parsekeymode error is annotated
'mutually exclusive (distinct members may not be combined)' in its group header,
while override (default) groups carry no such annotation; (c) the synopsis
one-line form carries no conflict detail (conflicts are a usage-table concern,
not a synopsis-line one). lsearch moduledoc: -sorted gains -conflicts {-glob
-regexp} and -bisect gains -conflicts {-all -not} (per-arg conflicts, the whole
group stays -parsekeymode override so -glob/-regexp remain last-wins); the
'(documentation incomplete - punk::args fixes required for grouped mutually
exclusive options and prefix calculation)' caveat is dropped from @cmd -help.
clock clicks left unchanged - its active positional-choice definition already
models exclusivity (exactly-one-of) and the acceptance clause names lsearch
only. relations.test extended with 8 increment-3 pins (4 rendering:
conflicts_usage_hint, parsekeymode_error_usage_group_header,
parsekeymode_override_no_header_annotation, conflicts_synopsis_no_hint; 4
lsearch adoption: lsearch_sorted_conflicts_glob + lsearch_bisect_conflicts_all
raise optionconflict via errorcode substring match, lsearch_glob_regexp_last_wins
stays last-wins, lsearch_caveat_dropped). Legacy untouched by default confirmed:
full punk/args suite 371 total / 368 passed / 3 skipped / 0 failed via tclsh90s;
tclcoreparity 10/10; punk/ns 125/125. All G-083 acceptance criteria met.
## Follow-ons
Follow-on: -requires/-implies dependency relations (e.g lsearch -subindices with -index, -bisect implying -sorted) - deliberately deferred during G-083 (the man-page semantics are mostly soft "has no effect unless"); the optionconflict errorcode vocabulary was shaped so a later requires-relation slots in alongside without reshaping it => open

2
punkproject.toml

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

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

@ -5927,6 +5927,11 @@ tcl::namespace::eval punk::args {
} else {
set groupinfo "(common flag group)\nkey:$thisgroup_parsekey"
}
#G-083: annotate a strict shared-parsekey group
set _g83_gmode [punk::args::system::Dict_getdef [dict get $form_dict OPT_GROUPS $thisgroup] -parsekeymode override]
if {$_g83_gmode eq "error"} {
append groupinfo "\nmutually exclusive (distinct members may not be combined)"
}
if {$use_table} {
$t add_row [list " $thisgroup" $groupinfo "" "" $help]
if {$arg_status eq "bad"} {
@ -6404,6 +6409,25 @@ tcl::namespace::eval punk::args {
}
}
#G-083: per-arg -conflicts hint - render the conflicting targets
#by their flag spelling (resolve parsekeys to their optset's last member).
set _g83_conflicts [Dict_getdef $arginfo -conflicts {}]
if {[llength $_g83_conflicts]} {
set _g83_cdisp [list]
foreach _g83_c $_g83_conflicts {
if {[dict exists $form_dict ARG_INFO $_g83_c]} {
lappend _g83_cdisp $_g83_c
} elseif {[dict exists $lookup_optset $_g83_c]} {
lappend _g83_cdisp [dict get $lookup_optset $_g83_c]
} else {
lappend _g83_cdisp $_g83_c
}
}
if {$help ne ""} {append help \n}
append help "conflicts with: [join $_g83_cdisp { }]"
unset _g83_conflicts _g83_c _g83_cdisp
}
# =============================================
#REVIEW
if {"-help" ni $unindentedfields} {

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

@ -1,6 +1,7 @@
0.20.0
0.21.0
#First line must be a semantic version number
#all other lines are ignored.
#0.21.0 - G-083 increment 3 (argument-relations usage rendering + lsearch moduledoc adoption): the -conflicts and -parsekeymode error vocabulary now surfaces in usage/arg_error and synopsis output, and the lsearch tclcore moduledoc models its documented option incompatibilities with the new vocabulary. Rendering: (a) a per-arg -conflicts list appends a 'conflicts with: <targets>' hint to the argument's help text in the usage table (targets resolved to display names via lookup_optset, so a parsekey target shows its member flag, not the raw parsekey); (b) a named @opts group marked -parsekeymode error is annotated 'mutually exclusive (distinct members may not be combined)' in its group header, while override (default) groups carry no such annotation; (c) the synopsis one-line form carries no conflict detail (conflicts are a usage-table concern, not a synopsis-line one). Characterized in relations.test (conflicts_usage_hint, parsekeymode_error_usage_group_header, parsekeymode_override_no_header_annotation, conflicts_synopsis_no_hint). lsearch moduledoc: -sorted gains -conflicts {-glob -regexp} and -bisect gains -conflicts {-all -not} (per-arg conflicts, the whole group stays -parsekeymode override so -glob/-regexp remain last-wins); the '(documentation incomplete - punk::args fixes required for grouped mutually exclusive options and prefix calculation)' caveat is dropped from the @cmd -help. Pinned in relations.test (lsearch_sorted_conflicts_glob + lsearch_bisect_conflicts_all raise optionconflict, lsearch_glob_regexp_last_wins stays last-wins, lsearch_caveat_dropped). clock clicks unchanged - its active positional-choice definition already models exclusivity (exactly-one-of) and the acceptance clause names lsearch only. Legacy untouched by default (no -conflicts/parsekeymode-error definition carries no new rendering). Full punk/args suite green; tclcoreparity 10/10.
#0.20.0 - G-083 increment 2 (argument-relations parse-time enforcement): the -conflicts and -parsekeymode error vocabulary declared at define time (0.19.0) is now enforced at parse. A new optionconflict failure class joins the PUNKARGS VALIDATION errorcode vocabulary, mirroring optionmissing's shape: {optionconflict <arg_a> <arg_b> received <receivednames>} - it names both offending received arguments, for both per-arg -conflicts violations (any pair, cross-group) and -parsekeymode error group co-occurrence (distinct members of a shared-parsekey group). The check runs in a single post-resolution site in get_dict_form (after the optionmissing/valuemissing block), so the ordinary option path and the mash (short-flag bundling) path share it - both raise identically. Checked against RECEIVED arguments only (defaults never conflict), after prefix/abbreviation resolution, on optset identity (a new optsets_received tracker for options, since flagsreceived collapses shared-parsekey members onto one api_opt). Runs unconditionally - a received conflict is a hard contradiction in any mode, not end-of-input exhaustion, so the candidacy/viability probe reports it as status invalid (parse_status_classify maps optionconflict to invalid, not incomplete). Define-time check added: a group marked -parsekeymode error must also declare a non-empty -parsekey (the strict mode only applies to a shared-parsekey group). Legacy untouched by default: -parsekeymode override is the default and absent -conflicts means no check runs, so the full existing suite (including the pinned parsekey_repeat_ordering last-wins / prepend-defaults idiom) passes unchanged. relations.test extended with 7 parse-time enforcement pins (raise cases, defaults-never-conflict, cross-group, parsekeymode error raise + one-received-ok + override-legacy, parse_status invalid); full punk/args suite green.
#0.19.0 - G-083 increment 1 (argument-relations define-time vocabulary): new per-argument key -conflicts <list> (parsekeys or flag names that must not be RECEIVED together with this argument; checked against received args only at parse time, defaults never conflict - parse-time optionconflict enforcement lands in a later G-083 increment) and new @opts-level -parsekeymode override|error (per named OPT_GROUPS group; error = distinct-member co-occurrence within a shared-parsekey group raises optionconflict at parse; override = legacy last-wins, the default). Both keys are cross-validated at resolve: -conflicts targets must name a defined argname or declared -parsekey; -parsekeymode requires -group and a value of override|error. Define-time integrity hole closed: a -parsekey value colliding with a distinct defined argument's name (one that does not share that -parsekey) is now a resolve error instead of silently forming an implicit shared-key group (parsekey_collides_with_defined_optname_GAP flipped to a define-time error pin in parsekey.test). Legacy behaviour untouched by default (no -conflicts, default -parsekeymode override, no colliding parsekey). New testsuite relations.test pins the define-time vocabulary; full punk/args suite green.
#0.18.0 - G-151 annotated success render: new punk::args::parse_report - landing report for a SUCCESSFUL parse, one row per value-holding argument (received, or filled from -default) in declaration-section order (leaders, opts, values) with columns Argument | Source | Value - the success-side sibling of the arg_error usage table (a flag-like word consumed as a VALUE by position shows attributed to its consuming argument - the make.tcl dry-run 'kitname = punk91 -confirm 0' class). Input: a punk::args::parse result dict (its id key must resolve to a registered definition), or words + 'withid <id>' (the parse is performed first; a rejected line raises the same validation error parse would - only successful parses have a landing report). Source column: received / received xN (-multiple aggregation count) / default - the plain-mode-safe twin of the colour marking (received CLR(check), default CLR(parsekey_hint)); absent optionals with no default-in-effect get no row. Returns: table (arg_error-styled bordered block via textblock; the default), tableobject, string (plain lines 'name (source) = value'; also the degraded no-textblock path), dict (machine form: id/form/rows with class/source/received/positions/multiple/hasvalue/value - never elided). Display elision is type-aware, deterministic and always marked (display-hint honesty): strings single-line-ized (ansistring VIEW -lf 1 -vt 1 -sp 0 - embedded controls/ANSI render visibly so a value cannot disturb the table; real spaces stay spaces) then grapheme-capped via the new ansistring TRUNCATE (punk::ansi 0.2.0) with trailing '...' plus ' (len N)' citing the raw value's string length; -multiple aggregations and -type list values element-capped with '(+N more)' (whole list-quoted elements kept while they fit, first element char-capped if it alone overflows); -type dict values pair-capped with '(+N pairs)'; markers stay intact so cells can exceed very small caps by the marker width; plain-string fallbacks when punk::ansi is unavailable. Width cascade per the G-149 plumbing precedent: caller -valuewidth > new @cmd directive key -reportvaluewidth > built-in 48; 0 means no cap; deliberately never console-derived (deterministic and pipe-safe - the G-007 emit-then-query lesson). Value-in-effect lookup bridges storage-key folds at display level (-parsekey renames, aliased optionsets' last-|-member finalopts fold) so received aliased/parsekey'd arguments keep their row - G-084 owns the storage-key model itself. Failure-side rendering (arg_error/usage) unchanged. make.tcl retiring its interim one-line 'dry-run: line accepted ...' report in favour of this render is consumer follow-through after a bootsupport promotion (not part of this change). New testsuite parsereport.test; define -help documents @cmd -reportvaluewidth beside the other directive-options.

13
src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm

@ -7506,10 +7506,7 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
"This command searches the elements of list to see if one of them matches pattern. If so, the command returns
the index of the first matching element (unless the options -all or -inline are specified.) If not, the command
returns -1 or (if options -all or -inline are specified) the empty string. The option arguments indicates how
the elements of the list are to be matched against pattern.
(documentation incomplete - punk::args fixes required for grouped mutually exlusive options and prefix calculation)
"
the elements of the list are to be matched against pattern. "
@leaders -min 0 -max 0
@opts -type none -parsekey "-MATCHSTYLE" -group "MATCHING STYLE OPTIONS" -grouphelp\
"If all matching style options are omitted, the default matching style is -glob.
@ -7524,13 +7521,15 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
-regexp -typedefaults "-regexp" -help\
"Pattern is treated as a regular expression and matched against each list element using the
rules described in the re_syntax reference page."
-sorted -typedefaults "-sorted" -help\
-sorted -typedefaults "-sorted" -conflicts {-glob -regexp} -help\
"The list elements are in sorted order. If this option is specified, lsearch will use a more
efficient searching algorithm to search list. If no other options are specified, list is
assumed to be sorted in increasing order, and to contain ASCII strings. This option is
mutually exclusive with -glob and -regexp, and is treated exactly like -exact when either
-all or -not are specified."
#G-083: -sorted mutually exclusive with -glob and -regexp (per the man page) -
#modelled with per-arg -conflicts while keeping the MATCHING STYLE OPTIONS group at
#the default override mode (last-wins among -exact/-glob/-regexp).
@opts -type string -parsekey "" -group "GENERAL MODIFIER OPTIONS"
-all -type none -help\
@ -7570,7 +7569,7 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
"The list elements are sorted in decreasing order. This option is only meaningful when used with -sorted."
-increasing -typedefaults "-increasing" -default "-increasing" -help\
"The list elements are sorted in increasing order. This option is only meaningful when used with -sorted."
-bisect -typedefaults "-bisect" -help\
-bisect -typedefaults "-bisect" -conflicts {-all -not} -help\
"Inexact search when the list elements are in sorted order. For an increasing list the last index where
the element is less than or equal to the pattern is returned. For a decreasing list the last index where
the element is greater than or equal to the pattern is returned. If the pattern is before the first

2
src/tests/modules/AGENTS.md

File diff suppressed because one or more lines are too long

105
src/tests/modules/punk/args/testsuites/args/relations.test

@ -1,6 +1,9 @@
package require tcltest
package require punk::args
#G-083 lsearch moduledoc adoption tests require the tclcore moduledoc package
tcltest::testConstraint have_tclcoredocs [expr {![catch {package require punk::args::moduledoc::tclcore}]}]
#G-083 argument relations: -conflicts (per-arg) and -parsekeymode (per @opts group).
#This file pins the DEFINE-TIME vocabulary (spec-compiler storage and validation);
#the parse-time optionconflict enforcement is pinned in validation.test and
@ -205,5 +208,107 @@ namespace eval ::testspace {
}\
-result [list invalid optionconflict]
#added 2026-08-07 (agent) - G-083 increment 3 rendering
test conflicts_usage_hint {the usage argument table shows a 'conflicts with:' hint on a -conflicts argument row}\
-setup $common -body {
set id ::testspace::g83_conflicts_usage_hint
punk::args::define [list @id -id $id] @opts {-x -type none -conflicts -y} {-y -type none}
lappend docids $id
set out [punk::ansi::ansistrip [punk::args::usage -return string -scheme nocolour $id]]
#the conflicts hint appears in the table
lappend result [string match "*conflicts with: -y*" $out]
#and appears exactly once (only the -x row carries it, not -y)
lappend result [regexp -all {conflicts with:} $out]
set result
}\
-cleanup {
foreach id $docids {punk::args::undefine $id 1}
}\
-result [list 1 1]
test parsekeymode_error_usage_group_header {the usage table annotates a -parsekeymode error group header as mutually exclusive}\
-setup $common -body {
set id ::testspace::g83_pkmode_usage
punk::args::define [list @id -id $id] {@opts -group G -parsekey -pk -parsekeymode error} {-a -type none} {-b -type none}
lappend docids $id
set out [punk::ansi::ansistrip [punk::args::usage -return string -scheme nocolour $id]]
lappend result [string match "*mutually exclusive*" $out]
set result
}\
-cleanup {
foreach id $docids {punk::args::undefine $id 1}
}\
-result [list 1]
test parsekeymode_override_no_header_annotation {a default -parsekeymode override group header carries no mutual-exclusion annotation}\
-setup $common -body {
set id ::testspace::g83_pkmode_override_usage
punk::args::define [list @id -id $id] {@opts -group G -parsekey -pk} {-a -type none} {-b -type none}
lappend docids $id
set out [punk::ansi::ansistrip [punk::args::usage -return string -scheme nocolour $id]]
lappend result [string match "*mutually exclusive*" $out]
set result
}\
-cleanup {
foreach id $docids {punk::args::undefine $id 1}
}\
-result [list 0]
test conflicts_synopsis_no_hint {the one-line synopsis does not carry per-arg conflict detail (it lives in the usage table)}\
-setup $common -body {
set id ::testspace::g83_syn_probe
punk::args::define [list @id -id $id] @opts {-x -type none -conflicts -y} {-y -type none}
lappend docids $id
set out [punk::ansi::ansistrip [punk::args::synopsis -return full $id]]
lappend result [string match "*conflicts with*" $out]
lappend result [regexp -- {\[-x\] \[-y\]} $out]
set result
}\
-cleanup {
foreach id $docids {punk::args::undefine $id 1}
}\
-result [list 0 1]
#added 2026-08-07 (agent) - G-083 increment 3 lsearch moduledoc adoption
test lsearch_sorted_conflicts_glob {lsearch -sorted and -glob are modelled as conflicting (optionconflict)}\
-constraints have_tclcoredocs\
-setup $common -body {
try {
punk::args::parse {-sorted -glob list pat} withid ::lsearch
set result "UNEXPECTED-accepted"
} on error {emsg eopts} {
set result [string match "*PUNKARGS VALIDATION*optionconflict -sorted -glob*" [dict get $eopts -errorcode]]
}
}\
-result [list 1]
test lsearch_bisect_conflicts_all {lsearch -bisect and -all are modelled as conflicting (optionconflict)}\
-constraints have_tclcoredocs\
-setup $common -body {
try {
punk::args::parse {-bisect -all list pat} withid ::lsearch
set result "UNEXPECTED-accepted"
} on error {emsg eopts} {
set result [string match "*PUNKARGS VALIDATION*optionconflict -bisect -all*" [dict get $eopts -errorcode]]
}
}\
-result [list 1]
test lsearch_glob_regexp_last_wins {lsearch -glob and -regexp stay last-wins (no conflict) - the group is NOT -parsekeymode error}\
-constraints have_tclcoredocs\
-setup $common -body {
set argd [punk::args::parse {-glob -regexp list pat} withid ::lsearch]
set result [dict get $argd opts -MATCHSTYLE]
}\
-result [list -regexp]
test lsearch_caveat_dropped {the lsearch @cmd -help no longer carries the 'punk::args fixes required' caveat}\
-constraints have_tclcoredocs\
-setup $common -body {
set spec [punk::args::get_spec ::lsearch]
set result [string match "*punk::args fixes required*" [dict get $spec cmd_info -help]]
}\
-result [list 0]
}
tcltest::cleanupTests ;#needed to produce test summary line.
Loading…
Cancel
Save