Browse Source

G-149 achieved: punk::args configurable multi-form synopsis hint labels (punk::args 0.17.0)

The per-form hint labels rendered beside a multi-form definition's
synopsis lines in the usage/arg_error table (hard-coded ' i -form N
...' previously) are now a template substituted per form: %formindex%
is replaced with the form's 0-based ordinal, %formname% with its name,
and an empty template suppresses the labels entirely (the cell keeps
just the Synopsis: label, as single-form renders always have). Both
acceptance arms landed. Definition side: new '@cmd -formhint
<template>' key - rides the existing arbitrary-key cmd_info merge (no
resolver change), round-trips through resolved_def, not display
-deferred; documented in the define -help directive-options. Caller
side: new -formhint option on arg_error (manual parser + PUNKARGS
block) pulled into usage via its existing resolved_def opts pull;
supplied-empty (suppress) is distinct from not-supplied (the option
declares no -default, so an unsupplied usage call passes nothing
through). Precedence: caller > definition > built-in default, whose
template substitutes to the exact prior bytes. Motivation (G-143
origin): script-level (script) ids - make.tcl's - are unreachable from
any repl, so the repl-oriented 'i' hint was not actionable there.
Scope decisions pinned: single-form renders never carry hint labels;
the -return string renderer has no hint column and is unchanged.

Byte-identity for unconfigured definitions proven by pre/post capture
diff of 'punk::args::usage ::punk::args::parse' (identical bytes,
through the usage pass-through path). Tests: new formhint.test (9
tests - default labels, directive/caller/suppression matrix, parse
-error path, single-form + string-renderer scope); full punk/args
subtree 302/0-fail on BOTH tclsh90 9.0.3 and the punk86 kit (8.6);
punk/ns subtree 115/115 (cmdhelp consumer). make.tcl help untouched
until promotion (maketclhelp.test still pins the bootsupport hint).

Goals: G-149 activated at user direction then flipped achieved
2026-08-05 + archived (index record newest-first, detail file to
goals/archive with Progress evidence and 1 follow-on: make.tcl
adoption after a bootsupport promotion => open). Activation-freshness
survey recorded the G-167 relationship (same renderer surface -
composition note both sides); reference sweep updated G-151/G-167
mentions; goals_lint clean, no orphaned xref pairs. punkshell 0.55.0 +
CHANGELOG. Bootsupport/vfscommon snapshots unchanged until promotion +
bake.

Claude-Session: https://claude.ai/code/session_01QgaxV27VZkmEec7oNbEVFc
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 5 days ago
parent
commit
7cdf5b2323
  1. 17
      CHANGELOG.md
  2. 4
      GOALS-archive.md
  3. 4
      GOALS.md
  4. 18
      goals/G-149-punkargs-formhint-labels.md
  5. 2
      goals/G-151-punkargs-annotated-success-render.md
  6. 14
      goals/G-167-punkargs-version-delta-availability.md
  7. 69
      goals/archive/G-149-punkargs-formhint-labels.md
  8. 2
      punkproject.toml
  9. 48
      src/modules/punk/args-999999.0a1.0.tm
  10. 3
      src/modules/punk/args-buildversion.txt
  11. 184
      src/tests/modules/punk/args/testsuites/args/formhint.test

17
CHANGELOG.md

