Browse Source

punk::args 0.4.1: ensemble autodef loads lazily-registered subcommand argdocs before id_exists checks

First 'i <ensemble>' in a fresh shell (e.g 'i ansistring') rendered the autogenerated
ensemble help without subcommand-help markers/choicelabels: ensemble_subcommands_definition
tested punk::args::id_exists against argdocs whose registered namespace
(::punk::args::register::NAMESPACES) had not yet been lazily loaded - the punk::ns
doc-lookup entry path only loads the ensemble command's parent namespace. The generator
now runs update_definitions for the namespaces its id checks could resolve in.
Self-healed on later calls (autodef regenerates per 'i' invocation), which is why the
defect went unnoticed; no prior test pinned the load ordering.
Tests: punk/args ensembledef.test (new), punk/ns cmdhelp.test
cmdhelp_ensemble_lazy_registered_argdocs. Project 0.4.14.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 5 days ago
parent
commit
383e4e5057
  1. 4
      CHANGELOG.md
  2. 2
      punkproject.toml
  3. 13
      src/modules/punk/args-999999.0a1.0.tm
  4. 3
      src/modules/punk/args-buildversion.txt
  5. 81
      src/tests/modules/punk/args/testsuites/args/ensembledef.test
  6. 43
      src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test

4
CHANGELOG.md

@ -5,6 +5,10 @@ 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.4.14] - 2026-07-10
- punk::args 0.4.1: first `i <ensemble>` in a fresh shell now shows subcommand-help markers and synopsis choicelabels for documented subcommands whose argdocs are lazily registered (e.g `i ansistring`); previously the autogenerated ensemble help omitted them until another path loaded the ensemble's registered namespace (`punk::args::update_definitions`).
## [0.4.13] - 2026-07-09
- punk::args 0.4.0 tidy-up (pre G-046): dead developer-test procs removed (`define2`, `parseXXX`, the unused `::punk::args::TEST` definition); the interactive `Get_caller` diagnostics and `lib::tstr_test_one` renamed with leading `__` and unexported; internal parser/renderer helpers moved from the exported `punk::args` namespace into a new internal `punk::args::private` namespace (underscore prefixes dropped); every remaining exported command now carries a PUNKARGS definition, so `i`/`punk::args::usage` render documentation for the whole punk::args API (including `punk::args::lib` utilities and the `helpers`/`argdocbase` ANSI shorthands).

2
punkproject.toml

@ -1,3 +1,3 @@
[project]
name = "punkshell"
version = "0.4.13"
version = "0.4.14"

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

