Browse Source
Five user-approved proposed goals with detail files, grounded in the 2026-07-10
characterization work (GAP pins in usagemarking.test/cmdhelp.test map to these):
- G-049 punk::args parse-status data model with machine-parsable cmdhelp returns:
documented per-argument status structure consumed by both arg_error renderers,
cmdhelp -return dict; fixes badarg coverage (type/allocation failures), scheme
honoured on failure path, caller attribution, and the stateful shared
colour-array scheme handling (nocolour fallthrough + dash-spelling leak).
- G-050 synopsis argument-validity marking and status-aware returns: 's' gains the
goodarg/badarg marking via the G-049 structure; replaces the curried-alias
excess-args length arithmetic with a parse against the resolved definition.
- G-051 cmdinfo truthful cmdtype for doc-only pseudo-commands + space-form docid
prefix parity via the shared choiceword_match resolver ('i string is tr').
- G-052 TclOO method-level autodef: undocumented methods get an (autodef) from the
introspected parameter list instead of the class-summary-only fallback.
- G-053 punk::args range-valued -multiple: {min max} occurrence ranges alongside
the legacy booleans, expressing "at most once, repeat is an error" without a
separate duplicates flag (mirrors the -choicemultiple precedent; motivated by
the runtests -include-paths last-wins incident).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
6 changed files with 329 additions and 0 deletions
@ -0,0 +1,80 @@ |
|||||||
|
# G-049 punk::args parse-status data model with machine-parsable cmdhelp returns |
||||||
|
|
||||||
|
Status: proposed |
||||||
|
Scope: src/modules/punk/args-999999.0a1.0.tm (arg_error, parse error dispatch, colour-scheme handling), src/modules/punk/ns-999999.0a1.0.tm (cmdhelp), src/tests/modules/punk/args/testsuites/args/usagemarking.test, src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test |
||||||
|
Acceptance: see GOALS.md index entry (canonical). |
||||||
|
|
||||||
|
## Context |
||||||
|
|
||||||
|
The interactive usage display ('i <cmd> <args...>' via punk::ns::cmdhelp) marks supplied |
||||||
|
arguments for validity: a fully-valid argument set renders with the info scheme (grey |
||||||
|
border) and received arguments struck through in green; a failed parse renders with the |
||||||
|
error scheme (yellow border) and - for some failure classes - the offending argument in |
||||||
|
red. This behaviour was characterized 2026-07-10 in usagemarking.test (arg_error |
||||||
|
primitives) and cmdhelp.test (integration), and several defects were pinned as GAP tests: |
||||||
|
|
||||||
|
- **badarg coverage is uneven**: only `choiceviolation` failures populate `-badarg` in the |
||||||
|
validation error dispatch, so only choice violations get the red row marking. A value |
||||||
|
failing its `-type` check surfaces as `missingrequiredvalue` (the value is never |
||||||
|
assigned, so allocation fails first) with NO badarg - 'i cmd v1 x x' with int-typed |
||||||
|
values names the failing argument in the message but marks nothing. |
||||||
|
(cmdhelp_GAP_no_badarg_marking_for_failed_typed_value) |
||||||
|
- **explicit -scheme is ignored on the failure path**: cmdhelp passes -scheme through to |
||||||
|
arg_error only on the success path and the tableobject failure branch; for |
||||||
|
-return table/string failures it returns the parse error message rendered with the |
||||||
|
default error scheme. (cmdhelp_GAP_explicit_scheme_ignored_on_failure) |
||||||
|
- **caller attribution leaks internals**: at top call depth (the interactive case) the |
||||||
|
failure message names cmdhelp's own parse call as raw source text |
||||||
|
(`punk::args::parse $args_remaining -cache 0 ...`) instead of the queried command; |
||||||
|
from nested contexts it attributes correctly. (cmdhelp_GAP_errormsg_leaks_internal_source) |
||||||
|
- **scheme colour handling is stateful**: arg_error's scheme normalization switch matches |
||||||
|
"", "-nocolor", "-nocolour" (with dash) for the nocolour arm, so the DOCUMENTED choice |
||||||
|
value `nocolour` falls through to the "na" scheme - no override merge - and renders |
||||||
|
with whatever colours the most recent info/error render left in the shared |
||||||
|
arg_error_CLR array. The dash spelling does merge, and because scheme merges write the |
||||||
|
shared array in place, its strike-only goodarg/badarg overrides then leak into all |
||||||
|
subsequent renders until a colour on/off flip forces an array reload. |
||||||
|
(usagemarking_GAP_scheme_nocolour_renders_with_leftover_colours, |
||||||
|
usagemarking_GAP_dash_nocolour_leaks_into_shared_array, |
||||||
|
usagemarking_GAP_dash_nocolour_leak_affects_later_info_render) |
||||||
|
|
||||||
|
Meanwhile the good/bad/scheme information itself exists only as transient locals inside |
||||||
|
arg_error (goodargs list derived from -parsedargs 'received', badarg string, scheme name) |
||||||
|
and as colour in the output. Nothing machine-readable reports "these supplied words |
||||||
|
validated, this one failed, for this reason". cmdhelp's -return choices are |
||||||
|
string/table/tableobject only. |
||||||
|
|
||||||
|
## Approach |
||||||
|
|
||||||
|
1. Define a parse-status structure (dict) produced from a parse attempt against a |
||||||
|
definition: per-argument entries (name, received value/position, status |
||||||
|
ok|bad|unparsed, failure class when bad) plus overall fields (scheme, message, form). |
||||||
|
Success and failure of punk::args::parse both map onto it (failure via the PUNKARGS |
||||||
|
VALIDATION errorcode payload - which should carry -badarg for type/allocation |
||||||
|
failures, not just choice violations). |
||||||
|
2. Make arg_error's table and string renderers consume the structure (replacing the |
||||||
|
goodargs/badarg locals), resolving colours per-render from the scheme arrays instead |
||||||
|
of merging into the shared array - fixing the nocolour choice and the leakage as a |
||||||
|
side effect. |
||||||
|
3. Add `-return dict` to punk::ns::cmdhelp returning the structure alongside resolution |
||||||
|
info (origin/docid/cmdtype from cmdinfo), and pass the explicit -scheme through the |
||||||
|
failure path. |
||||||
|
4. Fix caller attribution for the usage-display path (the message should name the |
||||||
|
queried command; related to the G-046 'Bad number of leading values...' rewording |
||||||
|
acceptance item - coordinate, don't duplicate). |
||||||
|
|
||||||
|
## Notes |
||||||
|
|
||||||
|
- G-050 (synopsis marking) and G-044 (repl completion/hinting) consume this structure; |
||||||
|
land the structure first. |
||||||
|
- Characterization baseline: usagemarking.test (14 tests) and cmdhelp.test (19 tests), |
||||||
|
both green on Tcl 9.0.3 and 8.7 as of 2026-07-10. GAP-prefixed tests pin the defects |
||||||
|
above and flip with this goal. |
||||||
|
- Preserved surface includes the goodchoice highlighting (reverse video on the choice |
||||||
|
word matching an argument's value-in-effect - supplied choice for received arguments, |
||||||
|
default choice for arguments the parse filled from -default; only when a parse result |
||||||
|
is supplied). The status structure should carry value-in-effect per argument so the |
||||||
|
renderers keep this without reaching back into the raw parse result. |
||||||
|
- The scheme discrimination trick used by the tests (info border SGR params are the only |
||||||
|
scheme-unique style; error border shares params with info title) is itself a hint that |
||||||
|
scheme styling deserves a small documented palette rather than ad-hoc per-key overrides. |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
# G-050 punk::ns::synopsis argument-validity marking and status-aware returns |
||||||
|
|
||||||
|
Status: proposed |
||||||
|
Scope: src/modules/punk/ns-999999.0a1.0.tm (synopsis), src/modules/punk/args-999999.0a1.0.tm (synopsis renderer), src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test (synopsis pins) |
||||||
|
Acceptance: see GOALS.md index entry (canonical). |
||||||
|
|
||||||
|
## Context |
||||||
|
|
||||||
|
'i <cmd> <args...>' (cmdhelp) marks supplied argument words for validity; 's <cmd> |
||||||
|
<args...>' (synopsis) does not - its output is identical whether the trailing words are |
||||||
|
valid, invalid or absent (pinned 2026-07-10: synopsis_GAP_no_argument_validity_marking). |
||||||
|
The user-facing intent is parity: the synopsis line(s) should show which supplied words |
||||||
|
validated (goodarg style) and where the first problem is (badarg style), with the same |
||||||
|
info/error scheme signalling as the usage table. |
||||||
|
|
||||||
|
Separately, synopsis's handling of curried aliases is length-arithmetic based: the |
||||||
|
`excess` computation (ns-999999.0a1.0.tm, "This isn't quite right.. e.g see: s pse" |
||||||
|
REVIEW comment) counts args_remaining vs supplied words and lreplaces that many leading |
||||||
|
synopsis words with resolved_args. For a curried alias the substituted command renders |
||||||
|
as the braced alias target (e.g `{::ns::realcmd curriedarg} ...`) rather than the alias |
||||||
|
name (pinned: synopsis_curried_alias_shows_braced_target). |
||||||
|
|
||||||
|
## Approach |
||||||
|
|
||||||
|
Consume the G-049 parse-status structure: parse the trailing words against the resolved |
||||||
|
definition (same docid resolution as cmdhelp via cmdinfo), then render the synopsis with |
||||||
|
per-word marking derived from the status, and expose the status in the dict return. |
||||||
|
Replacing the excess-args arithmetic with an actual parse against the resolved definition |
||||||
|
is the same change - the parse knows how many words the definition consumed and how the |
||||||
|
curried words map onto leaders. |
||||||
|
|
||||||
|
Also worth addressing while in there: the existing in-source note that treating the |
||||||
|
synopsis line as a Tcl list for lreplace is risky with ANSI content ("todo - consider |
||||||
|
always using 'punk::args::synopsis -return dict' and operating on that list to rebuild |
||||||
|
string"). |
||||||
|
|
||||||
|
## Notes |
||||||
|
|
||||||
|
- Depends on G-049 (status structure). G-044's hint display is a future consumer of the |
||||||
|
same API - keep the marking function reusable outside synopsis/cmdhelp rendering. |
||||||
|
- Existing string-shape tests: args testsuite synopsis.test and the installed |
||||||
|
test::punk::args synopsis tests ("must be no leading space" convention) - marking must |
||||||
|
be ANSI-only so ansistripped output stays stable, or those tests get a documented |
||||||
|
update. |
||||||
@ -0,0 +1,54 @@ |
|||||||
|
# G-051 cmdinfo truthful cmdtype for doc-only pseudo-commands and space-form docid prefix parity |
||||||
|
|
||||||
|
Status: proposed |
||||||
|
Scope: src/modules/punk/ns-999999.0a1.0.tm (cmdinfo, cmd_traverse), src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test, src/tests/modules/punk/ns/testsuites/ns/cmdflow.test |
||||||
|
Acceptance: see GOALS.md index entry (canonical). |
||||||
|
|
||||||
|
## Context |
||||||
|
|
||||||
|
Two related defects in the doc-lookup walk, characterized 2026-07-10: |
||||||
|
|
||||||
|
**Pseudo-command cmdtype.** A space-delimited punk::args id can document a "command" |
||||||
|
below the real implementation level - e.g. `::tcl::string::is true` documents a level |
||||||
|
below the `::tcl::string::is` proc (the class words are arguments, not subcommands). |
||||||
|
cmdinfo resolves such docids correctly but reports `cmdtype notfound` because cmdwhich |
||||||
|
finds no real command at the multiword path (cmdinfo's final cmdwhich is on the |
||||||
|
space-joined finalcommand). Consumers cannot distinguish "genuinely no such command" from |
||||||
|
"documentation-only level below a real command". |
||||||
|
Pins: cmdhelp_GAP_pseudo_command_cmdtype_notfound (fixture), |
||||||
|
cmdhelp_GAP_string_is_true_pseudo (real tclcore docs, constraint have_tclcoredocs). |
||||||
|
The documented-method oo case shows the same shape: `cmdinfo $obj docmeth` reports |
||||||
|
cmdtype notfound with docid "<class> docmeth" (see cmdhelp_oo_documented_method - its |
||||||
|
docid assertion holds through this goal; whether the method case adopts the new cmdtype |
||||||
|
or a method-specific one is a design decision for this goal, coordinated with G-052). |
||||||
|
|
||||||
|
**Space-form docid prefix parity.** The jump to a space-delimited child docid |
||||||
|
(cmd_traverse: `punk::args::id_exists "$origin [lindex $args $i]"`) requires the exact |
||||||
|
word. The parser accepts a unique choice prefix for the same word (G-040 landed |
||||||
|
choiceword_match parity for the choiceinfo/subhelp path, but the space-form id_exists |
||||||
|
jump happens BEFORE choice matching and never sees prefixes). Real-world effect: |
||||||
|
Tcl's `string is tr 1` works, but 'i string is tr' stays at the parent docid. |
||||||
|
Pins: cmdhelp_GAP_spaceform_docid_prefix_not_honoured (fixture), |
||||||
|
cmdhelp_GAP_string_is_prefix_not_honoured (real tclcore docs). |
||||||
|
|
||||||
|
## Approach |
||||||
|
|
||||||
|
- Introduce a distinct cmdtype value (candidate: `doconly`) returned by cmdinfo when the |
||||||
|
final resolution has an empty/notfound cmdwhich but a non-empty docid whose id maps |
||||||
|
below a real command. Audit consumers (cmdhelp, synopsis, eg, anything switching on |
||||||
|
cmdtype) - most treat docid as authoritative already, so the change should be additive. |
||||||
|
- For prefix parity: when the exact-word space-form lookup fails and the current docid's |
||||||
|
definition has a choices-bearing leader for the subcommand word, resolve the word via |
||||||
|
punk::args::choiceword_match FIRST (the shared resolver - honouring -choiceprefix, |
||||||
|
denylist, reservelist, aliases) and retry the space-form lookup with the resolved word. |
||||||
|
No second matching rule: if parse would reject the prefix, the walk must too (the G-040 |
||||||
|
parity contract, already pinned by cmdflow.test). |
||||||
|
|
||||||
|
## Notes |
||||||
|
|
||||||
|
- The cmdinfo result shape (keys origin cmdtype args_resolved args_remaining docid stack) |
||||||
|
is pinned by cmdhelp_cmdinfo_result_shape - the new cmdtype is a value change on an |
||||||
|
existing key, not a shape change. |
||||||
|
- 'string is' has a second wrinkle out of scope here: its per-class ids are generated |
||||||
|
from the parent's -choicelabels at tclcore load time; prefix parity must come from the |
||||||
|
walk, not from generating extra prefix ids. |
||||||
@ -0,0 +1,54 @@ |
|||||||
|
# G-052 TclOO method-level autodef documentation |
||||||
|
|
||||||
|
Status: proposed |
||||||
|
Scope: src/modules/punk/ns-999999.0a1.0.tm (generate_autodef oo branches, cmd_traverse), src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test |
||||||
|
Acceptance: see GOALS.md index entry (canonical). |
||||||
|
|
||||||
|
## Context |
||||||
|
|
||||||
|
For a tcl::oo object, generate_autodef builds a class-summary (autodef) definition: a |
||||||
|
`method` leader whose choices are the public methods, with choiceinfo doctype markers |
||||||
|
(objectmethod/classmethod/coremethod) and subhelp links for methods that have explicit |
||||||
|
punk::args docs (the id convention is "<location> <method>", e.g. |
||||||
|
"::punk::ansi::class::class_ansi rendertest"). A documented method therefore resolves and |
||||||
|
renders with full argument marking. |
||||||
|
|
||||||
|
An UNDOCUMENTED method does not: 'i $obj checksum' shows only the class summary with the |
||||||
|
method word highlighted, and because the class-summary definition ends with |
||||||
|
`@values -unnamed true`, ANY trailing words parse successfully - 'i $obj checksum bogus1 |
||||||
|
bogus2' renders an info-scheme (all good) class summary. Pinned 2026-07-10: |
||||||
|
cmdhelp_GAP_oo_undocumented_method_class_summary_only (cmdtype ooobject, docid |
||||||
|
(autodef)<obj>, args_remaining carries the method + bogus words, info scheme + goodarg |
||||||
|
on the method row). |
||||||
|
|
||||||
|
TclOO is fully introspectable: `info object call $obj $method` locates the |
||||||
|
implementation, and `info class definition $class $method` / `info object definition` |
||||||
|
yield the parameter list. generate_autodef already walks `info object call` for the |
||||||
|
choiceinfo markers, and already has a partial single-method arglist path (the oodef |
||||||
|
switch handling 1/2-element parameter entries, defaults, and trailing 'args') - it is |
||||||
|
just not wired up as a general method-level (autodef) generation step in the doc walk. |
||||||
|
|
||||||
|
## Approach |
||||||
|
|
||||||
|
- When cmd_traverse consumes a method word on an oo object/class and no explicit |
||||||
|
"<location> <method>" id exists, generate "(autodef)<location> <method>" from the |
||||||
|
introspected parameter list (same element mapping as the existing proc autodef: |
||||||
|
name / {name default} / trailing args -> -multiple 1 -optional 1) and resolve to it, |
||||||
|
leaving subsequent words as args_remaining for parse marking. |
||||||
|
- Location follows the same rules as the existing choiceinfo id convention (object-local |
||||||
|
methods keyed by the object, class methods by the class, inherited by the defining |
||||||
|
class) so explicit docs and autodefs share the lookup key. |
||||||
|
- Decide and document the class-summary `-choiceprefix 0` question ("methods must be |
||||||
|
specified in full always? - review" at the method choicelist): whatever the answer, |
||||||
|
parse and doc walk must agree (G-040 parity). |
||||||
|
- Constructor/'new' on classes: characterize at minimum; generating an autodef from |
||||||
|
`info class constructor` is the natural extension - explicitly defer if not landed. |
||||||
|
|
||||||
|
## Notes |
||||||
|
|
||||||
|
- punk::ansi::class::class_ansi (rendertest / render_to_input_line) is the documented- |
||||||
|
method reference pattern; the fixture in cmdhelp.test mirrors it (HelpClass docmeth |
||||||
|
documented, plainmeth undocumented). |
||||||
|
- Filters, private methods and `unknown` handlers are marked in choiceinfo today but out |
||||||
|
of scope for autodef generation. |
||||||
|
- Coordinate the cmdtype reported for method resolutions with G-051's doconly decision. |
||||||
@ -0,0 +1,67 @@ |
|||||||
|
# G-053 punk::args range-valued -multiple: occurrence arity with strict duplicate handling |
||||||
|
|
||||||
|
Status: proposed |
||||||
|
Scope: src/modules/punk/args-999999.0a1.0.tm (spec compiler, parse, arg_error/synopsis renderers), src/tests/modules/punk/args/testsuites/args/ |
||||||
|
Acceptance: see GOALS.md index entry (canonical). |
||||||
|
|
||||||
|
## Context |
||||||
|
|
||||||
|
Boolean `-multiple` conflates three axes: |
||||||
|
1. **occurrence arity** - how many times the argument may be supplied |
||||||
|
2. **overflow policy** - what happens beyond the limit: silent replace (legacy |
||||||
|
`-multiple 0` last-wins) or error |
||||||
|
3. **value shape** - scalar vs list-of-occurrences |
||||||
|
|
||||||
|
The motivating incident (2026-07-10): runtests.tcl's `-include-paths` was a |
||||||
|
non-multiple list option, so repeated `-include-paths` flags silently last-won - |
||||||
|
quietly narrowing a test run while reporting green, and defeating even a recorded |
||||||
|
memory note about the gotcha. Repeatable accumulating flags are the dominant |
||||||
|
convention in shell-facing CLIs (gcc -I, curl -H, rsync --exclude), so this misuse |
||||||
|
recurs. runtests was fixed by making that option `-multiple 1`, but the general |
||||||
|
fix for "repeat should be an error" has no expression today. |
||||||
|
|
||||||
|
A separate `-duplicates deny|replace` policy flag was considered and rejected: with |
||||||
|
`-multiple 1` a duplicates policy is meaningless (duplicates ARE the collected |
||||||
|
payload), so the flag's validity would depend on another flag's setting, and it |
||||||
|
would blur into the existing `-multipleunique`/`-multipleuniqueset` territory. |
||||||
|
|
||||||
|
punk::args already has the shape precedent in its own vocabulary: `-choicemultiple` |
||||||
|
is a `{min max}` pair, not a boolean. |
||||||
|
|
||||||
|
## Approach |
||||||
|
|
||||||
|
`-multiple` accepts a boolean (legacy, semantics preserved exactly) or a |
||||||
|
`{min max}` range (max -1 = unbounded): |
||||||
|
|
||||||
|
- `-multiple 0` - legacy: single-valued, repeats silently replace (last wins), |
||||||
|
scalar shape. Unchanged - this preserves the prepend-defaults override idiom |
||||||
|
`punk::args::parse [list -flag default {*}$userargs]`. |
||||||
|
- `-multiple 1` - legacy: unbounded collection, list shape. Unchanged. |
||||||
|
- `-multiple {0 1}` - at most once; a second occurrence is a parse (arity) error. |
||||||
|
This is the "duplicates deny" case. |
||||||
|
- `-multiple {2 4}`, `{1 -1}` etc - bounded/lower-bounded repetition, collected. |
||||||
|
|
||||||
|
Design decisions to settle (record here when made): |
||||||
|
- **Value shape for range forms**: lean - `max == 1` forms stay scalar (they are |
||||||
|
strict single-valued variants of legacy 0); `max > 1` or `-1` yield the |
||||||
|
occurrence list. Whatever is chosen must be pinned by the characterization tests. |
||||||
|
- **-optional vs range-min reconciliation**: lean - `-optional` governs presence, |
||||||
|
the range governs occurrence count when present; contradictory combinations |
||||||
|
(e.g. range min >= 1 with -optional 1 intended as "required") rejected at define |
||||||
|
time with a clear message. |
||||||
|
- **Hot-path canonicalization**: `-multiple` is truth-tested in many parse/render |
||||||
|
sites; a raw `"0 1"` value would fail expr boolean coercion. The spec compiler |
||||||
|
should canonicalize once into internal min/max/policy fields (alongside the |
||||||
|
existing ARG_INFO/ARG_CHECKS structures) so runtime checks stay cheap. |
||||||
|
|
||||||
|
Display benefits: the usage table's Multi column and the synopsis `?arg...?` |
||||||
|
rendering gain meaningful bounded-repetition forms (e.g. "0-1", "2-4"). |
||||||
|
|
||||||
|
## Notes |
||||||
|
|
||||||
|
- `-multipleunique` / `-multipleuniqueset` remain the uniqueness knobs and only |
||||||
|
make sense for max > 1; they compose with ranges unchanged. |
||||||
|
- Related: G-045 (authoring ergonomics), G-046 (parse-time performance - the |
||||||
|
canonicalization must not regress the hot path). |
||||||
|
- 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. |
||||||
Loading…
Reference in new issue