@ -5,6 +5,23 @@ 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.55.0] - 2026-08-05
- G-149 achieved: punk::args configurable multi-form synopsis hint labels
(punk::args 0.17.0, source module). The per-form hint labels rendered
beside a multi-form definition's synopsis lines in usage/help tables
(hard-coded " i -form N ..." previously) are now a template substituted
per form: %formindex% is replaced with the form's 0-based ordinal,
%formname% with its name, and an empty template suppresses the labels
entirely. A definition supplies its own via the new '@cmd -formhint'
directive key; callers of usage/arg_error may override with the new
-formhint option. Unconfigured definitions render byte-identical to
before. Motivation: script-level (script) definitions such as make.tcl's
are unreachable from any repl, so the repl-oriented 'i' hint was not
actionable there - make.tcl adoption follows a future bootsupport
promotion. Kits pick this up at the next bootsupport/vfscommon refresh
and bake.
## [0.54.0] - 2026-08-05
- G-073 achieved: punk::args unavailable choices (punk::args 0.16.0, tclcore

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-149 [achieved 2026-08-05] punk::args: configurable multi-form synopsis hint labels → detail: goals/archive/G-149-punkargs-formhint-labels.md
Scope: src/modules/punk/args-999999.0a1.0.tm (usage/arg_error multi-form Synopsis hint column); src/tests/modules/punk/args/testsuites/args/
Acceptance: a punk::args mechanism (definition directive and/or usage/arg_error caller option) replaces the ' i -form N ...' hint labels with caller-supplied text and can suppress the column entirely; unconfigured definitions render byte-identical to today (existing synopsis/usage/arg_error pins pass unchanged); the mechanism is pinned in the args testsuite. make.tcl adopting it (e.g a 'make.tcl help tool <action>' phrasing) is recorded as consumer follow-through after a bootsupport promotion, not part of this acceptance. (Achieved with both arms: '@cmd -formhint <template>' definition key + '-formhint' caller option on arg_error/usage, %formindex%/%formname% substitution, empty template suppresses; punk::args 0.17.0, new formhint.test, verified tclsh90 + punk86.)
### G-073 [achieved 2026-08-05] punk::args unavailable choices: displayed with notes and prefix-reserving, but rejected with a tailored message → detail: goals/archive/G-073-punkargs-unavailable-choices.md
Scope: src/modules/punk/args-999999.0a1.0.tm (spec key, choiceword_match pool, choices rendering in arg_error table+string renderers, validation message); src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm ('string is' forward-class adoption + per-class virtual id); src/tests/modules/punk/args/testsuites/args/ (new suite + tclcoreparity.test exemption)
Acceptance: a definition using the new key renders unavailable entries distinguishably among the choices (mechanism decided in the work: dedicated group heading, dim/warn styling, or marker) with their -choicelabels notes, in both the table and string renderers, and synopsis choice-literal display excludes them; parse/parse_status reject an unavailable choice word - exact, or a unique prefix resolving only to it - with a message identifying it as recognised-but-unavailable rather than the generic choice error; unavailable entries participate in prefix disambiguation via choiceword_match (a prefix shared between an available and an unavailable choice is rejected as ambiguous) and are honoured identically by the punk::ns doc walk and the G-071 allocation screen (parity free via the shared resolver - no second matching rule); value-in-effect/goodarg highlighting never marks an unavailable entry; the tclcore 'string is' definition adopts the key on runtimes lacking known forward classes (dict on 8.6; the forward list is explicitly curated in the definition - unicode deliberately excluded as never-released), with a per-class virtual id generated from the static description so 'i string is dict' documents the class with its note on 8.6; the deliberate strictness divergence (real 8.6 accepts 'string is di' as digit; the model rejects it as ambiguous) is recorded in tclcoreparity.test as a user-sanctioned exemption class rather than silently special-cased, with full-word behaviour staying parity-true on both versions; definitions without the key behave unchanged (full punk::args and punk::ns suites pass).

4
GOALS.md

@ -372,10 +372,6 @@ Detail: goals/G-147-libfetch-revision-check.md
Scope: src/modules/punk/mix/ (vendor-sync command surface - discovery beside the G-065 sync); punkproject.toml or sibling vendor manifest (G-065 declaration schema as consumed); src/tests/modules/punk/mix/ (discovery testsuite with local fixture upstreams)
Detail: goals/G-148-vendor-upstream-discovery.md
### G-149 [proposed] punk::args: configurable multi-form synopsis hint labels
Scope: src/modules/punk/args-999999.0a1.0.tm (usage/arg_error multi-form Synopsis hint column); src/tests/modules/punk/args/testsuites/args/
Detail: goals/G-149-punkargs-formhint-labels.md
### G-151 [proposed] punk::args annotated success render (received-args landing report)
Scope: src/modules/punk/args-999999.0a1.0.tm (parse-result presentation); src/tests/modules/punk/args/testsuites/args/
Detail: goals/G-151-punkargs-annotated-success-render.md

18
goals/G-149-punkargs-formhint-labels.md

@ -1,18 +0,0 @@
# G-149 punk::args: configurable multi-form synopsis hint labels
Status: proposed
Scope: src/modules/punk/args-999999.0a1.0.tm (usage/arg_error multi-form Synopsis hint column); src/tests/modules/punk/args/testsuites/args/
Goal: The per-form hint labels rendered beside a multi-form definition's Synopsis lines (hard-coded ' i -form N ...' today) are configurable - a definition or rendering caller can supply its own hint text (parameterized by form index/name) or suppress the column - so consumers whose users cannot type 'i' (script-level definitions like make.tcl's, whose (script) ids are unreachable from any repl) show an actionable hint, while unconfigured definitions keep the current repl-oriented default.
Acceptance: a punk::args mechanism (definition directive and/or usage/arg_error caller option) replaces the ' i -form N ...' hint labels with caller-supplied text and can suppress the column entirely; unconfigured definitions render byte-identical to today (existing synopsis/usage/arg_error pins pass unchanged); the mechanism is pinned in the args testsuite. make.tcl adopting it (e.g a 'make.tcl help tool <action>' phrasing) is recorded as consumer follow-through after a bootsupport promotion, not part of this acceptance.
## Context
Identified during G-143 (achieved 2026-08-01 - goals/archive/G-143-maketcl-multiform-help.md): multi-form usage tables render ' i -form N ...' beside each form's synopsis line, hard-coded in the renderer (bootsupport args-0.13.0.tm ~5475; same code in the dev module). The wording is repl-centric and doubly inapplicable in make.tcl output - 'i' is not a make.tcl surface, and (script)-scoped ids exist only inside a running make.tcl process, so no repl can address them. make.tcl meanwhile has its own per-form navigation the hint could name ('make.tcl help <subcommand> <action>' - G-143's dry-run help).
## Notes
- Related: G-050 (proposed) - synopsis argument-validity marking; same renderer surface, independent mechanism.
- Related: G-056 (proposed) - display-time word wrapping for help content; sibling render-surface work.
- Related: G-151 (proposed) - annotated success render; sibling punk::args render-surface goal drafted the same session.
- Origin: G-144 (achieved 2026-08-01 - goals/archive/G-144-punkargs-synopsis-name-fallback.md) shipped the @cmd -name auto-synopsis lines these hints sit beside.
- Overlap survey 2026-08-02: goals_xref paths src/modules/punk/args-999999.0a1.0.tm - the punkargs family (G-044 G-050 G-053 G-056 G-069 G-072 G-073 G-075 G-083 G-084 G-088); besides those named above the family entries are parser/spec mechanisms unrelated to the hint column. Consumer side: make.tcl's argdoc (src/AGENTS.md make.tcl bullet) after a bootsupport promotion.

