arg_error's cmd-help display transform (undent " "+help, max 4 - the
'#unindentedfields ?' todo site) is now gated by "-help" membership in the
@cmd line's -unindentedfields list, mirroring the existing per-argument
gate. The single transform site feeds both the table and string renderers,
so left-margin-authored @cmd help renders its first line flush with
continuations in both. @cmd -summary carries no indent transform in any
renderer, so -unindentedfields for -summary is vacuously honoured. No
in-tree definitions set -unindentedfields on @cmd - existing rendering is
unchanged. define doc for -unindentedfields now states where the option is
valid.
Tests: rendering_unindentedfields_cmd_help_GAP flipped to
rendering_unindentedfields_cmd_help (aligned expectation, both renderers
measured). Full punk::args suite 175 pass / 1 pre-existing skip / 0 fail;
punk::ns suite 53/53 (arg_error consumer); make.tcl modules builds clean
(tclsh 9.0.3).
Also: G-045 flipped proposed -> active (user direction 2026-07-12);
progress and remaining-work recorded in the detail file.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
An argument whose choice pool (-choices plus -choicegroups members,
deduplicated) has 1-3 members and -choicerestricted true (the default) now
renders those words unitalicised joined by | in synopses - e.g 'after cancel'
shows literal cancel, a 3-choice option shows (left|centre|right) - matching
the display style of literal()/literalprefix() type-alternates. Larger or
unrestricted choice sets keep the italicised argname/<type> display, and an
explicit -typesynopsis always takes precedence (textblock::frame -type
unchanged). New shared helper punk::args::private::synopsis_choice_literals
feeds both synopsis render paths (leaders/values via
synopsis_form_arg_display, options inline in synopsis); applies only to
single-element -type lists. Superseded commented-out single-choice sketches
removed; define doc for -choices documents the rule.
tests: synopsis.test 4 -> 13 - characterization coverage for
literal/literalprefix/stringstartswith/stringendswith type-alternates,
option alternate parenthesization, multi-element clause display (?type?
members, argname tail-word hints), -typesynopsis value-element lists and
option passthrough incl documenter ANSI, plus the new choice-literal rule
(leader/option/value positions, choicegroups counting, >3 and unrestricted
fallbacks, -typesynopsis precedence). rendering.test choicelabel fixtures
padded to 4 choices so markercol keeps matching the choices cell rather
than the now-literal synopsis line.
Known residue (deliberate): option-path -typesynopsis ?-trim edge cases
remain (todo comments at the render site) - resolve-time rejection deferred;
any future normalization surgery belongs to punk::ansi::ansistring/opunk::Str.
project 0.12.0
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- new punk::args::parse_status: documented status structure from a parse attempt (overall ok / status valid|invalid|incomplete / scheme / message / errorcode minus -argspecs / failureclass / badarg / form / receivednames + per-argument argstatus with class, status ok|bad|unparsed, received count/positions, value-in-effect incl -default fill); validation failures reported in the structure, not raised
- arg_error -parsestatus: both renderers derive goodarg/badarg row marking and choice value-in-effect highlighting from the structure (transient goodargs/badarg locals replaced; built internally from -badarg/-parsedargs when not supplied)
- per-render scheme colour resolution: documented -scheme choice value 'nocolour' now takes effect and scheme renders no longer mutate the shared arg_error_CLR array (strike-only goodarg leak fixed)
- parse: new -caller option overriding the %caller% frame-walk substitution (included in parse cache key); get_dict missingrequiredvalue/missingrequiredleader now carry -badarg (type/allocation failures get badarg marking, not just choice violations)
- punk::ns::cmdhelp: -return dict {origin docid cmdtype args_remaining parsestatus}; advisory parse via parse_status on both alias and main paths; explicit -scheme honoured on the parse-failure render; failure messages name the queried command instead of leaking the internal parse source line at top call depth
- tests: parsestatus.test new (12); usagemarking.test G-049 GAP pins flipped + -parsestatus parity/badarg render tests (17); cmdhelp.test G-049 GAP pins flipped + cmdhelp_return_dict_* (25); punk/args + punk/ns trees green on Tcl 9.0.3 and 8.7; full source-tree suite baseline unchanged (exec-14.3 only)
- punkshell 0.5.0: CHANGELOG entry; src/tests/modules/AGENTS.md test-index blurbs updated
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Argument resolution no longer tstr-expands display-only content (-help on
@cmd/@examples/argument records, @formdisplay bodies): ${...} there is masked with
inert tokens (spec key DISPLAY_DEFERRED) and expanded at display time via
private::expand_display_fields (hooks in arg_error/eg/resolved_def/@default copyfrom;
separate display cache for non-dynamic defs; @dynamic re-expands per render preserving
provider refresh; reentrancy guard substitutes raw sources when expansion of the same
id re-enters). First parse of heavily documented commands drops accordingly
(punk::ansi::mark_columns first call ~4.3s -> ~12ms; tclcore ::lseq resolve
~184ms -> ~2ms) and -help content that calls punk::args-parsing commands - including
against its own definition id - resolves cleanly instead of stalling or looping.
-choicelabels stays eager (punk::ns reads it from parse specs in the subcommand walk).
Record splitter factored to private::split_definition_records.
Also per G-046 acceptance:
- @dynamic multiline -help substitutions align at the insertion column
(rendering_atdynamic_multiline_help_insertion GAP flipped)
- prefix/alias choice normalization keeps single-element-clause values plain-string
({\Deleted} list-wrap shape bug; choicegroups_imap_prefix_shape GAP flipped)
- -return string renderer aligns cmd-help continuations under the Description: label
(characterization updated) and its Example: line shows the example, not the doc url
- punk::ns cmdhelp shows plain info-scheme usage when no argument words are supplied
for a leader/value-requiring definition ('i string is', 'i punk::args::define') -
advisory parse runs -errorstyle minimal so the discarded error no longer renders a
second usage table (timing parity with pre-change: ~5.3s/4.1s on punk91 src)
Tests: deferredhelp.test (new); suites green on tcl903 + tcl87; full source-tree run
baseline-clean (exec-14.3 only). GOALS.md: G-046 -> achieved 2026-07-10 (activated
this session at user direction); design + verification evidence in the detail file.
Project 0.4.15.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
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
TIP 746 (Tcl 9.1) removed lseq's expr-operand behaviour. punk::lib::range
(lseq branch) now normalizes int[+-]int offsets via offset_expr so callers
like `range 0 [llength $list]-1` keep working (punk::ansi::grepstr broke
under 9.1b0, taking example-block highlighting and the punk::args
examples.test with it). The lseq branch is also aligned with the tcl8
fallback contract: default 'by' now infers direction (descending ranges
previously returned empty under tcl9) and 'by 0' returns empty (Tcl 9.1
lseq changed by-0 to return one element).
Direct lseq expression operands expr-wrapped: punk::lib lzipn_tcl9b/c,
cols, cols2; punk::args zero_based_posns.
check::has_tclbug_safeinterp_compile falls back to interp invokehidden
tcl:unsupported:disassemble - Tcl 9.1 safe interps hide tcl::unsupported::*.
New modules/punk/lib range.test pins the range contract on 9.0 and 9.1;
core tests AGENTS.md documents native-tclsh vs punk-exe exec.test baselines.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Tests-first coverage pass (user-directed) before any changes to the
definition-parsing/indentation machinery. punk/args suite: 128 tests, 15 files.
- rendering.test (15): nesting independence (plain + tstr definitions render
identically regardless of source-block indentation), relative-indent
preservation (2-space help and 1-space choicelabel conventions, single and
multiline - preservation, not enforcement), -unindentedfields (arg -help
works left-margin; @cmd accepts-but-ignores it - GAP pinned), constructed
(string-built) definition absolute-indent characterization ('constructed'
to avoid confusion with the @dynamic directive), multiline ${[cmd]}/${$var}/
resolved_def insertions (aligned at insertion column, nesting-independent),
and @dynamic ${$DYN_X} double substitution: stable across repeated
resolutions, provider refresh honoured in help text, choice display and
parse acceptance, textblock::frame-style art choicelabels byte-aligned -
but multiline -help insertions land lines 2+ at insertion-4 (GAP pinned:
the second-round subst path lacks paramindent re-alignment)
- choicegroups.test (5): group/-choices union parsing, cross-group prefixes,
duplicate-entry dedup, per-group titled choice tables (the punk::imap4
multi-table display), imap4-style {\Deleted}/{$MDNSent} values with labels
incl. through tstr-bearing blocks; GAP pinned: prefix-normalization
list-quotes special-character choices (exact '\Deleted' stores the plain
string, prefix '\De' stores a one-element list) - value shape depends on
input style
- defquoting.test (3): container quoting rules pinned - braced values fully
literal ($, [], two-char \n, bare backslashes), quoted values get Tcl
backslash semantics (\n -> newline, \ -> \) with $/[] still literal, and
\$\{...\} renders a literal ${...} in tstr-processed blocks
- examples.test (6): helpers::example box/title/-tstr toggle (no-dedent
direct-call contract pinned), ${[example ...]} insertion into @cmd help
(bars at insertion column, content +2, automatic 'Example: eg <id>' row),
@examples -> punk::args::eg with #<nodisplay>/@#<nodisplay> stripping
(ANSI-tolerant), strip_nodisplay_lines unit, live tclcore ::lseq smoke
- punk::args 0.3.1: helpers::example -title path called bare [a] (resolvable
only where a punk shell's global alias exists - errored under plain tclsh);
now punk::ansi::a. Found by examples.test in the runtests environment.
- investigated punk::ansi::mark_columns first-call slowness (3.94s first,
60us after): argdoc -help calls a proc that calls mark_columns, whose parse
resolves the same still-resolving definition - hazard already documented
in-source with candidate fixes; recorded in CHANGELOG for goal
consideration, deliberately not patched
- project 0.4.11 (CHANGELOG entry)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Activated and completed under user direction (2026-07-08). Aliases for choice
values are now first-class: accepted at parse, normalized to their canonical
choice in results, folded into the canonical entry in usage display, and
resolved identically by the punk::ns doc-lookup walk.
- punk::args 0.3.0:
* -choicealiases {alias canonical ...} on argument lines; dict shape
validated in the option switch, alias->existing-choice and no-collision
cross-validated after spec merge (surfaces at resolve time - define is
lazy)
* the choice-word matching chain extracted from get_dict into the shared
resolver punk::args::choiceword_match {word nocase allchoices
choicealiases choiceprefix denylist reservelist} -> {matched exact
canonical}; get_dict delegates to it (writeback/lset logic stays at the
call site) - one implementation for parser and doc walk
* exact aliases match under any -choiceprefix/-nocase setting; alias names
join the prefix-calculation pool when -choiceprefix is true; deny applies
to the matched NAME (denied alias requires the full alias; a canonical
reached via its alias is exempt from the canonical's own deny entry)
* display: aliases fold into the canonical entry as an '(alias: x)' /
'(aliases: x|y)' label note (single convergence point covers all render
paths); alias names join the display prefix calculation so highlighted
minimal prefixes stay truthful
* define doc gains the -choicealiases section
- punk::ns 0.1.2: cmd_traverse resolves subcommand words via choiceword_match
- -choiceprefixdenylist/-choiceprefixreservelist now honoured in doc lookup
(the two pinned parity GAP tests flipped to agreement), aliases normalize
before choiceinfo lookup, -nocase honoured in the walk
- punk 0.2.3: punk::help adopts the feature - 'i help' lists one entry per
registered topic (topics+help, tcl, env+environment, console+term|terminal);
topic words accept aliases and unique prefixes with the user-decided
minimum-prefix policy recorded in ::punk::helptopic: denylist {help}
(h/he/hel stay command words), reservelist {c to tc} (fall through to
command lookup; e/en and te/ter fall through naturally as ambiguous).
Argless 'help' output byte-identical to the strict-80 baseline.
- tests: choices.test +5 (alias exact/prefix normalization, noprefix exact-
only, nocase, the punk::help policy matrix, resolve-time validation);
cmdflow.test parity tests flipped from GAP to agreement + alias-traverse and
display-fold assertions (+flowaliased fixture)
- suites: punk/args 98 pass + 1 skip, punk/ns 28 pass, punk/lib 21 pass
- G-040 flipped active -> achieved 2026-07-08; detail file records
implementation and verification
- project 0.4.10 (CHANGELOG entry)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- fixed "@opts -any 1" (arbitrary/adhoc option passthrough): an option not in
the definition crashed get_dict with "can't read argname" at the name-mapping
stage - adhoc opts now fall back to their raw supplied name, under which
argstate/arg_checks were already populated at scan time (real consumers:
vendored tomlish definitions declare -any 1)
- commented out a debug puts stderr (">>>_get_dict_can_assign_value NOT
alloc_ok...") that fired on every failed clause type assignment (visible on
any multiform parse miss; its happy-path twin was already commented)
- new forms.test (8 tests): @form spec structure (form_names, shared
{a b}-prologue blocks), explicit -form by name/index with cross-form
rejection, multiform synopsis rendering, and GAP pins for G-041 - default
parse is effectively form-0-only, -form rejects the documented list-of-forms
usage, and @form -synopsis overrides are stored in the spec but ignored by
synopsis rendering
- new validation.test (11 tests): -type validation matrix, -range/-minsize/
-maxsize, -nocase choice normalization, option -multiple accumulation,
required-option enforcement, unknown-option rejection vs -any adhoc
passthrough (the bugfix's regression test), -- end-of-options marker,
@values -max enforcement, received-key clause ordinals, solos list, and
default-vs-received distinction
- src/tests/AGENTS.md: note that tcltest compares the -body return value -
bodies ending in a loop must end with an explicit 'set result'
- suites: punk/args 93 pass + 1 skip (was 70+1), punk/ns 18 pass; full
source-tree run 569/581 with only the known exec-14.3 baseline failure
- project 0.4.5 (CHANGELOG entry)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- 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
- Document virtual channel facade API with special options and ownership model
- Fix punk/ansi module TRIE regex and struct::set ordering issues
- Refactor overtype module cursor/column handling TODOs with future roadmap
- Add punk/args documentation for CLOCK_ARITHMETIC and TIME ZONES
- Optimize cookfs pages.tcl seek operations with strategy notes
- Review ANSI ID character mapping security considerations
- Add grapheme cluster detection documentation
- Consolidate NOTE comments analysis (7,802 comments mostly appropriate)
Completed 10 of 11 high-impact TODO/FIXME resolution tasks.
Remaining: MIME module test coverage (requires comprehensive test suite).