From 58da288d453e37cb4f39a86c59fe14c0b022c266 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Sun, 12 Jul 2026 22:02:17 +1000 Subject: [PATCH] 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 --- CHANGELOG.md | 4 +++ goals/G-041-punkargs-form-matching.md | 28 ++++++++++++++++++ goals/G-055-tclcore-regen-workflow.md | 40 ++++++++++++++++++++++++++ punkproject.toml | 2 +- src/modules/punk/args-999999.0a1.0.tm | 2 +- src/modules/punk/args-buildversion.txt | 3 +- 6 files changed, 76 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ef695b2..f976e686 100644 --- a/CHANGELOG.md +++ b/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). diff --git a/goals/G-041-punkargs-form-matching.md b/goals/G-041-punkargs-form-matching.md index d7971ce8..c6f51f54 100644 --- a/goals/G-041-punkargs-form-matching.md +++ b/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). diff --git a/goals/G-055-tclcore-regen-workflow.md b/goals/G-055-tclcore-regen-workflow.md index b7a41cfd..c7537b7a 100644 --- a/goals/G-055-tclcore-regen-workflow.md +++ b/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). diff --git a/punkproject.toml b/punkproject.toml index d640f65d..b07a2f75 100644 --- a/punkproject.toml +++ b/punkproject.toml @@ -1,4 +1,4 @@ [project] name = "punkshell" -version = "0.12.8" +version = "0.12.9" license = "BSD-2-Clause" diff --git a/src/modules/punk/args-999999.0a1.0.tm b/src/modules/punk/args-999999.0a1.0.tm index f8644005..2b5a9ead 100644 --- a/src/modules/punk/args-999999.0a1.0.tm +++ b/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] diff --git a/src/modules/punk/args-buildversion.txt b/src/modules/punk/args-buildversion.txt index 8378f85d..383f56ca 100644 --- a/src/modules/punk/args-buildversion.txt +++ b/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 '' against 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.