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 2 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`
"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
- 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
2026-07-08 (punk::args 0.2.3) - noted here because form-attempt logic will exercise
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).

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
harvesting error metadata). This does not relax cmdhelp's rule of never calling
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).

2
punkproject.toml

@ -1,4 +1,4 @@
[project]
name = "punkshell"
version = "0.12.8"
version = "0.12.9"
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 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 {
"literal(*" {
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
#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.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.

Loading…
Cancel
Save