diff --git a/goals/G-055-tclcore-regen-workflow.md b/goals/G-055-tclcore-regen-workflow.md index 3b275b3b..5c088c37 100644 --- a/goals/G-055-tclcore-regen-workflow.md +++ b/goals/G-055-tclcore-regen-workflow.md @@ -149,11 +149,18 @@ other commands including the multi-form ::after. G-074 sanction then covers exactly that id-shaped witness rather than the whole form pair. APPLIED 2026-07-13 (tclcore moduledoc 0.3.0, user-directed): both ids typed stringstartswith() via a build-time - %AFTERIDPREFIX% string map (note for the workflow: tstr ${...} in - non-display fields is NOT expanded for plain PUNKARGS definitions - G-046 - defers display fields only; parse-field interpolation needs the explicit - tstr wrap, as ::tcl::string::is does, or a string-map token); parity pins - in tclcoreparity.test including the accepted dead-id divergence. + %AFTERIDPREFIX% string map. CORRECTED note for the workflow (the first + record of this wrinkle misattributed it): parse-field tstr ${...} IS + expanded for plain registered PUNKARGS definitions - but in the DEFSPACE, + which is the argdoc subnamespace whenever one exists, even when the + PUNKARGS list lives in the parent namespace (update_definitions evalns + rule); a variable set in the parent is unresolvable there and the param is + left SILENTLY literal (diagnostic gap - a literal ${...} reaching a parse + field is always an authoring error and could warn). Load-time-computed + values are safest via build-time substitution (explicit tstr wrap as + ::tcl::string::is, or a string-map token as ::after) or by setting the + variable in the argdoc namespace. Parity pins in tclcoreparity.test + including the accepted dead-id divergence. - 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 diff --git a/src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm b/src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm index 6c37291e..b1ace07b 100644 --- a/src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm +++ b/src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm @@ -4542,10 +4542,14 @@ tcl::namespace::eval punk::args::moduledoc::tclcore { set after_id_prefix $_aip_prefix } } - #the %AFTERIDPREFIX% token below is substituted at build time (string map) because - #tstr ${...} in non-display fields is not expanded for plain PUNKARGS definitions - #(G-046 defers display fields only; the %TYPECHOICES% pattern in punk::args' own - #argdocs is the precedent) + #the %AFTERIDPREFIX% token below is substituted at build time (string map; the + #%TYPECHOICES% pattern in punk::args' own argdocs is the precedent). A tstr + #${$after_id_prefix} in the -type field would also work, BUT only if the variable + #lives in the DEFSPACE the definition resolves in - for registered PUNKARGS + #definitions that is the argdoc subnamespace whenever one exists (even when the + #PUNKARGS list itself is in the parent, as here), and an unresolvable param is + #left silently literal. Build-time substitution avoids the defspace subtlety + #entirely for load-time-computed values like this harvest. # -- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- lappend PUNKARGS [list [string map [list %AFTERIDPREFIX% $after_id_prefix] { #test of @form diff --git a/src/modules/punk/args/moduledoc/tclcore-buildversion.txt b/src/modules/punk/args/moduledoc/tclcore-buildversion.txt index c85c2e1e..afdec034 100644 --- a/src/modules/punk/args/moduledoc/tclcore-buildversion.txt +++ b/src/modules/punk/args/moduledoc/tclcore-buildversion.txt @@ -1,5 +1,5 @@ 0.3.0 #First line must be a semantic version number #all other lines are ignored. -#0.3.0 - ::after cancel-id discrimination (user-directed 2026-07-13): the cancelid and info forms' id argument is typed stringstartswith() with the prefix harvested from the RUNNING interpreter at define time (safe create+cancel probe 'after 999999 {}' / 'after cancel $id' - G-054 technique; prefix is after# on 8.6.11 and 9.0.3, hardcoded after#%d in tclTimer.c), substituted into the definition via a build-time %AFTERIDPREFIX% string map (tstr ${...} in non-display fields is not expanded for plain PUNKARGS definitions - G-046 defers display fields only). -typesynopsis id keeps the synopsis rendering as the man page's 'id'. Effect under G-041 form candidacy: 'after cancel ' resolves cleanly to the cancelscript form matching real semantics (real 'after cancel' with a non-id is a silent script-match no-op), and 'after info ' is model-rejected where real errors at runtime (parity-true); an id-SHAPED word after cancel remains truthfully ambiguous (cancelid+cancelscript) - real Tcl resolves that junction by id liveness at runtime, which no static type expresses; dead-id over-acceptance on 'after info' recorded as the accepted runtime-liveness boundary. Both ids gain man-page-derived -help text. Parity pins added in tclcoreparity.test (id-shape harvest agreement, cancel discrimination incl the liveness ambiguity, info error-vs-ok parity + accepted dead-id divergence). +#0.3.0 - ::after cancel-id discrimination (user-directed 2026-07-13): the cancelid and info forms' id argument is typed stringstartswith() with the prefix harvested from the RUNNING interpreter at define time (safe create+cancel probe 'after 999999 {}' / 'after cancel $id' - G-054 technique; prefix is after# on 8.6.11 and 9.0.3, hardcoded after#%d in tclTimer.c), substituted into the definition via a build-time %AFTERIDPREFIX% string map (CORRECTED finding: parse-field tstr IS expanded for registered PUNKARGS definitions, but in the argdoc subnamespace when one exists - a variable set in the parent namespace is unresolvable there and the param is left silently literal; build-time substitution sidesteps the defspace subtlety). -typesynopsis id keeps the synopsis rendering as the man page's 'id'. Effect under G-041 form candidacy: 'after cancel ' resolves cleanly to the cancelscript form matching real semantics (real 'after cancel' with a non-id is a silent script-match no-op), and 'after info ' is model-rejected where real errors at runtime (parity-true); an id-SHAPED word after cancel remains truthfully ambiguous (cancelid+cancelscript) - real Tcl resolves that junction by id liveness at runtime, which no static type expresses; dead-id over-acceptance on 'after info' recorded as the accepted runtime-liveness boundary. Both ids gain man-page-derived -help text. Parity pins added in tclcoreparity.test (id-shape harvest agreement, cancel discrimination incl the liveness ambiguity, info error-vs-ok parity + accepted dead-id divergence). #0.2.0 - G-054: 'string is' class choices (and the generated per-class virtual docids) are harvested from the RUNNING interpreter at define time instead of a hand-maintained list - a deliberately invalid probe of the builtin yields the authoritative class set from its error message (8.6: 21 classes, no dict; unreleased 8.7: +dict +unicode; 9.0: +dict, unicode removed), fixing accept/reject drift such as the doc wrongly accepting 'string is dict' under 8.6. Hand-written man-page descriptions apply only to classes the runtime accepts (generic label for unrecognized future classes); static version notes on dict (not in 8.6) and unicode (unreleased 8.7 only). Parity pinned by tclcoreparity.test with expectations derived from the live interpreter (green on 8.6.13, 8.7a6, 9.0.3)