@ -11779,6 +11779,19 @@ tcl::namespace::eval punk::args {
} [dict get $cinfo origin] {*}{
}
]
#id_exists only sees definitions already loaded - registered argdocs
#(::punk::args::register::NAMESPACES) load lazily, and callers such as the
#punk::ns doc-lookup path only load the ensemble command's parent namespace.
#Load the namespaces the id_checks could resolve in, or a first call before
#they are loaded (e.g 'i <ensemble>' in a fresh shell) builds the definition
#without subhelp/choicelabel entries for documented subcommands.
set id_check_namespaces [list]
foreach checkid $id_checks {
set idns [namespace qualifiers $checkid]
if {$idns eq ""} {set idns ::}
if {$idns ni $id_check_namespaces} {lappend id_check_namespaces $idns}
}
punk::args::update_definitions $id_check_namespaces
set N [punk::ansi::a+ normal]
set RST [punk::ansi::a]
foreach checkid $id_checks {

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

@ -1,6 +1,7 @@
0.4.0
0.4.1
#First line must be a semantic version number
#all other lines are ignored.
#0.4.1 - fixed ensemble_subcommands_definition building the definition against unloaded argdocs: subhelp/doctype-punkargs choiceinfo entries and synopsis choicelabels were omitted for documented subcommands whose registered namespace (::punk::args::register::NAMESPACES) had not yet been lazily loaded - e.g first 'i ansistring' in a fresh shell rendered the autogenerated ensemble help without subcommand-help markers until something else loaded ::punk::ansi::ansistring. The generator now runs update_definitions for the namespaces its id_exists checks could resolve in before testing them (tests: punk/args ensembledef.test, punk/ns cmdhelp.test cmdhelp_ensemble_lazy_registered_argdocs)
#0.4.0 - tidy-up (pre G-046): removed dead developer-test procs define2 and parseXXX and the unused ::punk::args::TEST definition; renamed the interactive Get_caller diagnostics test_get_dict/test_get_by_id/test_callers (+ test1 id) and lib::tstr_test_one with leading double underscores and excluded them from exports (punk::args::lib now exports {[a-z]*} instead of *). new punk::args::private namespace (namespace path to punk::args/lib/system) for internal helpers - _get_dict_can_assign_value, _split_type_expression, _check_clausecolumn, _synopsis_form_arg_display, _argerror_load_colours and lib::_parse_tstr_parts moved there with underscore prefixes dropped. documentation: PUNKARGS definitions added for every remaining exported command (errorstyle, undefine, undefine_deflist, raw_def, resolved_def_values, get_spec, is_dynamic, rawdef_id, rawdef_is_dynamic, id_exists, idaliases, set_idalias, unset_idalias, get_idalias, id_query, real_id, status, choiceword_match, get_dict; lib: string_is_dict, flatzip, zero_based_posns, choiceinfo_marks, indent, undent, undentleader, longestCommonPrefix, lunique; helpers and argdocbase B/N/I/NI); resolve's empty @cmd -help filled in; punk::args::argdocbase added to the registered definition namespaces
#0.3.2 - Tcl 9.1 compatibility (TIP 746 removed expr behaviour from lseq operands): zero_based_posns now wraps its count-1 operand in expr
#0.3.1 - fixed helpers::example -title path calling bare [a] (only resolvable where a global 'a' alias exists, e.g. punk shells) - now punk::ansi::a; found by the new examples.test running under plain tclsh

81
src/tests/modules/punk/args/testsuites/args/ensembledef.test

@ -0,0 +1,81 @@
package require tcltest
package require punk::args
package require punk::ns
package require punk::ansi
#ensemble_subcommands_definition lazy-argdoc ordering - added 2026-07-10 (defect found
#via 'i ansistring' in a fresh shell: the autogenerated ensemble help rendered without
#subcommand-help markers/choicelabels until punk::args::update_definitions was run for
#::punk::ansi::ansistring by some other path).
#Root cause: punk::args::ensemble_subcommands_definition decides subhelp/doctype-punkargs
#choiceinfo entries and synopsis choicelabels via bare punk::args::id_exists checks, and
#id_exists only sees definitions already loaded. Registered argdocs
#(::punk::args::register::NAMESPACES) load lazily, and the punk::ns doc-lookup entry path
#only loads [namespace qualifiers $ensemblecommand] - the parent namespace - never the
#ensemble's own registered namespace where subcommand argdocs conventionally live.
#The generator must load the namespaces its id checks could resolve in before testing.
namespace eval ::testspace {
namespace import ::tcltest::*
variable common {
set result ""
}
#fixture: module-style ensemble whose subcommand argdocs are registered for lazy
#loading (as punk::ansi does for ::punk::ansi::ansistring) but never explicitly
#loaded by this file before the generator runs.
namespace eval ::testspace::ensdeflazy {
namespace export alpha beta
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::testspace::ensdeflazy::alpha
@cmd -name testspace::ensdeflazy::alpha -summary\
"Lazy ensemble alpha fixture."\
-help\
"alpha help."
@values -min 1 -max 1
zzduration -type integer -help\
"distinctive argument name for rendered-output assertions."
}]
}
proc alpha {zzduration} {return alpha-$zzduration}
proc beta {} {return beta}
namespace ensemble create
}
lappend ::punk::args::register::NAMESPACES ::testspace::ensdeflazy
test ensembledef_lazy_registered_argdocs_first_call {first ensemble_subcommands_definition call loads registered-but-unloaded subcommand argdocs itself: documented subcommand gets subhelp/doctype-punkargs choiceinfo, undocumented does not}\
-setup $common -body {
#precondition: argdoc registered but not yet loaded (id_exists is a pure
#cache lookup with no loading side effect)
lappend result [punk::args::id_exists ::testspace::ensdeflazy::alpha]
set vline [punk::args::ensemble_subcommands_definition -columns 1 ::testspace::ensdeflazy]
#the generator itself must have loaded the registered definitions
lappend result [punk::args::id_exists ::testspace::ensdeflazy::alpha]
#documented subcommand marked
lappend result [string match {*subhelp ::testspace::ensdeflazy::alpha*} $vline]
lappend result [string match {*doctype punkargs*} $vline]
#undocumented subcommand not marked
lappend result [string match {*subhelp ::testspace::ensdeflazy::beta*} $vline]
#choicelabel (synopsis) generated for the documented subcommand
lappend result [string match {*zzduration*} $vline]
}\
-cleanup {
}\
-result [list 0 1 1 1 0 1]
test ensembledef_loaded_argdocs_stable {a repeat call after the definitions are loaded produces the same marked definition (loaded-path regression guard)}\
-setup $common -body {
punk::args::update_definitions [list ::testspace::ensdeflazy]
set vline [punk::args::ensemble_subcommands_definition -columns 1 ::testspace::ensdeflazy]
lappend result [string match {*subhelp ::testspace::ensdeflazy::alpha*} $vline]
lappend result [string match {*doctype punkargs*} $vline]
lappend result [string match {*zzduration*} $vline]
}\
-cleanup {
}\
-result [list 1 1 1]
}
tcltest::cleanupTests ;#needed to produce test summary line.

