Browse Source

tclcore 0.3.1: after id-shape harvest via tstr placeholders in the argdoc defspace (project 0.12.18)

Authoring-style change only, user-directed: the interim %AFTERIDPREFIX%
string map is replaced by plain tstr placeholders, with the harvest
variable set in the argdoc namespace - the defspace registered PUNKARGS
definitions resolve placeholders in when an argdoc child exists (the
rule documented in punk::args 0.11.1). One mechanism now serves both
the -type parse field (expanded at first resolve) and the -help display
fields (expanded at display time); the module showcases the placeholder
style, with string map reserved for genuinely build-time cases.

Behaviour identical to 0.3.0: resolved -type stringstartswith(after#),
form discrimination, parity pins and rendered help re-verified.
punk::args 201/202 (1 pre-existing skip), punk::ns 57/57. G-055
workflow note updated to name the tstr-in-argdoc pattern as preferred.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 2 days ago
parent
commit
2e603413e6
  1. 4
      CHANGELOG.md
  2. 8
      goals/G-055-tclcore-regen-workflow.md
  3. 2
      punkproject.toml
  4. 39
      src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm
  5. 3
      src/modules/punk/args/moduledoc/tclcore-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.18] - 2026-07-13
- tclcore moduledoc 0.3.1 (authoring-style only): the after id-shape harvest is consumed via tstr placeholders with the variable in the argdoc namespace (the defspace), replacing the interim string-map token - the module now showcases the neater placeholder style documented in punk::args 0.11.1, with string map reserved for when build-time substitution is genuinely necessary. Behaviour unchanged.
## [0.12.17] - 2026-07-13
- punk::args 0.11.1 (documentation-only): the define help now documents the two definition-registration styles (direct define vs lazy PUNKARGS/register::NAMESPACES registration used by module templates and moduledocs) and the tstr interpolation rules - display-field deferral, parse-field expansion at first resolve, the defspace rule (argdoc child namespace wins when present), the silent-literal fallback for unresolvable placeholders, and the safe patterns for load-time-computed values.

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

@ -157,9 +157,11 @@ other commands including the multi-form ::after.
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
values work via plain tstr placeholders with the variable set in the
argdoc namespace (the ::after harvest showcases this since tclcore 0.3.1,
user-preferred style), or via build-time substitution (explicit tstr wrap
as ::tcl::string::is; string-map token reserved for when build-time
substitution is genuinely necessary). 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

2
punkproject.toml

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

39
src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm

@ -4535,23 +4535,24 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
#accept/reject parity follows the interpreter these docs load into.
#The probe is safe: create + immediately cancel - no event loop entry, no output,
#no lasting state.
set after_id_prefix "after#"
if {![catch {after 999999 {}} _aip_id]} {
catch {after cancel $_aip_id}
if {[regexp {^(.+#)\d+$} $_aip_id -> _aip_prefix]} {
set after_id_prefix $_aip_prefix
#The harvested prefix is consumed via tstr ${$after_id_prefix} placeholders in the
#::after definition below. The variable MUST live in the argdoc namespace: that is
#the DEFSPACE registered PUNKARGS definitions resolve their placeholders in
#whenever an argdoc child exists - even when the PUNKARGS list itself is in the
#parent, as here - and an unresolvable param is left silently literal (see the
#'Interpolation and the defspace' section of the punk::args::define help).
namespace eval argdoc {
set after_id_prefix "after#"
if {![catch {after 999999 {}} _aip_id]} {
catch {after cancel $_aip_id}
if {[regexp {^(.+#)\d+$} $_aip_id -> _aip_prefix]} {
set after_id_prefix $_aip_prefix
}
}
unset -nocomplain _aip_id _aip_prefix
}
#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] {
lappend PUNKARGS [list {
#test of @form
@id -id ::after
@cmd -name "Built-in: after"\
@ -4599,10 +4600,10 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
#An id-SHAPED word remains genuinely ambiguous with a script of the same text -
#real Tcl resolves that by id liveness at runtime (tries the id first, falls
#back to script match), which no static type expresses (goals/G-055 record).
id -type stringstartswith(%AFTERIDPREFIX%) -typesynopsis id -help\
id -type stringstartswith(${$after_id_prefix}) -typesynopsis id -help\
"Identifier of the delayed command to cancel.
It must have been the return value from a previous
after command (%AFTERIDPREFIX%N shaped)."
after command (${$after_id_prefix}N shaped)."
#@form -form {cancelscript} -synopsis "after cancel script ?script...?"
@ -4627,13 +4628,13 @@ tcl::namespace::eval punk::args::moduledoc::tclcore {
@values -min 0 -max 1
#real 'after info <non-id-shaped-word>' errors at runtime ("event ... doesn't
#exist") - the model's shape rejection keeps error-vs-ok parity
id -optional 1 -type stringstartswith(%AFTERIDPREFIX%) -typesynopsis id -help\
id -optional 1 -type stringstartswith(${$after_id_prefix}) -typesynopsis id -help\
"Identifier of an existing event handler - the
return value from some previous call to after
(%AFTERIDPREFIX%N shaped). It must not have triggered
(${$after_id_prefix}N shaped). It must not have triggered
yet or been canceled."
}] "@doc -name Manpage: -url [manpage_tcl after]"\
} "@doc -name Manpage: -url [manpage_tcl after]"\
{
@examples -help {
This defines a command to make Tcl do nothing at all for N seconds:

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

@ -1,5 +1,6 @@
0.3.0
0.3.1
#First line must be a semantic version number
#all other lines are ignored.
#0.3.1 - authoring-style only (user-directed): the ::after id-shape harvest is consumed via tstr ${$after_id_prefix} placeholders instead of the 0.3.0 build-time %AFTERIDPREFIX% string map - the harvest variable now lives in the argdoc namespace (the defspace registered PUNKARGS definitions resolve placeholders in when an argdoc child exists; see the punk::args::define 'Interpolation and the defspace' help section), which makes plain placeholders work in both the -type parse field (expanded at first resolve) and the -help display fields (expanded at display time). This module showcases the tstr style; string map remains reserved for cases where build-time substitution is genuinely necessary. Behaviour identical to 0.3.0 (resolved -type, form discrimination, parity pins and help renders re-verified).
#0.3.0 - ::after cancel-id discrimination (user-directed 2026-07-13): the cancelid and info forms' id argument is typed stringstartswith(<prefix>) 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 <non-id-shaped-word>' 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 <non-id>' 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)

Loading…
Cancel
Save