2
goals/G-151-punkargs-annotated-success-render.md

@ -13,5 +13,5 @@ From the G-143 dry-run help sessions (2026-08-01/02 - archived detail file): the
- Related: G-050 (proposed) - synopsis argument-validity marking; the nearest sibling (status-aware marking over the same argument rows).
- Related: G-044 (proposed) - punk::args-driven completion; landing attribution is the same data completion hints from.
- Related: G-149 (proposed) - configurable form-hint labels; sibling punk::args render-surface goal drafted the same session.
- Related: G-149 (achieved 2026-08-05 - goals/archive/G-149-punkargs-formhint-labels.md) - configurable form-hint labels; sibling punk::args render-surface goal drafted the same session. Its landed shape (caller option on arg_error/usage + @cmd definition key, caller > definition > default precedence) is the plumbing precedent for adding a render option to the same surfaces.
- Overlap survey 2026-08-02: goals_xref paths src/modules/punk/args-999999.0a1.0.tm - the punkargs family as recorded in G-149's survey; G-050 (named above) is the closest surface, the remainder are parser/spec mechanisms.

14
goals/G-167-punkargs-version-delta-availability.md

@ -86,7 +86,11 @@ deltas use -choiceunavailable; forms are reserved for true shape deltas.
- Related: G-166 (proposed) - sibling surfacing goal: word-level cmdinfo
availability key + cmdhelp marking. This goal's machine surface is the
formstatus 'available' field; the two must share marking vocabulary and
styling, and whichever lands first owns it (with G-149).
styling, and whichever lands first owns it. (G-149, formerly named here as
a third owner candidate, achieved 2026-08-05 without introducing any
form-line marking vocabulary - its hint labels are the LEFT label column
of the Synopsis cell, composing freely with form-line annotations - so
ownership is between this goal and G-166.)
- Related: G-152 (achieved 2026-08-02 - goals/archive/G-152-punkargs-form-viability-verdict.md) -
the formstatus consumer contract the 'available' field extends additively.
- Related: G-041 (achieved - goals/archive/G-041-punkargs-form-matching.md) -
@ -96,8 +100,12 @@ deltas use -choiceunavailable; forms are reserved for true shape deltas.
formcheck and the -overlapallowed sanctioning vocabulary era forms consume.
- Related: G-150 (achieved 2026-08-04 - goals/archive/G-150-cmdhelp-form-narrowing.md) -
cmdhelp form narrowing inherits the availability filter via form selection.
- Related: G-149 (form-hint labels) - the annotated form-line styling is
shared vocabulary; coordinate rather than duplicate.
- Related: G-149 (achieved 2026-08-05 - goals/archive/G-149-punkargs-formhint-labels.md) -
form-hint labels. Landed as a per-form template for the LEFT label column
of the Synopsis header cell (%formindex%/%formname% substitution,
suppressible) - it carries no form-line annotation styling, so this goal's
annotated form lines (right column) compose with it without collision;
an availability-aware hint template remains expressible by consumers.
- Related: G-044 (completion/hinting) - downstream consumer of the formstatus
field and option availability; G-055 - the tclcore workflow guidance gains
the era-form/option pattern once proven here, as it gains the G-073

69
goals/archive/G-149-punkargs-formhint-labels.md

