Browse Source

G-041 prework: if/switch/try/lseq real-vs-model probe findings recorded; punk::args 0.8.2 debug-leak fix (project 0.12.9)

Probing (punk902z kit, Tcl 9.0.2) of the tclcore moduledoc models against
the real builtins, driven by user concerns about variable-length clauses
with optional elements before G-041 activation. Findings recorded in the
G-041 detail file (auto-selection evidence on real commands, the
allocator prerequisite, parse_status -form gap) and the G-055 detail file
(modelability over-acceptance list: try compound handler types,
reserved-word clause allocation, "-" fallthrough constraints, switch
two-argument option suppression, unvalidated -type expr, TIP 746
version-conditionality for lseq operands; plus the user doc-direction
guidance that definitions split into @form entries where source docs
simplify).

Key result: the per-form models are doc-faithful (lseq.n synopsis maps
1:1; switch separate/block parse correctly under explicit -form; try
mixed on/trap interleaving and finally positioning all agree with the
real command) - but the value allocator mishandles an optional
single-word choice value followed by a required value plus trailing
optional-member clause, so lseq range arglists without the ../to noise
word ("0 10 2", "0 10 by 2") fail in-form. No lseq form accepts them, so
G-041 form selection alone cannot fix "i lseq 0 10 2" - allocator fix
flagged as a candidate prerequisite goal.