43
src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test

@ -483,5 +483,48 @@ namespace eval ::testspace {
-cleanup {
}\
-result [list {{::testspace::helpfix v1} [-flag] firstval lastval}]
#--- ensemble autodef with lazily-registered subcommand argdocs ----------------------------
#Integration surface of the defect pinned unit-level in
#src/tests/modules/punk/args/testsuites/args/ensembledef.test ('i ansistring' in a
#fresh shell): the cmdhelp/arginfo entry path only loads the ensemble command's
#PARENT namespace, so the ensemble's own registered namespace (where subcommand
#argdocs conventionally live) is unloaded when the (autodef) definition is built.
namespace eval ::testspace::lazyintens {
namespace export delta gamma
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::testspace::lazyintens::gamma
@cmd -name testspace::lazyintens::gamma -summary\
"Lazy integration gamma fixture."\
-help\
"gamma help."
@values -min 1 -max 1
zzintdelay -type integer -help\
"distinctive argument name for autodef content assertion."
}]
}
proc gamma {zzintdelay} {return gamma-$zzintdelay}
proc delta {} {return delta}
namespace ensemble create
}
lappend ::punk::args::register::NAMESPACES ::testspace::lazyintens
test cmdhelp_ensemble_lazy_registered_argdocs {first cmdhelp render of an ensemble whose subcommand argdocs are registered but not yet loaded produces an (autodef) carrying the documented subcommand's subhelp marker and synopsis choicelabel}\
-setup $common -body {
#precondition: argdoc registered but not yet loaded
lappend result [punk::args::id_exists ::testspace::lazyintens::gamma]
set out [punk::ns::cmdhelp -return string ::testspace::lazyintens]
lappend result [expr {[string length $out] > 0}]
set rawdef [punk::args::raw_def (autodef)::testspace::lazyintens]
lappend result [string match {*subhelp ::testspace::lazyintens::gamma*} $rawdef]
lappend result [string match {*zzintdelay*} $rawdef]
lappend result [string match {*subhelp ::testspace::lazyintens::delta*} $rawdef]
}\
-cleanup {
}\
-result [list 0 1 1 1 0]
}
tcltest::cleanupTests ;#needed to produce test summary line.

Loading…
Cancel
Save