@ -0,0 +1,69 @@
# G-149 punk::args: configurable multi-form synopsis hint labels
Status: achieved 2026-08-05
Scope: src/modules/punk/args-999999.0a1.0.tm (usage/arg_error multi-form Synopsis hint column); src/tests/modules/punk/args/testsuites/args/
Goal: The per-form hint labels rendered beside a multi-form definition's Synopsis lines (hard-coded ' i -form N ...' today) are configurable - a definition or rendering caller can supply its own hint text (parameterized by form index/name) or suppress the column - so consumers whose users cannot type 'i' (script-level definitions like make.tcl's, whose (script) ids are unreachable from any repl) show an actionable hint, while unconfigured definitions keep the current repl-oriented default.
Acceptance: a punk::args mechanism (definition directive and/or usage/arg_error caller option) replaces the ' i -form N ...' hint labels with caller-supplied text and can suppress the column entirely; unconfigured definitions render byte-identical to today (existing synopsis/usage/arg_error pins pass unchanged); the mechanism is pinned in the args testsuite. make.tcl adopting it (e.g a 'make.tcl help tool <action>' phrasing) is recorded as consumer follow-through after a bootsupport promotion, not part of this acceptance.
## Context
Identified during G-143 (achieved 2026-08-01 - goals/archive/G-143-maketcl-multiform-help.md): multi-form usage tables render ' i -form N ...' beside each form's synopsis line, hard-coded in the renderer (bootsupport args-0.13.0.tm ~5475; same code in the dev module). The wording is repl-centric and doubly inapplicable in make.tcl output - 'i' is not a make.tcl surface, and (script)-scoped ids exist only inside a running make.tcl process, so no repl can address them. make.tcl meanwhile has its own per-form navigation the hint could name ('make.tcl help <subcommand> <action>' - G-143's dry-run help).
## Notes
- Related: G-050 (proposed) - synopsis argument-validity marking; same renderer surface, independent mechanism.
- Related: G-056 (proposed) - display-time word wrapping for help content; sibling render-surface work.
- Related: G-151 (proposed) - annotated success render; sibling punk::args render-surface goal drafted the same session.
- Related: G-167 (proposed) - version-delta availability; drafted 2026-08-05, touches the same multi-form synopsis renderer surface (annotated form lines beside the same hint column). Found by the activation-freshness overlap survey 2026-08-05. Composition note at achievement: this goal's template governs the LEFT label column of the Synopsis header cell; G-167's annotations mark the synopsis form lines themselves (right column) - the mechanisms compose without collision, and per G-167's Notes the shared marking vocabulary is owned by whichever of its sibling surfaces lands next.
- Origin: G-144 (achieved 2026-08-01 - goals/archive/G-144-punkargs-synopsis-name-fallback.md) shipped the @cmd -name auto-synopsis lines these hints sit beside.
- Overlap survey 2026-08-02: goals_xref paths src/modules/punk/args-999999.0a1.0.tm - the punkargs family (G-044 G-050 G-053 G-056 G-069 G-072 G-073 G-075 G-083 G-084 G-088); besides those named above the family entries are parser/spec mechanisms unrelated to the hint column. Consumer side: make.tcl's argdoc (src/AGENTS.md make.tcl bullet) after a bootsupport promotion.
## Progress
Achieved 2026-08-05 in one session (activated and implemented same day; punk::args 0.17.0, punkshell 0.55.0).
Mechanism landed (both arms of the "definition directive and/or caller option" acceptance):
- Definition directive: `@cmd -formhint <template>`. Rides the existing @cmd
arbitrary-key merge into cmd_info - no resolver change; round-trips through
punk::args::resolved_def (whole-dict emission) automatically; not part of the
G-046 display-deferral set (only -help defers), so the renderer reads it
directly. Documented in the define -help @cmd directive-options.
- Caller option: `-formhint <template>` on arg_error (manual dict-for parser +
PUNKARGS block); usage pulls it from arg_error's definition via its existing
resolved_def opts pull and passes it through. Supplied-empty is distinct from
not-supplied (formhint_received flag; the option declares no -default so an
unsupplied usage call passes nothing through - proven by the byte-identity
capture below running THROUGH the usage path).
- Template semantics: rendered once per form; %formindex% -> 0-based ordinal,
%formname% -> form name (string map; the repo's %...% placeholder
convention). Empty template (from either source) suppresses the hint lines -
the cell keeps just the Synopsis: label, as single-form renders always have.
Precedence: caller > definition > built-in default
" i -form %formindex% <U+2026>" (substitutes to the exact prior bytes).
- Scope decisions (deliberate, pinned): single-form definitions never render
hint labels (unchanged - the column is multi-form only); the -return string
renderer has no hint column and is unchanged.
Verification evidence:
- Byte-identity: pre/post-change captures of `punk::args::usage
::punk::args::parse` (multi-form, unconfigured) via `bin/punk91 src script`
byte-identical apart from a boot-noise threadid line outside the render.
- New suite src/tests/modules/punk/args/testsuites/args/formhint.test
(9 tests): default labels, single-form scope, directive template +
cmd_info round-trip, directive suppression, caller override, caller
supplied-empty suppression, arg_error direct option, parse-failure path
using the directive, string-renderer scope.
- Full modules/punk/args subtree: 302 tests, 0 failed (3 pre-existing skips)
on BOTH tclsh90 (Tcl 9.0.3) and the punk86 kit (Tcl 8.6) - existing
synopsis/usage/arg_error pins pass unchanged per acceptance.
- Adjacent consumer: modules/punk/ns subtree 115/115 on tclsh90 (cmdhelp
renders through the same machinery).
- make.tcl's own help (bootsupport punk::args) is untouched until promotion;
src/tests/shell/testsuites/punkexe/maketclhelp.test continues to pin the
current ' i -form 0 ' hint there.
## Follow-ons
Follow-on: make.tcl adopts the mechanism for its (script)::punkboot definitions (e.g a 'make.tcl help <subcommand> <action>' hint phrasing via @cmd -formhint) after a bootsupport promotion carrying punk::args 0.17.0- (maketclhelp.test formhints pin updates with it) => open