Fix included (punk::args 0.8.2): unconditional debug puts on the clause
type-check path (get_dict_can_assign_value) commented, companion to the
0.2.3 find. Full punk::args suite 186 pass / 1 pre-existing skip / 0 fail.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 3 days ago
parent
commit
58da288d45
  1. 4
      CHANGELOG.md
  2. 28
      goals/G-041-punkargs-form-matching.md
  3. 40
      goals/G-055-tclcore-regen-workflow.md
  4. 2
      punkproject.toml
  5. 2
      src/modules/punk/args-999999.0a1.0.tm
  6. 3
      src/modules/punk/args-buildversion.txt

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` Entries are newest-first; one bullet per notable change. See the root `AGENTS.md`
"Project Versioning" section for the bump policy. "Project Versioning" section for the bump policy.
## [0.12.9] - 2026-07-12
- punk::args 0.8.2: fixed stray debug output ("checking tp ... against value ...") printed to the console when parsing multi-element clause arguments (e.g. 'i try ...' style usage against the tclcore moduledoc). Found during G-041 prework probing of if/switch/try/lseq real-vs-model divergence; findings recorded in the G-041 and G-055 goal detail files.
## [0.12.8] - 2026-07-12 ## [0.12.8] - 2026-07-12
- G-045 achieved: punk::args 0.8.1 documents the definition container quoting rules in the punk::args::define help (braced values fully literal; double-quoted values with Tcl backslash semantics at record parse while $ and [] stay literal; the backslash-escaped tstr placeholder idiom). This completes the definition authoring ergonomics goal: @cmd -unindentedfields honoured (0.6.1), -& record continuation (0.7.0), @normalize block-form indent normalization with the help-system definitions as consumer proof (0.8.0 / punk 0.2.5), quoting documentation (0.8.1). - G-045 achieved: punk::args 0.8.1 documents the definition container quoting rules in the punk::args::define help (braced values fully literal; double-quoted values with Tcl backslash semantics at record parse while $ and [] stay literal; the backslash-escaped tstr placeholder idiom). This completes the definition authoring ergonomics goal: @cmd -unindentedfields honoured (0.6.1), -& record continuation (0.7.0), @normalize block-form indent normalization with the help-system definitions as consumer proof (0.8.0 / punk 0.2.5), quoting documentation (0.8.1).

28
goals/G-041-punkargs-form-matching.md

@ -84,4 +84,32 @@ when shaping step 1.
assignment (punk/args ~l.6186; its happy-path twin was already commented). Fixed assignment (punk/args ~l.6186; its happy-path twin was already commented). Fixed
2026-07-08 (punk::args 0.2.3) - noted here because form-attempt logic will exercise 2026-07-08 (punk::args 0.2.3) - noted here because form-attempt logic will exercise
that failure path heavily. that failure path heavily.
- 2026-07-12 prework probes (user concerns raised before activation - real-builtin
evidence beyond the after fixture; results from the punk902z kit, Tcl 9.0.2):
- Auto-selection gaps confirmed on real commands: 'lseq 3' (count form),
'lseq 1 count 5 ?by 2?' (start_count form) and 'switch abc {a {} default {}}'
including options+block ('switch -regexp -matchvar m abc {...}') all fail under
default form-0 parsing but parse correctly with an explicit -form. The per-form
models are doc-faithful: lseq.n's three synopsis lines map 1:1 to the modelled
range/start_count/count forms; switch separate/block both verified per-form.
- PREREQUISITE independent of form selection: the value allocator mishandles an
optional single-word choice value followed by a required value plus a trailing
optional-member clause. lseq range-form arglists WITHOUT the ../to noise word
but WITH a step ('0 10 2', '0 10 by 2', '1 5 by 0') fail IN the range form (the
error blames '..|to'), while '0 to 10 2' and '0 .. 10 by 2' parse fine. No other
lseq form accepts those arglists, so form auto-selection alone cannot make
'i lseq 0 10 2' work - the allocator fix must precede or accompany this goal
(flagged as a candidate goal 2026-07-12). ::if's noise-word clauses
('?literal(then)?' mid-clause, '?literal(else)?' clause-leading) parse correctly
in the same probes, so the failure is specific to the optional standalone value
+ trailing optional-member clause combination, not optional clause members
generally.
- punk::args::parse_status does not accept -form - needed for per-form probing,
for this goal's candidacy API, and for the G-044 partial-arglist consumer.
- Over-acceptance divergences (model valid where the real command errors) recorded
for the modelability list in G-055's detail file - not this goal's scope.
- Incidental fix during probing: another unconditional debug puts on the clause
type-check path (private::get_dict_can_assign_value "checking tp ...", fixed as
punk::args 0.8.2) - companion to the 0.2.3 find noted above; form-attempt logic
will exercise these paths heavily.
- Archived-goal references in this file: G-040 achieved 2026-07-08 (goals/archive/G-040-punkargs-choicealiases.md). - Archived-goal references in this file: G-040 achieved 2026-07-08 (goals/archive/G-040-punkargs-choicealiases.md).

40
goals/G-055-tclcore-regen-workflow.md

@ -90,4 +90,44 @@ other commands including the multi-form ::after.
side-effect-free invocations of pure builtins (deliberately-invalid calls side-effect-free invocations of pure builtins (deliberately-invalid calls
harvesting error metadata). This does not relax cmdhelp's rule of never calling harvesting error metadata). This does not relax cmdhelp's rule of never calling
arbitrary commands to elicit usage at query time. arbitrary commands to elicit usage at query time.
- 2026-07-12 modelability findings from if/switch/try/lseq real-vs-model probing
(G-041 prework; all in the over-acceptance class - model reports valid where the
real command errors - so parity probes per this workflow's verification gate
would catch them):
- try handler compound types: the current handler clause
{literal(on)|literal(trap) string list string} cannot type the second element
per-alternate (on wants code choices ok/error/return/break/continue/int; trap
wants a pattern list), so 'try {} on bogus {e} {s}' over-accepts. The
moduledoc's own embedded notes at ::try sketch candidate mechanisms (named
compound types with arity indicators, @typeimport, RPN type expressions).
- reserved-word awareness in clause allocation: 'if {0} {b} elseif' over-accepts
(bare trailing word is a valid else-script EXCEPT when it is a reserved
keyword); clause members have no deny-list concept (cf -choiceprefixdenylist
for choices).
- '-' fallthrough constraints (try handler script, switch body): a literal '-'
is invalid in the LAST handler/pattern position - a cross-element constraint
outside per-element typing.
- switch two-argument special case: leading dash words are options only when
more than two arguments are present ('switch -exact {p b}' means
string="-exact") - argument-count-conditional option parsing.
- -type expr performs no validation (syntactically invalid '3+' accepted, as is
the non-expression 'to'); syntax-level checking is the most that is possible
(expressions may reference runtime vars/functions) - decide and record whether
to add it.
- TIP 746 (user-flagged 2026-07-12,
https://core.tcl-lang.org/tips/doc/trunk/tip/746.md) removes the expr
behaviour from lseq operands in Tcl 9.1: the lseq model's number|expr operand
types are correct for 9.0 but must become version-conditional (number-only on
9.1+) - the same adaptive-definition technique as the 'string is' class list
(G-054). punkshell's own code already adapted (punk::args 0.3.2
zero_based_posns). Parity probes for lseq must therefore derive expectations
from the live interpreter, not from the man page vintage.
- Doc-direction guidance (user, 2026-07-12) for this workflow: punkshell synopses
are deliberately richer than the underlying man pages - where the source docs
simplify (a single synopsis line hiding genuinely orthogonal usages), the
definition SHOULD split into multiple @form entries even though the man page does
not present them as forms (lseq range/start_count/count and switch
separate/block are the in-tree precedents). This extends the existing
synopsis-translation exception: form structure, like synopsis notation, follows
punkshell's modelling needs rather than the source document's presentation.
- Archived-goal references in this file: G-049 achieved 2026-07-10 (goals/archive/G-049-punkargs-parse-status-model.md);G-054 achieved 2026-07-11 (goals/archive/G-054-tclcore-stringis-harvest.md). - Archived-goal references in this file: G-049 achieved 2026-07-10 (goals/archive/G-049-punkargs-parse-status-model.md);G-054 achieved 2026-07-11 (goals/archive/G-054-tclcore-stringis-harvest.md).

2
punkproject.toml

@ -1,4 +1,4 @@
[project] [project]
name = "punkshell" name = "punkshell"
version = "0.12.8" version = "0.12.9"
license = "BSD-2-Clause" license = "BSD-2-Clause"

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

@ -7069,7 +7069,7 @@ tcl::namespace::eval punk::args {
set clause_member_optional 0 set clause_member_optional 0
} }
set tp [string trim $tp ?] set tp [string trim $tp ?]
puts "private::get_dict_can_assign_value: checking tp '$tp' against value '$rv'" #puts "private::get_dict_can_assign_value: checking tp '$tp' against value '$rv'"
switch -glob -- $tp { switch -glob -- $tp {
"literal(*" { "literal(*" {
set litmatch [string range $tp 8 end-1] set litmatch [string range $tp 8 end-1]

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

@ -1,6 +1,7 @@
0.8.1 0.8.2
#First line must be a semantic version number #First line must be a semantic version number
#all other lines are ignored. #all other lines are ignored.
#0.8.2 - fixed stray debug output: an unconditional 'puts' in private::get_dict_can_assign_value ("checking tp '<type>' against value '<value>'") fired on multi-element clause type checks (e.g. parsing 'try ... trap ... on ...' style arglists against the tclcore moduledoc), polluting interactive output - now commented like its 0.2.3 companion. Found during G-041 prework probing (real-vs-model divergence sweep of if/switch/try/lseq).
#0.8.1 - G-045 documentation-only: the container quoting rules pinned by defquoting.test promoted into the define -help documentation (-help key section): braced values fully literal (backslash sequences survive as typed) with only tstr placeholders and their backslash escape special; double-quoted values get Tcl backslash semantics at record parse (\n -> newline, doubled backslash collapses) while $ and [] stay literal with no variable/command substitution outside tstr placeholders; the backslash-escaped placeholder idiom renders a literal placeholder. #0.8.1 - G-045 documentation-only: the container quoting rules pinned by defquoting.test promoted into the define -help documentation (-help key section): braced values fully literal (backslash sequences survive as typed) with only tstr placeholders and their backslash escape special; double-quoted values get Tcl backslash semantics at record parse (\n -> newline, doubled backslash collapses) while $ and [] stay literal with no variable/command substitution outside tstr placeholders; the backslash-escaped placeholder idiom renders a literal placeholder.
#0.8.0 - G-045: new bare @normalize directive - opts a definition into indent normalization of BLOCK-FORM multi-line field values (first line whitespace-only, as authored by opening a braced literal with a newline): the structural first newline and a whitespace-only trailing line are dropped, the content lines' common leading whitespace is the block's base indent, the first content line is unindented fully and subsequent lines re-based to the standard 4-space continuation convention (deeper relative indents preserved; whitespace-only inner lines become empty). Head-form values are never altered - their base indent is ambiguous (uniform continuation indent may be the deliberate +2 relative convention over base 4) - which makes @normalize a no-op on conforming file-style definitions (idempotence pinned). Fields in a record's -unindentedfields are exempt. Implemented as a resolve pre-pass over split records (private::normalize_records / rebase_multiline_value); @normalize with options is an error. Intended for constructed/string-built definitions (no whole-block indent treatment otherwise); ::punk::helptopic::define_docs converts to it as the consumer proof (punk 0.2.5). New testsuite normalize.test; define doc documents the directive; rendering.test P4 notes stay pinned as the unopted default. #0.8.0 - G-045: new bare @normalize directive - opts a definition into indent normalization of BLOCK-FORM multi-line field values (first line whitespace-only, as authored by opening a braced literal with a newline): the structural first newline and a whitespace-only trailing line are dropped, the content lines' common leading whitespace is the block's base indent, the first content line is unindented fully and subsequent lines re-based to the standard 4-space continuation convention (deeper relative indents preserved; whitespace-only inner lines become empty). Head-form values are never altered - their base indent is ambiguous (uniform continuation indent may be the deliberate +2 relative convention over base 4) - which makes @normalize a no-op on conforming file-style definitions (idempotence pinned). Fields in a record's -unindentedfields are exempt. Implemented as a resolve pre-pass over split records (private::normalize_records / rebase_multiline_value); @normalize with options is an error. Intended for constructed/string-built definitions (no whole-block indent treatment otherwise); ::punk::helptopic::define_docs converts to it as the consumer proof (punk 0.2.5). New testsuite normalize.test; define doc documents the directive; rendering.test P4 notes stay pinned as the unopted default.
#0.7.0 - G-045: record-continuation token -& - an unquoted trailing -& element on a definition record line continues the record on the next line. Implemented in private::split_definition_records: the token is dropped and the next line joins after a single space with its leading whitespace collapsed, exactly how the Tcl parser joins backslash-newline continuations before a braced definition reaches the splitter - so a -& record assembles byte-identical to its backslash-continued equivalent (proven by parse+render equality test). Motivation: constructed/string-built definitions cannot author backslash-newline ergonomically (the building code's own quoting consumes it); -& is plain text and survives any construction. Collision rules: the token must be a bare word preceded by whitespace (or the whole line), trailing whitespace after it is tolerated (more forgiving than raw backslash-newline); a braced/quoted -& is data ({-&} is the escape for a literal trailing value); -& mid-line, -& as a word suffix (abc-&), and -& on lines inside still-open braced/quoted values are all data. Backslash continuation authoring is unchanged (continuation is additive). New testsuite recordcontinuation.test; define doc documents the token alongside backslash continuation. #0.7.0 - G-045: record-continuation token -& - an unquoted trailing -& element on a definition record line continues the record on the next line. Implemented in private::split_definition_records: the token is dropped and the next line joins after a single space with its leading whitespace collapsed, exactly how the Tcl parser joins backslash-newline continuations before a braced definition reaches the splitter - so a -& record assembles byte-identical to its backslash-continued equivalent (proven by parse+render equality test). Motivation: constructed/string-built definitions cannot author backslash-newline ergonomically (the building code's own quoting consumes it); -& is plain text and survives any construction. Collision rules: the token must be a bare word preceded by whitespace (or the whole line), trailing whitespace after it is tolerated (more forgiving than raw backslash-newline); a braced/quoted -& is data ({-&} is the escape for a literal trailing value); -& mid-line, -& as a word suffix (abc-&), and -& on lines inside still-open braced/quoted values are all data. Backslash continuation authoring is unchanged (continuation is additive). New testsuite recordcontinuation.test; define doc documents the token alongside backslash continuation.

Loading…
Cancel
Save