Browse Source

G-040 proposed (punk::args -choicealiases); choice-flow characterization tests; -choice* doc clarifications

- GOALS.md: G-040 punk::args choice aliasing with parse normalization, display
  folding, and doc-lookup parity (user-approved); detail file records the
  prefix/deny/reserve collapse analysis from the punk::help work, the
  cmd_traverse divergence, the c/to/tc minimum-prefix policy question, and the
  test-first sequencing
- new punk/ns cmdflow.test (10 tests): cmdwhich/cmdinfo/cmd_traverse docid
  flow - direct, via-alias and subhelp-choiceinfo resolution (exact + unique
  prefix), unknown-word fallback to parent docid, -choiceprefix 0 exact-only,
  space-delimited ids, cmdhelp -return string smoke; the parse-vs-doc-walk
  divergence (deny/reserve lists honoured by punk::args::parse, ignored by the
  cmd_traverse walk) pinned as GAP tests pending G-040
- choices.test: 4 new unrestricted-mode (-choicerestricted 0) tests - prefix
  normalization to the canonical choice, unknown/ambiguous passthrough,
  denylist and reservelist passthrough, and the punk::help collapse recipe
  (prefix+deny+reserve on a -multiple leader)
- punk::args 0.2.2 (doc-only): define doc -choice* fields now state the
  -choicerestricted 0 passthrough semantics, -choiceprefix result
  normalization + minimal-prefix display highlighting, denylist behaviour for
  shorter prefixes in both restricted modes, and the reservelist phantom-entry
  idiom for per-choice minimum-prefix control
- suites: punk/args 74 pass + 1 skip (was 70+1), punk/ns 18 pass (was 8)

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 7 days ago
parent
commit
e45fea9b16
  1. 1
      CHANGELOG.md
  2. 6
      GOALS.md
  3. 102
      goals/G-040-punkargs-choicealiases.md
  4. 27
      src/modules/punk/args-999999.0a1.0.tm
  5. 3
      src/modules/punk/args-buildversion.txt
  6. 105
      src/tests/modules/punk/args/testsuites/args/choices.test
  7. 243
      src/tests/modules/punk/ns/testsuites/ns/cmdflow.test

1
CHANGELOG.md