2
punkproject.toml

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

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

@ -798,6 +798,18 @@ tcl::namespace::eval punk::args {
directive-options: -name <str>
-summary <str>
-help <str>
-formhint <str>
The -formhint value replaces the built-in ' i -form N ...'
hint labels rendered beside a multi-form definition's
synopsis lines in usage/arg_error table output.
Occurrences of %formindex% and %formname% are replaced
per form with the form's 0-based ordinal and its name.
An empty string suppresses the hint labels entirely.
Intended for definitions whose users cannot act on the
repl-oriented default - e.g script-level (script) ids,
which are unreachable from any repl - so the hint can
name that consumer's own navigation instead. Callers of
usage/arg_error may override with their -formhint option.
%B%@leaders%N% ?opt val...?
(used for leading args that come before switches/opts)
directive-options:
@ -5071,6 +5083,16 @@ tcl::namespace::eval punk::args {
-scheme -default error -choices {nocolour info error}
-form -default 0 -help\
"Ordinal index or name of command form"
-formhint -type string -help\
"Template for the per-form hint labels displayed beside each
form's synopsis line when the definition has multiple forms.
Occurrences of %formindex% and %formname% in the template are
replaced per form with the form's ordinal index (0-based) and
its name. An empty string suppresses the hint labels entirely.
When this option is not supplied, a definition may carry its
own template via '@cmd -formhint'; with neither present the
built-in repl-oriented default renders ' i -form N ...' labels
(one per form)."
}] ]
@ -5240,8 +5262,10 @@ tcl::namespace::eval punk::args {
set as_error 1 ;#usual case is to raise an error
set scheme error
set form 0
set formhint ""
set formhint_received 0 ;#distinguish supplied-empty (suppress) from not-supplied (definition/default applies)
dict for {k v} $args {
set fullk [tcl::prefix::match -error "" {-badarg -parsedargs -parsestatus -aserror -return -scheme -form} $k]
set fullk [tcl::prefix::match -error "" {-badarg -parsedargs -parsestatus -aserror -return -scheme -form -formhint} $k]
switch -- $fullk {
-badarg {
set badarg $v
@ -5272,9 +5296,13 @@ tcl::namespace::eval punk::args {
-form {
set form $v
}
-formhint {
set formhint $v
set formhint_received 1
}
default {
set arg_error_isrunning 0
error "arg_error invalid option $k. Known_options: -badarg -parsedargs -parsestatus -aserror -scheme -return -form"
error "arg_error invalid option $k. Known_options: -badarg -parsedargs -parsestatus -aserror -scheme -return -form -formhint"
}
}
}
@ -5546,12 +5574,24 @@ tcl::namespace::eval punk::args {
set form_names [dict get $spec_dict form_names]
set synhelp "Synopsis:"
if {[llength $form_names] > 1} {
#G-149: the per-form hint labels beside the synopsis lines are a
#template substituted per form (%formindex% 0-based ordinal,
#%formname% form name). Precedence: caller -formhint > definition
#@cmd -formhint > builtin repl-oriented default. An empty template
#suppresses the labels (the cell keeps just the Synopsis: label).
if {$formhint_received} {
set hint_template $formhint
} else {
set hint_template [Dict_getdef $spec_dict cmd_info -formhint " i -form %formindex% \U2026"]
}
if {$hint_template ne ""} {
set fn 0
foreach fname $form_names {
append synhelp \n " i -form $fn \U2026"
append synhelp \n [string map [list %formindex% $fn %formname% $fname] $hint_template]
incr fn
}
}
}
$t configure_header $h -colspans $arg_colspans -values [list $synhelp [punk::ansi::ansiwrap brightwhite $synopsis]]
} else {
#todo
@ -6490,7 +6530,7 @@ tcl::namespace::eval punk::args {
"
-return -default table -choices {string table tableobject}
}\
{${[punk::args::resolved_def -types opts -override {-scheme {-default info}} ::punk::args::arg_error -scheme]}}\
{${[punk::args::resolved_def -types opts -override {-scheme {-default info}} ::punk::args::arg_error -scheme -formhint]}}\
{${[punk::args::resolved_def -types opts ::punk::args::resolved_def -form]}}\
{

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

@ -1,6 +1,7 @@
0.16.0
0.17.0
#First line must be a semantic version number
#all other lines are ignored.
#0.17.0 - G-149 configurable multi-form synopsis hint labels: the per-form hint labels rendered beside a multi-form definition's synopsis lines in the usage/arg_error table (hard-coded ' i -form N ...' previously) are now a template substituted per form - %formindex% replaced with the form's 0-based ordinal, %formname% with its name; an empty template suppresses the labels entirely (the cell keeps just the Synopsis: label, as single-form renders do). Precedence: caller option > definition > built-in default. New arg_error/usage option -formhint <template> (supplied-empty means suppress; usage pulls it from arg_error's definition via resolved_def); new @cmd directive key -formhint <template> for definition-supplied hints (rides the existing arbitrary-key cmd_info merge, so resolved_def round-trips it; not display-deferred). Unconfigured definitions render byte-identical to 0.16.0 (default template reproduces the exact prior bytes, verified by capture diff). Motivation (G-143 origin): script-level (script) ids - e.g make.tcl's - are unreachable from any repl, so the repl-oriented ' i -form N ...' default is not actionable there; consumers can now name their own per-form navigation. The string (non-table) renderer never carried hint labels and is unchanged. make.tcl adoption is consumer follow-through after a bootsupport promotion (not part of this change). New testsuite formhint.test; define -help documents the @cmd key beside the other directive-options.
#0.16.0 - G-073 unavailable choices: new per-argument key -choiceunavailable <list> - names that are recognised but not available in the current runtime/context. Display: entries render among the choices under a dedicated 'Unavailable (recognised, not selectable here)' group heading (table title / string-renderer Group heading - the distinguishing mechanism) with their ordinary -choicelabels notes, and join the prefix-calculation pool (display shortest-prefix marking reflects their reserving effect); synopsis choice-literal display and value-in-effect/goodarg marking never include them (structural: they are not in -choices/-choicegroups, and -default may not name one). Parse: choiceword_match gains an optional 8th argument unavailablelist and an 'unavailable' result key - a word landing on an unavailable name (exact, or unique prefix; case per -nocase) reports unavailable 1 with the name as canonical while matched stays 0, so matched-only consumers treat it as an ordinary non-match; a prefix shared between an available and an unavailable name is a plain ambiguous non-match. Validation rejects such a landing with the tailored PUNKARGS VALIDATION {choiceunavailable <name> choices <list>} error (-badarg/-badval; message names the entry, its -choicelabels note - e.g a version boundary - and the available values; private::unavailable_choice_msg shared by all raise surfaces). The G-071 allocation screen honours the list (optional-arg landings recorded as G-082 rejection kind 'unavailable' and preferred at the overflow raise sites); required args are unscreened as before, so their landing reports via final validation identically. Ambiguous-prefix messages annotate unavailable longer matches '(recognised but unavailable)'. -choicerestricted 0 keeps full passthrough (no tailored rejection). Resolve-time validation: requires -choices/-choicegroups; entries must not collide with choices, -choicealiases alias names, or -choiceprefixreservelist entries; -default may not be an unavailable name. form_literal_affinity/formcheck screens pass the list through (uniform pool semantics). In-tree adopter: punk::args::moduledoc::tclcore 'string is' forward classes (dict on 8.6). New testsuite choiceunavailable.test; tclcoreparity.test gains forward-class pins + the user-sanctioned 'di' strictness exemption; define -help documents the key.
#0.15.2 - G-082 follow-on: a REQUIRED typed value or leader whose present word fails the basic-type allocation screen (int/double/bool/number/dict) now reports the pointed typemismatch (message + errorcode with -badarg/-badval, same shape as the G-082 overflow-site selection) instead of the generic missingrequiredvalue/missingrequiredleader shortfall - the in-loop required-arg raise sites consult the rejection record returned by get_dict_can_assign_value for the current word. Unchanged by design: end-of-input shortages (valuemissing/count classes), a word failing a required literal-typed member, and multi-member clause partials (no rejection record - conservatism preserved); required-arg choice words were never screened (G-071 asymmetry) so choiceviolation reporting is untouched. parse_status consequences: this shape's top-level status flips incomplete->invalid with failureclass typemismatch (the formstatus verdict was already 'invalid' - the G-152 probe confirmed each such 'incomplete' away before reporting; one probe re-parse saved); multiform noformmatch classes unchanged (already invalid), per-form failureclass/message now the pointed typemismatch. parse_status argdoc status-key note updated. Pins flipped deliberately: parsestatus.test typed-value allocation failure, formviability.test single-form record (top-status/formstatus divergence demonstration re-anchored on a required-option shape), punk/ns cmdhelp.test badarg message match; new errorselection.test required-position coverage (value + leader) with unchanged-shape guards (end-of-input, literal-typed, clause partial).
#0.15.1 - G-164 viability-probe alternative-allocation re-probe: when the valmin reservation seats a word that literally names a declared required option into a required value slot at end-of-input, and the resulting optionmissing stands in the G-152 viability probe (a value word consumed, defeating the position guard), the probe now re-parses with the stolen word(s) consumed as the options they name (private::get_dict_form's viabilitycheck argument carries {1 reseat_words} on the re-probe pass) and grants 'incomplete' (viable) on pure end-of-input exhaustion after that re-seating. Cures the 'i hash -t' false negative: the test form classes viable since {-t foo} parses; sibling forms stay invalid. Options still missing after the re-seating are ordinary exhaustion in the re-probe. Runtime seating is unchanged - the re-probe runs only inside the probe, only via reservation-seated words (the trailing-flag-as-value rule is load-bearing; flagvalues_plain_then_dash and the runtime optionmissing path pinned unchanged). A stolen word naming a value-TAKING required option stays conservative (invalid) - re-seated, it seats as a value again at end-of-input. Verdict contract documentation updated in the parse/parse_status argdoc. formviability.test gains 7 pins (re-probe, delete-form, other-missing-options exhaustion, unnamed stolen word, value-taking conservatism, runtime-unchanged, single-form formstatus); the cmdhelp.test GAP pin cmdhelp_GAP_multiform_flagled_testform_false_notviable flips to cmdhelp_multiform_flagled_testform_viable.

184
src/tests/modules/punk/args/testsuites/args/formhint.test

@ -0,0 +1,184 @@
package require tcltest
package require punk::args
package require punk::ansi
#G-149: configurable multi-form synopsis hint labels - added 2026-08-05.
#The usage/arg_error table renders per-form hint labels beside a multi-form
#definition's synopsis lines (the left column of the Synopsis: header cell).
#Previously hard-coded ' i -form N ...' (repl-oriented), the labels are now a
#template substituted per form: %formindex% -> the form's 0-based ordinal,
#%formname% -> its name. Precedence: caller -formhint option (arg_error/usage)
#> definition '@cmd -formhint' > built-in default (byte-identical to the old
#hard-coded labels). An empty template - from either source - suppresses the
#hint lines entirely (supplied-empty at the caller is distinct from
#not-supplied). Single-form definitions never render hint labels, and the
#-return string renderer has no hint column - both pinned here as scope.
#
#Assertions are on ANSI-stripped output only (no SGR-dependent pins), so the
#suite is independent of terminal colour state.
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
#U+2026 horizontal ellipsis - trailing char of the built-in default label
#(built with format so this source file stays pure ASCII)
variable ellipsis [format %c 0x2026]
#multi-form fixture with no -formhint configuration (default labels)
punk::args::define {
@id -id ::testspace::fh_plain
@cmd -name testspace::fh_plain -summary "formhint default fixture" -help "formhint default fixture"
@form -form alpha
@values -min 1 -max 1
v1 -type string
@form -form beta
@values -min 2 -max 2
a -type string
b -type string
}
#multi-form fixture carrying a definition-supplied hint template
punk::args::define {
@id -id ::testspace::fh_hinted
@cmd -name testspace::fh_hinted -summary "formhint directive fixture" -help "formhint directive fixture" -formhint "myprog help %formname% (form %formindex%)"
@form -form alpha
@values -min 1 -max 1
v1 -type string
@form -form beta
@values -min 2 -max 2
a -type string
b -type string
}
#multi-form fixture suppressing the hint labels at the definition
punk::args::define {
@id -id ::testspace::fh_suppressed
@cmd -name testspace::fh_suppressed -summary "formhint suppress fixture" -help "formhint suppress fixture" -formhint ""
@form -form one
@values -min 1 -max 1
x -type string
@form -form two
@values -min 2 -max 2
y -type string
z -type string
}
#single-form fixture carrying a hint template (must render no hint labels)
punk::args::define {
@id -id ::testspace::fh_single
@cmd -name testspace::fh_single -summary "formhint single-form fixture" -help "formhint single-form fixture" -formhint "should not appear %formname%"
@values -min 1 -max 1
only -type string
}
#added 2026-08-05 (agent, G-149)
test formhint_default_render {unconfigured multi-form definition renders the built-in ' i -form N ...' hint labels, one per form}\
-setup $common -body {
set out [punk::ansi::ansistrip [punk::args::usage ::testspace::fh_plain]]
lappend result [string match "* i -form 0 $::testspace::ellipsis*" $out]
lappend result [string match "* i -form 1 $::testspace::ellipsis*" $out]
}\
-cleanup {
}\
-result [list 1 1]
test formhint_single_form_no_hints {single-form definitions render no hint labels even when @cmd -formhint is set}\
-setup $common -body {
set out [punk::ansi::ansistrip [punk::args::usage ::testspace::fh_single]]
lappend result [expr {![string match "*should not appear*" $out]}]
lappend result [expr {![string match "* i -form *" $out]}]
lappend result [string match "*Synopsis:*" $out]
}\
-cleanup {
}\
-result [list 1 1 1]
test formhint_cmd_directive_template {@cmd -formhint template substitutes %formindex% and %formname% per form and replaces the built-in labels}\
-setup $common -body {
set out [punk::ansi::ansistrip [punk::args::usage ::testspace::fh_hinted]]
lappend result [string match "*myprog help alpha (form 0)*" $out]
lappend result [string match "*myprog help beta (form 1)*" $out]
lappend result [expr {![string match "* i -form *" $out]}]
#the key is stored in cmd_info (round-trips via get_spec/resolved_def)
lappend result [dict get [punk::args::get_spec ::testspace::fh_hinted] cmd_info -formhint]
}\
-cleanup {
}\
-result [list 1 1 1 "myprog help %formname% (form %formindex%)"]
test formhint_cmd_directive_suppress {@cmd -formhint {} suppresses the hint labels while the per-form synopsis lines still render}\
-setup $common -body {
set out [punk::ansi::ansistrip [punk::args::usage ::testspace::fh_suppressed]]
lappend result [expr {![string match "* i -form *" $out]}]
lappend result [string match "*Synopsis:*" $out]
#both forms' synopsis lines remain
lappend result [string match "*fh_suppressed x*" $out]
lappend result [string match "*fh_suppressed y z*" $out]
}\
-cleanup {
}\
-result [list 1 1 1 1]
test formhint_caller_option_overrides_directive {usage -formhint overrides a definition-supplied template}\
-setup $common -body {
set out [punk::ansi::ansistrip [punk::args::usage -formhint "caller says %formindex%" ::testspace::fh_hinted]]
lappend result [string match "*caller says 0*" $out]
lappend result [string match "*caller says 1*" $out]
lappend result [expr {![string match "*myprog help*" $out]}]
}\
-cleanup {
}\
-result [list 1 1 1]
test formhint_caller_empty_suppresses {usage -formhint {} suppresses the hint labels even when the definition supplies a template (supplied-empty is distinct from not-supplied)}\
-setup $common -body {
set out [punk::ansi::ansistrip [punk::args::usage -formhint "" ::testspace::fh_hinted]]
lappend result [expr {![string match "*myprog help*" $out]}]
lappend result [expr {![string match "* i -form *" $out]}]
lappend result [string match "*Synopsis:*" $out]
}\
-cleanup {
}\
-result [list 1 1 1]
test formhint_arg_error_direct_option {arg_error accepts -formhint directly (the option usage passes through)}\
-setup $common -body {
set spec [punk::args::get_spec ::testspace::fh_plain]
set out [punk::ansi::ansistrip [punk::args::arg_error "" $spec -aserror 0 -formhint "direct %formname%"]]
lappend result [string match "*direct alpha*" $out]
lappend result [string match "*direct beta*" $out]
lappend result [expr {![string match "* i -form *" $out]}]
}\
-cleanup {
}\
-result [list 1 1 1]
test formhint_parse_error_uses_directive {a failed parse's raised usage table renders the definition's @cmd -formhint labels (internal arg_error path)}\
-setup $common -body {
if {[catch {punk::args::parse {bogus x y z toomany} withid ::testspace::fh_hinted} errmsg]} {
set stripped [punk::ansi::ansistrip $errmsg]
lappend result [string match "*myprog help alpha (form 0)*" $stripped]
lappend result [expr {![string match "* i -form *" $stripped]}]
} else {
lappend result UNEXPECTED-parse-accepted
}
}\
-cleanup {
}\
-result [list 1 1]
test formhint_string_renderer_unchanged {the -return string renderer has no hint column - a definition template does not appear there}\
-setup $common -body {
set out [punk::ansi::ansistrip [punk::args::usage -return string ::testspace::fh_hinted]]
lappend result [expr {![string match "*myprog help*" $out]}]
lappend result [string match "*Synopsis:*" $out]
}\
-cleanup {
}\
-result [list 1 1]
}
tcltest::cleanupTests ;#needed to produce test summary line.
Loading…
Cancel
Save