@ -7,6 +7,7 @@ Entries are newest-first; one bullet per notable change. See the root `AGENTS.md
## [0.4.4] - 2026-07-08 ## [0.4.4] - 2026-07-08
- punk::args 0.2.2 (doc-only): the `punk::args::define` documentation for the -choice* fields now spells out the verified interplay semantics — `-choicerestricted 0` passthrough of non-matching/ambiguous/denied/reserved words, `-choiceprefix` normalization of accepted prefixes to the canonical choice in parse results (with minimal-prefix highlighting in usage display), and the `-choiceprefixreservelist` phantom-entry idiom for per-choice minimum-prefix control. Characterization tests added (choices.test unrestricted-mode tests; new punk/ns cmdflow.test for the cmdwhich/cmdinfo/cmd_traverse docid flow, pinning the parse-vs-doc-walk deny/reserve divergence as GAP pending G-040).
- help system restructured onto a topic registry (punk module 0.2.0, `::punk::helptopic`): each topic (`topics|help`, `tcl`, `env|environment`, `console|term|terminal`) is a handler proc with its own punk::args definition, and the `::punk::help`/`::punk::help_chunks` definitions are (re)generated from the registry — `i help` now renders a documented usage table with topic choices and summaries, and `i help <topic>` shows per-topic documented usage (previously an autogenerated stub). `help topics` is derived from the registry (lists all aliases, fits 80 columns). No-arg overview and command-fallthrough (`help <cmdname>`) output byte-identical to before; 80-column layout preserved; verified on both generations, script and shell subcommands. `help env` without an initialised punk::config (e.g. script contexts) degrades to a one-line notice instead of an error stack. The registry is the intended seam for future subshell-declared topics (punk::config-gated — not yet a goal). - help system restructured onto a topic registry (punk module 0.2.0, `::punk::helptopic`): each topic (`topics|help`, `tcl`, `env|environment`, `console|term|terminal`) is a handler proc with its own punk::args definition, and the `::punk::help`/`::punk::help_chunks` definitions are (re)generated from the registry — `i help` now renders a documented usage table with topic choices and summaries, and `i help <topic>` shows per-topic documented usage (previously an autogenerated stub). `help topics` is derived from the registry (lists all aliases, fits 80 columns). No-arg overview and command-fallthrough (`help <cmdname>`) output byte-identical to before; 80-column layout preserved; verified on both generations, script and shell subcommands. `help env` without an initialised punk::config (e.g. script contexts) degrades to a one-line notice instead of an error stack. The registry is the intended seam for future subshell-declared topics (punk::config-gated — not yet a goal).
## [0.4.3] - 2026-07-08 ## [0.4.3] - 2026-07-08

6
GOALS.md

@ -284,3 +284,9 @@ Scope: src/modules/punk/repl-999999.0a1.0.tm (console reader/event loop and EOF/
Detail: goals/G-039-orphan-console-spin.md Detail: goals/G-039-orphan-console-spin.md
Goal: the observed failure mode - an interactive punk902z left running after its hosting terminal/console went away spins roughly a full core indefinitely (observed 2026-07-08: a 37-minute orphan with a single hard-looping thread) - is reliably reproduced and root-caused, then fixed or mitigated so a shell whose console dies exits or reaches zero-CPU idle cleanly. Goal: the observed failure mode - an interactive punk902z left running after its hosting terminal/console went away spins roughly a full core indefinitely (observed 2026-07-08: a 37-minute orphan with a single hard-looping thread) - is reliably reproduced and root-caused, then fixed or mitigated so a shell whose console dies exits or reaches zero-CPU idle cleanly.
Acceptance: a documented procedure reproduces the spin on the current kit (e.g. launch an interactive shell in a terminal, then kill/close the hosting terminal or conhost), or the investigation records the attempts made and what evidence would reopen it; the spinning code path is identified (prime suspect: a console read/event loop treating a dead console's immediate EOF/error as retryable without backoff or termination - adjacent to the console-EOF restart path G-038 takes ownership of); after fix/mitigation, the same procedure shows the orphaned process exiting or settling at effectively zero CPU within a short grace period, with live-console interactive behaviour unchanged; the wedge-scoring hazard note (orphans polluting process-liveness checks in test harnesses) is updated to match the outcome. Acceptance: a documented procedure reproduces the spin on the current kit (e.g. launch an interactive shell in a terminal, then kill/close the hosting terminal or conhost), or the investigation records the attempts made and what evidence would reopen it; the spinning code path is identified (prime suspect: a console read/event loop treating a dead console's immediate EOF/error as retryable without backoff or termination - adjacent to the console-EOF restart path G-038 takes ownership of); after fix/mitigation, the same procedure shows the orphaned process exiting or settling at effectively zero CPU within a short grace period, with live-console interactive behaviour unchanged; the wedge-scoring hazard note (orphans polluting process-liveness checks in test harnesses) is updated to match the outcome.
### G-040 [proposed] punk::args choice aliasing (-choicealiases) with parse normalization, display folding, and doc-lookup parity
Scope: src/modules/punk/args-999999.0a1.0.tm (parse + usage rendering), src/modules/punk/ns-999999.0a1.0.tm (cmdinfo/cmd_traverse choice resolution parity), src/modules/punk-999999.0a1.0.tm (punk::help topic argdoc as first consumer), src/tests/modules/punk/args/testsuites/, src/tests/modules/punk/ns/testsuites/
Detail: goals/G-040-punkargs-choicealiases.md
Goal: punk::args supports choice aliases (-choicealiases {alias canonical ...}) accepted at parse and normalized to the canonical choice in results, folded into the canonical entry in usage display - and the punk::ns doc-lookup walk resolves choice words by the same rules as the parser (aliases, -choiceprefix, -choiceprefixdenylist, -choiceprefixreservelist) - so alias sets like punk::help's topics|help and console|term|terminal collapse to one displayed entry per topic with `help X` and `i help X` agreeing.
Acceptance: a definition using -choicealiases parses an alias (and an alias prefix where -choiceprefix allows) to its canonical choice in the parse result, with -choicerestricted 0 passthrough and the deny/reserve lists honoured unchanged; usage display shows one entry per canonical choice with aliases folded (no duplicate rows; -choicelabels attach to the canonical); punk::ns::cmdinfo/cmd_traverse resolve subcommand words to docids with the same outcome as the parser for alias, prefix, denied, reserved and unknown words (the pre-goal characterization tests updated from pinned-GAP to fixed); punk::help's topic definition adopts the feature so `i help` lists one entry per registered topic while `help h`/`help e` still fall through to command lookup; definitions without -choicealiases behave unchanged (existing punk::args and punk::ns suites pass).

102
goals/G-040-punkargs-choicealiases.md

@ -0,0 +1,102 @@
# G-040 punk::args choice aliasing (-choicealiases) with parse normalization, display folding, and doc-lookup parity
Status: proposed
Scope: src/modules/punk/args-999999.0a1.0.tm (parse + usage rendering), src/modules/punk/ns-999999.0a1.0.tm (cmdinfo/cmd_traverse choice resolution parity), src/modules/punk-999999.0a1.0.tm (punk::help topic argdoc as first consumer), src/tests/modules/punk/args/testsuites/, src/tests/modules/punk/ns/testsuites/
Acceptance: a definition using -choicealiases parses an alias (and an alias prefix where -choiceprefix allows) to its canonical choice in the parse result, with -choicerestricted 0 passthrough and the deny/reserve lists honoured unchanged; usage display shows one entry per canonical choice with aliases folded (no duplicate rows; -choicelabels attach to the canonical); punk::ns::cmdinfo/cmd_traverse resolve subcommand words to docids with the same outcome as the parser for alias, prefix, denied, reserved and unknown words (the pre-goal characterization tests updated from pinned-GAP to fixed); punk::help's topic definition adopts the feature so `i help` lists one entry per registered topic while `help h`/`help e` still fall through to command lookup; definitions without -choicealiases behave unchanged (existing punk::args and punk::ns suites pass).
## Context
Arose from the 2026-07-08 punk::help topic-registry restructure. The `::punk::help` argdoc
declares the topic leader with `-choices {topics help tcl env environment console term
terminal}` (`-choiceprefix 0 -choicerestricted 0`): every alias is its own choice entry, so
the `i help` usage table shows 8 entries for 4 topics, with duplicated -choicelabels.
What the existing -choice* machinery already expresses (all verified 2026-07-08 on the dev
tree, punk902z src, tcl 9.0.2):
- Prefix-shaped alias pairs collapse completely via `-choiceprefix 1` plus the two lists:
topic ... -choicerestricted 0 -choiceprefix 1
-choices {topics help tcl environment console terminal}
-choiceprefixdenylist {help}
-choiceprefixreservelist {en ter}
gives: `env`..`environment` -> normalized to `environment` in the parse result (the
parser returns the canonical choice, so downstream alias resolution needs no prefix
logic); `e`/`en` pass through to the unrestricted fallthrough (the reserved phantom `en`
blocks them without becoming selectable); `term`+ -> `terminal` with `t`/`te`/`ter`
passing through; `help` requires the full word (denylist) so `h`/`he`/`hel` stay
available as command-fallthrough words; unknown words (`lindex`) pass through unchanged.
- The usage renderer self-documents the scheme: each choice cell highlights its minimal
accepted prefix (bright green `env` in `environment`, `c` in `console`), and a denied
choice renders fully highlighted (type all of it).
- Minimum-prefix policy is fully controllable per word by reserving each shorter phantom
(e.g. reserve `c` and `co` to require `con` for console). Open policy question for
punk::help: whether to allow the free extra prefixes `c`->console, `to`->topics,
`tc`->tcl, which mildly shadow those words as command-fallthrough candidates.
What it cannot express - the actual feature gap:
- Aliases with no prefix relation to their canonical (`help` -> `topics`) must remain
separate displayed choice entries with duplicated labels.
- Even prefix-related aliases remain separate *display* entries when both full words are
listed (`console` and `terminal` both render, same label), because a choice entry is an
atomic string with no canonicalization link.
Hence `-choicealiases {alias canonical ...}`: alias accepted at parse (participating in
prefix calculation like a reservelist entry, but selectable), normalized to its canonical
in the parse result, and folded in usage display (shown as an annotation on the canonical
entry - e.g. dimmed `help` beneath/beside `topics` - not as its own row). -choicelabels
keys stay canonical-only.
## Approach
1. punk::args::define: accept and validate `-choicealiases` (each value must name an
existing choice; alias must not collide with a choice or another alias). Parse layer:
alias hits (exact, or unique prefix when -choiceprefix and not denied/reserved)
normalize to the canonical, before -choicemultiple/-multiple accounting so duplicates
collapse predictably. Usage/arg_error rendering: fold alias names into the canonical
entry's cell; extend the minimal-prefix highlighting to alias words.
2. punk::ns cmd_traverse parity: the subcommand walk currently does its own
`tcl::prefix::match` over -choices/-choicegroups and honours only `-choiceprefix` -
`-choiceprefixdenylist` and `-choiceprefixreservelist` are ignored (divergence proven:
`i help en`-style lookups prefix-match where `help en` execution falls through).
Factor the choice-word resolution into one shared punk::args helper (choices + groups +
aliases + prefix + deny/reserve -> canonical-or-miss) consumed by both the parser and
cmd_traverse, rather than a second implementation. choiceinfo lookups (subhelp et al)
then key on the canonical.
3. punk::help adoption: registry aliases map onto -choicealiases (topics/help,
env/environment, console/term/terminal); decide and record the minimum-prefix policy
(reservelist phantoms) including the c/to/tc question above; `help topics` output and
`helptopic::resolve` stay as-is (resolve receives canonicals from the parse).
4. Test-first sequencing (user-directed): characterization tests for the current parse
choice behaviour (prefix/deny/reserve under -choicerestricted 0, normalization) and the
punk::ns cmdinfo/cmd_traverse docid flow land BEFORE this goal's implementation, with
the parse-vs-traverse divergence pinned as a GAP test (fossilmove pattern). This goal
flips those GAP pins to the fixed behaviour.
## Alternatives considered
- Prefix mechanics alone (no new feature) - covers env|environment and term|terminal but
cannot fold topics|help or unify console/terminal display; rejected as the full answer,
retained as the interim option for punk::help if the feature is deferred.
- Synthesising alias folding in punk::help only (custom display, registry-side resolution)
- rejected: the duplication belongs to every ensemble-style argdoc with alias words, and
cmd_traverse would still diverge; solve it in punk::args once.
- Documenting subcommand aliases via duplicate -choices entries with shared -choicelabels
(status quo) - works but scales the display linearly with alias count and lets labels
drift; this is the shortcoming that motivated the goal.
## Notes
- Session probe scripts (scratchpad, 2026-07-08): choicecollapse.tcl (parse behaviour
table for the deny/reserve/prefix recipe), choicedisplay.tcl (usage rendering of the
collapsed def, minimal-prefix highlighting).
- cmd_traverse divergence site: src/modules/punk/ns-999999.0a1.0.tm ~5157 (subcommand walk
`tcl::prefix::match` over allchoices; consults -choiceprefix at ~5162, never the
deny/reserve lists).
- Parser reservelist/denylist semantics doc: 'i punk::args::define' -choice* section
(clarified 2026-07-08 alongside this goal's creation).
- Related: the punk::help topic registry (punk module 0.2.0) is the intended first
consumer; the possible future punk::config-declared subshell topic sets (not yet a
goal) would inherit the folding for free.

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

@ -871,19 +871,46 @@ tcl::namespace::eval punk::args {
-choicerestricted <bool> -choicerestricted <bool>
Whether values not specified in -choices or -choicegroups are Whether values not specified in -choices or -choicegroups are
allowed. Defaults to true. allowed. Defaults to true.
When false, an input that matches no choice - including an
ambiguous prefix, a prefix of a -choiceprefixdenylist entry,
or a -choiceprefixreservelist word - is accepted unchanged as
an ordinary value (subject to -type validation) instead of
raising an error. This passthrough is the pattern for
arguments that mix a known choice set with free-form values
(e.g. a help topic word falling through to command lookup).
-choiceprefix <bool> -choiceprefix <bool>
This specifies whether unique prefixes are able to be used This specifies whether unique prefixes are able to be used
instead of the complete string. This is calculated using instead of the complete string. This is calculated using
tcl::prefix::match - and will display in the autogenerated tcl::prefix::match - and will display in the autogenerated
usage output. Defaults to true. usage output. Defaults to true.
A matching prefix is normalized: the parse result contains
the full choice string, never the typed prefix - so callers
switch on canonical choice values only. The usage display
highlights each choice's minimal accepted prefix (a choice
in -choiceprefixdenylist displays fully highlighted - the
whole word is required).
-choiceprefixdenylist {<choices>} -choiceprefixdenylist {<choices>}
These choices should match exactly a choice entry in one of These choices should match exactly a choice entry in one of
the settings -choices or -choicegroups. the settings -choices or -choicegroups.
These will still be used in prefix calculation - but the full These will still be used in prefix calculation - but the full
choice argument must be entered to select the choice. choice argument must be entered to select the choice.
A shorter prefix of a denied choice is an error when
-choicerestricted is true, and passes through as an ordinary
(non-choice) value when -choicerestricted is false - use this
to keep short words available for other purposes (e.g. with
'help' denied, h/he/hel remain free-form values).
-choiceprefixreservelist {<choices>} -choiceprefixreservelist {<choices>}
These choices are additional values used in prefix calculation. These choices are additional values used in prefix calculation.
The values will not be added to the list of available choices. The values will not be added to the list of available choices.
A reserved word never matches: entered exactly it is an error
when -choicerestricted is true and passes through when false,
as do any shorter words it shadows into ambiguity. This gives
per-choice control of the minimum accepted prefix by reserving
phantom entries: e.g. -choices {environment}
-choiceprefixreservelist {en} means e/en do not match while
env, envi, ... environment all normalize to environment.
(To require a longer minimum, reserve each shorter form:
reserving {en env} raises the minimum to envi.)
-choicegroups {<dict>} -choicegroups {<dict>}
Generally this would be used instead of -choices to allow Generally this would be used instead of -choices to allow
usage display of choices grouped by some name (or the empty usage display of choices grouped by some name (or the empty

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

@ -1,3 +1,4 @@
0.2.1 0.2.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.2.2 - documentation-only: define doc clarifies -choicerestricted 0 passthrough (non-matching/ambiguous/denied/reserved words accepted as ordinary values), -choiceprefix result normalization to the canonical choice + minimal-prefix display highlighting, -choiceprefixdenylist behaviour for shorter prefixes in both restricted modes, and -choiceprefixreservelist phantom-entry idiom for per-choice minimum-prefix control

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

@ -247,6 +247,111 @@ namespace eval ::testspace {
{leftright {heavy x}}\ {leftright {heavy x}}\
{leftright {arc heavy}}\ {leftright {arc heavy}}\
] ]
# ---- unrestricted (-choicerestricted 0) interplay with prefix/deny/reserve ----
# Characterization added 2026-07-08 ahead of G-040 (choice aliasing).
# Key properties pinned here:
# - a matching prefix is NORMALIZED to the full (canonical) choice in the parse result
# - non-matching words (unknown, ambiguous prefix, denied prefix, reserved word) pass
# through unchanged instead of erroring - the pattern used by ::punk::help's topic
# leader where unrecognised words fall through to command lookup
test choiceprefix_unrestricted_normalizes_to_canonical {prefix input normalized to the full choice in the parse result when -choicerestricted 0}\
-setup $common -body {
#unique prefix -> canonical choice in result
set argd [punk::args::parse {env} withdef @values {topic -choices {topics environment console} -choiceprefix 1 -choicerestricted 0}]
lappend result [dict get $argd values]
#unknown word passes through unchanged
set argd [punk::args::parse {lindex} withdef @values {topic -choices {topics environment console} -choiceprefix 1 -choicerestricted 0}]
lappend result [dict get $argd values]
#ambiguous prefix passes through unchanged (no error when unrestricted)
set argd [punk::args::parse {t} withdef @values {topic -choices {topics tcl environment} -choiceprefix 1 -choicerestricted 0}]
lappend result [dict get $argd values]
}\
-cleanup {
}\
-result [list\
{topic environment}\
{topic lindex}\
{topic t}\
]
test choiceprefix_denylist_unrestricted_passthrough {denied prefix falls through as an ordinary value when -choicerestricted 0}\
-setup $common -body {
set docid ::testspace::choiceprefix_denylist_unrestricted_passthrough
punk::args::define [list @id -id $docid] @values {action -choices {delete describe} -choiceprefixdenylist {delete} -choicerestricted 0}
#denied prefix -> passthrough, not an error and not a match
set argd [punk::args::parse {del} withid $docid]
lappend result [dict get $argd values]
#full denied word still matches
set argd [punk::args::parse {delete} withid $docid]
lappend result [dict get $argd values]
#undenied choice still prefix-matches and normalizes
set argd [punk::args::parse {des} withid $docid]
lappend result [dict get $argd values]
}\
-cleanup {
punk::args::undefine $docid 1
}\
-result [list\
{action del}\
{action delete}\
{action describe}\
]
test choiceprefix_reservelist_unrestricted_passthrough {reserved word and shorter prefixes fall through when -choicerestricted 0; longer prefixes normalize}\
-setup $common -body {
set docid ::testspace::choiceprefix_reservelist_unrestricted_passthrough
punk::args::define [list @id -id $docid] @values {state -choices {environment} -choiceprefixreservelist {en} -choicerestricted 0}
#shorter than the reserved phantom: ambiguous -> passthrough
set argd [punk::args::parse {e} withid $docid]
lappend result [dict get $argd values]
#the reserved word itself: not a selectable choice -> passthrough
set argd [punk::args::parse {en} withid $docid]
lappend result [dict get $argd values]
#beyond the phantom: unique prefix -> canonical
set argd [punk::args::parse {env} withid $docid]
lappend result [dict get $argd values]
set argd [punk::args::parse {environment} withid $docid]
lappend result [dict get $argd values]
}\
-cleanup {
punk::args::undefine $docid 1
}\
-result [list\
{state e}\
{state en}\
{state environment}\
{state environment}\
]
test choiceprefix_collapse_recipe_multi_leader {combined prefix+deny+reserve on an unrestricted -multiple leader (the punk::help topic pattern)}\
-setup $common -body {
set docid ::testspace::choiceprefix_collapse_recipe_multi_leader
punk::args::define [list @id -id $docid] {@leaders -min 0 -max -1} {topic -optional 1 -multiple 1 -type string -choicerestricted 0 -choiceprefix 1 -choiceprefixdenylist {help} -choiceprefixreservelist {en ter} -choices {topics help tcl environment console terminal}} {@values -min 0 -max 0}
foreach word {env environment en term ter help hel to lindex} {
set argd [punk::args::parse [list $word] withid $docid]
lappend result [dict get [dict get $argd leaders] topic]
}
set result
}\
-cleanup {
punk::args::undefine $docid 1
}\
-result [list\
environment\
environment\
en\
terminal\
ter\
help\
hel\
topics\
lindex\
]
} }
tcltest::cleanupTests ;#needed to produce test summary line. tcltest::cleanupTests ;#needed to produce test summary line.

243
src/tests/modules/punk/ns/testsuites/ns/cmdflow.test

@ -0,0 +1,243 @@
package require tcltest
package require punk::args
package require punk::ns
#Tests for the command help/doc-lookup flow: punk::ns::cmdwhich -> cmdinfo -> cmd_traverse
#docid resolution, including punk::args choiceinfo subhelp mapping and the -choiceprefix /
#-choiceprefixdenylist / -choiceprefixreservelist handling of subcommand words.
#Characterization added 2026-07-08 ahead of G-040 (choice aliasing + parse/doc-lookup parity):
#tests marked GAP pin the CURRENT divergence between punk::args::parse and the cmd_traverse
#doc walk (deny/reserve lists honoured by parse, ignored by the walk). G-040 flips those pins.
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
#--- fixtures: commands + argdocs exercised by the flow tests ---------------------------
proc flowcmd {args} {}
punk::args::define {
@id -id ::testspace::flowcmd
@cmd -name testspace::flowcmd -summary "flowcmd summary" -help "flowcmd help"
@opts
-flag -type none
@values -min 0 -max -1
item -type string -optional 1 -multiple 1
}
interp alias {} ::testspace::flowalias {} ::testspace::flowcmd
#documented parent with subhelp choiceinfo mapping subcommand words to child docids
proc flowparent {args} {}
punk::args::define {
@id -id ::testspace::flowparent
@cmd -name testspace::flowparent -summary "parent" -help "parent"
@leaders -min 1 -max 1
subcmd -choices {alpha beta} -choiceinfo {
alpha {{doctype punkargs} {subhelp ::testspace::flowsub_alpha}}
beta {{doctype punkargs} {subhelp ::testspace::flowsub_beta}}
}
@values -min 0 -max 0
}
punk::args::define {
@id -id ::testspace::flowsub_alpha
@cmd -name "testspace::flowparent alpha" -summary "alpha subcommand" -help "alpha subcommand"
@values -min 0 -max 0
}
punk::args::define {
@id -id ::testspace::flowsub_beta
@cmd -name "testspace::flowparent beta" -summary "beta subcommand" -help "beta subcommand"
@values -min 0 -max 0
}
#as flowparent but prefixes disallowed on the subcommand word
proc flownoprefix {args} {}
punk::args::define {
@id -id ::testspace::flownoprefix
@cmd -name testspace::flownoprefix -summary "parent noprefix" -help "parent noprefix"
@leaders -min 1 -max 1
subcmd -choiceprefix 0 -choices {alpha beta} -choiceinfo {
alpha {{doctype punkargs} {subhelp ::testspace::flowsub_alpha}}
beta {{doctype punkargs} {subhelp ::testspace::flowsub_beta}}
}
@values -min 0 -max 0
}
#as flowparent but 'alpha' must be typed in full (denylist)
proc flowdeny {args} {}
punk::args::define {
@id -id ::testspace::flowdeny
@cmd -name testspace::flowdeny -summary "parent deny" -help "parent deny"
@leaders -min 1 -max 1
subcmd -choices {alpha beta} -choiceprefixdenylist {alpha} -choiceinfo {
alpha {{doctype punkargs} {subhelp ::testspace::flowsub_alpha}}
beta {{doctype punkargs} {subhelp ::testspace::flowsub_beta}}
}
@values -min 0 -max 0
}
#reserved phantom 'en' raises environment's minimum accepted prefix to 'env'
proc flowreserve {args} {}
punk::args::define {
@id -id ::testspace::flowreserve
@cmd -name testspace::flowreserve -summary "parent reserve" -help "parent reserve"
@leaders -min 1 -max 1
subcmd -choices {environment} -choiceprefixreservelist {en} -choiceinfo {
environment {{doctype punkargs} {subhelp ::testspace::flowsub_env}}
}
@values -min 0 -max 0
}
punk::args::define {
@id -id ::testspace::flowsub_env
@cmd -name "testspace::flowreserve environment" -summary "environment subcommand" -help "environment subcommand"
@values -min 0 -max 0
}
#undocumented command whose subcommand is documented via a space-delimited id
proc flowgap {args} {}
punk::args::define {
@id -id "::testspace::flowgap deep"
@cmd -name "testspace::flowgap deep" -summary "deep subcommand of undocumented parent" -help "deep subcommand"
@values -min 0 -max 0
}
#--- cmdwhich ---------------------------------------------------------------------------
test cmdwhich_proc_alias_notfound {cmdwhich whichtype/origin for proc, alias and missing command}\
-setup $common -body {
set w [punk::ns::cmdwhich ::testspace::flowcmd]
lappend result [dict get $w whichtype]
set w [punk::ns::cmdwhich ::testspace::flowalias]
lappend result [dict get $w whichtype] [dict get $w origin]
set w [punk::ns::cmdwhich ::testspace::no_such_cmd]
lappend result [dict get $w whichtype]
}\
-cleanup {
}\
-result [list proc alias ::testspace::flowcmd notfound]
#--- cmdinfo docid resolution -----------------------------------------------------------
test cmdinfo_docid_direct {cmdinfo returns the explicit punk::args id for a documented proc}\
-setup $common -body {
set cinfo [punk::ns::cmdinfo ::testspace::flowcmd]
lappend result [dict get $cinfo docid] [dict get $cinfo args_remaining]
}\
-cleanup {
}\
-result [list ::testspace::flowcmd {}]
test cmdinfo_docid_via_alias {cmdinfo resolves an alias to its target's docid}\
-setup $common -body {
set cinfo [punk::ns::cmdinfo ::testspace::flowalias]
lappend result [dict get $cinfo docid]
}\
-cleanup {
}\
-result [list ::testspace::flowcmd]
test cmdinfo_subhelp_choiceinfo {a subcommand word maps to its subhelp docid; unique prefix accepted when -choiceprefix 1 (default)}\
-setup $common -body {
set cinfo [punk::ns::cmdinfo ::testspace::flowparent alpha]
lappend result [dict get $cinfo docid]
set cinfo [punk::ns::cmdinfo ::testspace::flowparent beta]
lappend result [dict get $cinfo docid]
#unique prefix of a choice resolves to the same subhelp docid
set cinfo [punk::ns::cmdinfo ::testspace::flowparent al]
lappend result [dict get $cinfo docid]
}\
-cleanup {
}\
-result [list ::testspace::flowsub_alpha ::testspace::flowsub_beta ::testspace::flowsub_alpha]
test cmdinfo_unknown_subword_returns_parent {a word matching no choice leaves docid at the parent with the word in args_remaining}\
-setup $common -body {
set cinfo [punk::ns::cmdinfo ::testspace::flowparent zzz]
lappend result [dict get $cinfo docid] [dict get $cinfo args_remaining]
}\
-cleanup {
}\
-result [list ::testspace::flowparent zzz]
test cmdinfo_choiceprefix0_requires_exact {with -choiceprefix 0 a prefix is not accepted by the doc walk - docid stays at the parent}\
-setup $common -body {
set cinfo [punk::ns::cmdinfo ::testspace::flownoprefix alpha]
lappend result [dict get $cinfo docid]
set cinfo [punk::ns::cmdinfo ::testspace::flownoprefix al]
lappend result [dict get $cinfo docid] [dict get $cinfo args_remaining]
}\
-cleanup {
}\
-result [list ::testspace::flowsub_alpha ::testspace::flownoprefix al]
test cmdinfo_spaceform_id {a space-delimited id documents a subcommand of an undocumented parent}\
-setup $common -body {
set cinfo [punk::ns::cmdinfo ::testspace::flowgap deep]
lappend result [dict get $cinfo docid]
}\
-cleanup {
}\
-result [list {::testspace::flowgap deep}]
#--- parse vs doc-walk parity (GAP pins pending G-040) -----------------------------------
#punk::args::parse honours -choiceprefixdenylist/-choiceprefixreservelist; the cmd_traverse
#doc walk (punk::ns) consults only -choiceprefix and ignores both lists. These tests pin
#the CURRENT divergent behaviour; G-040's parity work flips them to agreement.
test cmdinfo_parity_denylist_GAP {GAP (G-040): parse rejects a denied prefix but the doc walk still resolves it}\
-setup $common -body {
#parse side: denied prefix is an error (restricted choices)
if {[catch {punk::args::parse {al} withid ::testspace::flowdeny}]} {
lappend result parse-rejects-denied-prefix
} else {
lappend result parse-UNEXPECTEDLY-accepted-denied-prefix
}
#full word accepted by parse
set argd [punk::args::parse {alpha} withid ::testspace::flowdeny]
lappend result [dict get [dict get $argd leaders] subcmd]
#doc-walk side: the same denied prefix currently resolves to the subhelp docid
set cinfo [punk::ns::cmdinfo ::testspace::flowdeny al]
lappend result [dict get $cinfo docid]
}\
-cleanup {
}\
-result [list parse-rejects-denied-prefix alpha ::testspace::flowsub_alpha]
test cmdinfo_parity_reservelist_GAP {GAP (G-040): parse blocks a reserved prefix but the doc walk still resolves it}\
-setup $common -body {
#parse side: reserved word blocks itself and shorter prefixes
if {[catch {punk::args::parse {en} withid ::testspace::flowreserve}]} {
lappend result parse-rejects-reserved-word
} else {
lappend result parse-UNEXPECTEDLY-accepted-reserved-word
}
#parse side: a prefix beyond the reserved phantom resolves
set argd [punk::args::parse {env} withid ::testspace::flowreserve]
lappend result [dict get [dict get $argd leaders] subcmd]
#doc-walk side agreement for the beyond-phantom prefix
set cinfo [punk::ns::cmdinfo ::testspace::flowreserve env]
lappend result [dict get $cinfo docid]
#doc-walk side: the reserved word currently resolves anyway
set cinfo [punk::ns::cmdinfo ::testspace::flowreserve en]
lappend result [dict get $cinfo docid]
}\
-cleanup {
}\
-result [list parse-rejects-reserved-word environment ::testspace::flowsub_env ::testspace::flowsub_env]
#--- cmdhelp smoke ------------------------------------------------------------------------
test cmdhelp_string_smoke {cmdhelp -return string renders usage for a documented proc and a subhelp subcommand}\
-setup $common -body {
set out [punk::ns::cmdhelp -return string ::testspace::flowcmd]
lappend result [string match "*flowcmd summary*" $out]
set out [punk::ns::cmdhelp -return string ::testspace::flowparent alpha]
lappend result [string match "*alpha subcommand*" $out]
}\
-cleanup {
}\
-result [list 1 1]
}
tcltest::cleanupTests ;#needed to produce test summary line.
Loading…
Cancel
Save