User-directed removal of the two pipeline-based development iterations flagged
as candidates in the 0.7.0 hygiene pass. Both were undocumented, unexported
global aliases superseded by the plain proc nscommands (which adds
weird-namespace handling); no callers existed in the repo and nscommands1 was
broken - it referenced nonexistent commands (nsthis2, inspect at global
scope) and errored on first use. Recoverable via git history.
ns/nav/repl suites 111/111 green. buildversion 0.7.1 -> 0.8.0 (command
removal); project 0.12.36 + CHANGELOG (patch - the aliases were never part of
the documented shell contract).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
User-approved from the serversecretsync tm-path-shadowing incident
(8.6 require-before-path-setup silently selected a stale tomlish from the
machine-default tm paths over the project's vendored copy). Default
mechanism per user: vendor punk::libunknown + punk::packagepreference as
the light layout's bootstrap minimum. Related: G-012/G-027/G-029/G-035.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
The -return summarydict choicelabel claimed the linemap key appears only when
-n is supplied - actual behaviour (pinned in ansi/grepstr.test since the
characterization wave) is that linemap is always present. Help text corrected
to match; deferred-to-the-hygiene-pass item from the grepstr characterization.
ansi suites 63/63 green. buildversion 0.1.2 -> 0.1.3; project 0.12.35 +
CHANGELOG.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Both fixes were pinned as known warts before the change; the pins flip with it:
- get_nslist: the -types default sat inside a braced dict-create literal, so it
was the literal string $known_types - any call without -types (notably bare
user-level `nslist`) errored with the malformed "Unrecognised namespace
member type: $known_types". An unsupplied -types now defaults to all known
member types. Pin flipped: nslist.test nslist_types_default_error_QUIRK ->
nslist_types_default (bare nslist renders children + commands). nslist's own
PUNKARGS -types gained help text, deliberately without a -default so the
passthrough stays absent when unsupplied.
- nsjoinall: the non-empty-prefix-vs-absolute-path error message carried a
copy-paste "nsjoin:" prefix - now "nsjoinall:". Pin flipped in
nsprimitives.test nsjoinall_edges.
- nscommands: commented out a development trace puts (">>> base: ...") that
polluted stdout on the weird-namespace path (same treatment as the 0.1.1
doc-lookup trace-puts cleanup; not pinned).
Suites green: ns/nav/repl 111/111; full source-tree run at the known baseline
(only core exec-14.3, pre-existing). buildversion 0.7.0 -> 0.7.1; project
0.12.34 + CHANGELOG.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
No change to shipped behaviour (ns+nav+repl suites 111/111 green). Minor bump
because unused procs were removed.
Removed (~1200 lines, recoverable via git history):
- the fully commented-out predecessor help command arginfo (~930 lines) -
its own header marked it replaced by cmdhelp
- superseded/deprecated name-primitive twins nsparts1, nsprefix1,
nsprefix_orig, nstail1, nstail_orig (never exported, no callers; NOT
drop-in equivalents - divergence on 5+ colon runs was pinned in
nsprimitives.test before removal; those divergence pins are removed with
the twins and replaced by a direct nsprefix long-colon-run pin - the
divergence record lives in the test file history, commit 0c7168a1)
- obsolete nsglob_as_re1, empty stubs ns_relative_to_location/
ns_absolute_to_location, obsolete internal::_pkguse_vars, an if-0 block in
nstree_list, unused interp_aliases assignment in get_ns_dicts, assorted
commented-out remnants (pre-punk::args defaults blocks, pipedata width
calcs, corp default-retrieval experiments, cmdtrace trace-add alternates)
Documentation accuracy:
- nsprefix comment corrected (:::a prefix is :: not ::a - per pins)
- nsglob_as_re stale "should be fixed" note replaced with an accurate
description of the in-place negative-lookahead fix (* matches an inner
single colon, never crosses ::)
- nstree_list previously empty PUNKARGS summary/help fields filled
- nstail gained a doc comment; nseval_ifexists gained the native-vs-punk
p:::x divergence note; the dangling "string functions" sentence completed
- cmdhelp help typos (auto-gnerated, commands commands); corp -syntax help
truncated sentence completed
Flagged, deliberately unchanged (comment only):
- get_nslist red-strike/masked alias display branches appear unreachable
from current bucket derivation (nslist.test fixture findings) - reassess
at the planned display rework
- nscommands1/nscommands2 pipeline aliases: superseded removal candidates
(nscommands1 errors on first use - references nonexistent nsthis2)
- cmdtrace stdout/stderr noise deferred to proposed G-085
- test_switch* marked as live cmdtrace test fixtures (used by cmdtrace.test)
All inserted/updated comments carry 2026-07-14 Agent-Generated/Agent-Updated
marks. buildversion 0.6.0 -> 0.7.0. Known-wart fixes sanctioned by the pinned
backlog (nslist -types default, nsjoinall error prefix) follow separately.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
New shell/testsuites/punkexe/shellnavns.test - one hang-proof punk_run piped
session against the built executable in src launch mode (dev modules, so
current punk::repl/nav source is exercised without a rebuild):
PROVEN end-to-end:
- n/ sets ::punk::nav::ns::ns_current in the running shell
- the current namespace continues into `subshell punk` and back out, and the
0.5.2 stray-namespace seeding fix holds live (no phantom ns_current
namespace in the subshell context)
- navigation inside the subshell takes effect; the session exits cleanly
(exitcode 0, no timeout, exit unwinds subshell -> parent -> terminate)
FINDINGS from the E3 probes (pinned in the test with markers):
- the FIRST subshell evaluates in the SAME code interp as the parent shell -
the "first subshell asymmetry" already TODO-documented in repl::init.
Continuity therefore manifests via shared state at shell level (SHARED
marker: a variable set in the subshell is visible to the parent after
exit, and the parent adopts the subshell''s final namespace). The
repl::start seeding template - the carry-over mechanism for genuinely
separate code interps - is proven at module level in punk/repl
nscurrent.test. A G-002-style isolated subshell would flip these pins.
- KNOWN GAP: piped-session submissions evaluate at :: even after n/ sets
ns_current - the inscope application proven interactively and at module
level does not take effect for subsequent piped submissions in this spawn
shape. Root cause undetermined (piped-path evaluation vs kit/codethread
interplay); flagged for investigation alongside G-038 session-context
work.
Verified: shell/** suite 42 total / 40 passed / 0 failed (2 gated skips)
under Tcl 9.0.3. AGENTS updated (shell contracts bullet; punk/repl module
index E3 reference).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Groundwork requested for the future rework of n/ n// n/// output to punk
tables with terminal-width responsiveness: the underlying display elements
and navigation/retention semantics are now pinned so only deliberately
REWORK-flagged pins flip when the layout changes.
- punk/ns nslist.test (11): tier A pins get_ns_dicts classification buckets
(the machine contract: children/packagetails/packageprefixes per the
package-names derivation, commands/exported/imported/aliases/procs/
ensembles/oo/coroutines/usageinfo incl alias-target doc resolution and its
update_definitions dependence, alias edge cases, native via builtin ns);
tier B pins per-element marking layout-agnostically (underline/underdouble/
underdotted package styles, type tag colours, exported green / imported -I
under overtype SGR canonicalization, the punkargs doc icon); tier C pins
the CURRENT hardcoded 2-col/4-col layout and nspath subtables, explicitly
flagged to flip with the rework; KNOWN QUIRK pinned: bare nslist without
-types errors with a malformed message (literal dollar-known_types).
Findings recorded: the red-strike/masked alias display branches appear
unreachable from current bucket derivation.
- punk/nav/ns navns.test (6, new suite dir): ns/ navigation state machine
(absolute/relative/glob-no-nav/failed-nav preservation/quad-colon
normalization), v-form content selection, ensemble annotation, and the
::punk::nav::ns::ns_current variable contract.
- punk/repl nscurrent.test (4): repl retention proven against a REAL
codethread (repl::init, synchronous runscript sends): inscope evaluation,
retention across submissions, n/ navigation retained, auto-create-with-
notice for a missing current namespace.
- punk::repl 0.5.2 fix: repl::start''s codethread seeding template
namespace-eval''d the full ns_current VARIABLE name, creating a stray
namespace of that name in every code interp (phantom child in n/ listings
of the nav namespace). Now ensures only the parent ::punk::nav::ns.
Subshell namespace carry-over semantics unchanged; pinned behaviourally
(mirrored template) plus a source-text guard, pending the planned
end-to-end piped subshell test (E3) at the shell level.
Verified: punk/ns 80/80, punk/nav 6/6, punk/repl 7/7 under Tcl 9.0.3;
nslist/navns/nscurrent green under 8.7. Console queries mocked throughout
(the render path consults get_size/get_tabstops). Project 0.12.33 +
CHANGELOG; tests AGENTS.md index updated.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
New check pair in punk::lib::check, in the established style (G-076
console_deadspin exemplar: pure facts-in/verdict-out classifier
tclbug_nestedswitch_tracelines_signature + has_tclbug_* proc returning the
standard buginfo dict {bug bugref description level}, auto-surfaced through
the help tcl warning report via the info procs enumeration).
Unlike the version-gated checks, this one is a LIVE behavioural probe (safe
and cheap - no console interaction): a dynamically-defined proc with a
nested 4-word switch is enterstep-traced for inner arm 1 (affected split-
list index 1 - reports line 3) and the default arm (index 5, beyond the
command word count - reports line 2 correctly), matched against the
characterized wrong/right signature. Detection therefore flips
automatically on a Tcl runtime containing the upstream fix - no fixed_in
version variable needed. Probe layout note: a 2-arm probe would put the
default body at affected index 3 and hide the signature (both arms
mismark) - the middle arm exists to push default to safe index 5 (this
initially bit the probe itself, confirming the law once more).
Followup-when-fixed documented in the proc: re-verify with
scriptlib/developer/tcl_switch_traceline_repro.tcl, flip the
ns/cmdtrace.test GAP pins and fixed-canary, update the punk::ns::cmdtrace
argdoc caveat, note the fixed-in version. New canary test
cmdtrace_upstream_bug_check_canary asserts the check still reports the bug
- on a fixed Tcl it fails first and points at that workflow.
Verified: punk/ns 69/69 and punk/lib 37/37 under Tcl 9.0.3; cmdtrace.test
6/6 under 8.7; all has_tclbug_*/has_libbug_* procs exercised help-tcl style
without error; probe self-cleans (no temp proc/var residue).
punk::lib buildversion 0.4.3 -> 0.5.0 (new API procs = minor); project
0.12.32 + CHANGELOG; tests AGENTS.md index updated.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
User-approved wording (drafted from the 2026-07-14 cmdtrace investigation):
an attribution option (live|sourcefile|auto - sourcefile mode doubling as a
practical workaround for the upstream nested-switch mis-attribution, tcl
tktview 5d5b1052280c976ea3d4) and a documented machine dict return for
future hotspot-analysis/repl consumers (implies no interactive pause).
Context records the unexplored regime boundary (module .tm procs trace
relative, plain sourced procs absolute) as goal groundwork. goals_lint clean.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
cmdtrace gains -pause <bool> (default 1, existing interactive behaviour
unchanged): -pause 0 bypasses the "paused - hit enter key to continue"
askuser in _cmdtrace_leave so cmdtrace runs unattended and the marked-up
traced-body report (return value) can be captured by scripts and tests.
Line-mismark investigation (all punk-free raw enterstep probes, results
byte-identical on Tcl 8.6.17 / 8.7a6 / 9.0.3 - conclusively upstream):
- with the ::switch argdoc available (punk::args::moduledoc::tclcore),
cmdtrace marks flat single-block switches and 2-word-form nested switches
correctly for EVERY arm; the residual mismarks match the raw trace data
- empirical law, predictive across 4 command shapes x 6 arms and deeper
nestings: an arm body at index j of the split pattern/body list reports
container-relative lines (shift = switch command line within its
containing script - 1) exactly when j lands on a LITERAL word of the
switch command (options, --, or the block); dynamic words and j beyond
the command word count fall back to correct arm-relative attribution.
Position-based, not call-order based. Explains the ticket observation
that the affected-arm count "varies based on switch options".
- suspected machinery: Tcl_SwitchObjCmd passes split-list index j as the
TclNREvalObjEx word; TclInitCompileEnv adopts ctx line[word] when the
index happens to be in range of a per-word array describing the switch
command itself (the splitObjs munging looks intended to prevent this)
Artifacts:
- new tests ns/cmdtrace.test (5): -pause 0 smoke + error-call count,
flat-switch and 2-word-nested correct-mark guards, upstream mismark GAP
pins (gated on have_tclcoredocs - cmdtrace arm-offset correction parses
supplied switch commands against the ::switch argdoc)
- scriptlib/developer/tcl_switch_traceline_repro.tcl: standalone pure-Tcl
repro + findings write-up, suitable as an attachment/addition to the
upstream ticket
- cmdtrace argdoc caveat updated: cites the ticket and the characterized
pattern (was "possibly an unreported bug")
Verified: punk/ns suite 68/68 under Tcl 9.0.3; cmdtrace.test 5/5 under 8.7.
punk::ns buildversion 0.5.0 -> 0.6.0 (new flag = minor); project 0.12.31 +
CHANGELOG; tests AGENTS.md index updated.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
New punk/ansi suites and extended punk/ns corp.test, characterizing the
corp -> grepstr -> untabify tab-handling interaction and the standalone
behaviours. All console-query paths are mocked (the overtype renderline.test
get_tabstops pattern, extended to get_size + punk::console::tabwidth): the
-untabify none / -stops terminal / -stops <int> paths consult
punk::console::get_tabstops / get_size which emit live terminal queries that
block or error in headless/piped environments - mocking is the answer to
probing that behaviour deterministically in the test harness.
- ansi/untabify.test (10): -stops 8/int/list/terminal(mocked), -with
spaces/unicode(U+21E5 + U+00A0)/custom pair, multiline, usage errors, and
the EXPERIMENTAL -plastic elastic-tabstop mode: basic expansion, the
cross-line column-alignment property, -with composition - pinned as
interim behaviour (hardcoded 8-multiple stops marked temp in source) and
deliberately retained for possible future repl editbuf use.
- ansi/grepstr.test (9): return modes incl summarydict (linemap pinned
always-present; -help claims -n-only - reconciliation deferred to the
hygiene pass), exact highlight SGR wrapping, -n numbering with * match
counts, invert + empty-highlight strip, -C context with break indicators,
capture groups, and tab handling: warns once per call on stderr, a simple
single-pass tab line survives with correct highlights.
- ns/corp.test (+6): basic-highlight ansistrip equivalence with -syntax none,
-n numbering, -untabify spaces exact tab-free expansion + proper 4-element
-syntax none list, -untabify unicode markers, the KNOWN DEFICIENCY pin for
default -untabify none on a tabbed body (5 grepstr warnings; brace overlays
mangle tabbed lines - leaked/shifted close-braces pinned exactly under
mocked tabstops; flip deliberately if tab handling is fixed or corp moves
off grepstr), and a ::tcl::CopyDirectory -untabify spaces smoke test
(tab-free, warning-free, constraint-guarded).
src/tests/modules/AGENTS.md punk/ansi + punk/ns index entries updated.
Verified: punk/ansi 63/63 and punk/ns 63/63 under Tcl 9.0.3; the three
new/extended files green under Tcl 8.7 (10/10, 9/9, 13/13).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
The stringstartswith(*) arm of get_dict_can_assign_value''s multi-member
tail-clause reservation walk compared the prefix against $tp (the type
string) instead of $rv (the candidate value). Consequences (pinned pre-fix
in the previous commit):
- a later optional clause mixing literal(...) and stringstartswith(...)
members never had genuinely matching trailing words reserved - a greedy
-multiple argument swallowed them and the tail clause was silently lost
- a prefix text that happened to prefix the literal type string (e.g str vs
stringstartswith(str)) wrongly reserved NON-matching words, raising a
spurious toomanyarguments overflow
One-token fix; allocation_tailclause_ssw_reservation_GAP flipped to the
correct expectations (matching words reserved; non-matching words left to
the -multiple argument with no error); literal-only and no-match guards
unchanged. Known remaining approximation (pre-existing, noted in the
buildversion changelog): the walk still requires a literal* member in the
tail clause''s typelist to run at all - a stringstartswith-only tail clause
is not reserved.
Verified: full punk/args suite 231 total / 227 passed / 0 failed (4 intended
skips), punk/ns 57/57 under Tcl 9.0.3; allocation.test 10/10 under Tcl 8.7.
punk::args buildversion 0.12.2 -> 0.12.3; project 0.12.30 + CHANGELOG.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
allocation.test gains coverage for the get_dict_can_assign_value multi-member
tail-clause reservation walk (a later optional clause mixing literal(...) and
stringstartswith(...) members, guarded from a greedy -multiple argument):
- guards (must hold pre and post fix): literal-only tail clause reserved
correctly; non-matching trailing words correctly left to the -multiple arg
- allocation_tailclause_ssw_reservation_GAP pins the current bug: the
stringstartswith(*) arm matches against $tp (the type string) instead of
$rv (the value), so matching trailing words are swallowed by the -multiple
argument (optional tail clause lost), and a prefix text that happens to
prefix the type string (str vs stringstartswith(str)) wrongly reserves
non-matching words, ending in a spurious toomanyarguments overflow
Verified 10/10 against the unfixed module under Tcl 9.0.3.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
No behaviour change (full args suite 224/0-failed, ns 57/57, 8.7 spot check).
Removed (~1100 lines of commented-out code, recoverable via git history):
- old-signature private::check_clausecolumn ("old version")
- _check_clausecolumn2 "interim version" experiment (its collect-all-clause-
failures idea preserved as an in-place note for G-072-era rework)
- pre-parsekey opts ordering loop, pasted opts-logic reference copy in the
values loop, disabled XXXliteral* switch arm, misc debug remnants
MAINTENANCE comments: the leaders/values loop-parity notes expanded into a
dated refactor assessment - shared clause-assignment helper extraction is
indicated (drift is real: G-082 duplicated its selection block, parsekey
idents exist only values-side) but deferred to ride the G-084 parity work
under the G-046 hot-path constraints; mirror-or-justify rule stated at both
loops. The old "synchronize with values loop" todo folded into it.
Documentation accuracy:
- get_dict PUNKARGS + doctools: real result keys (leaders opts values
received solos multis id form, + formstatus when candidacy ran); directive
lists corrected beyond @cmd/@leaders/@opts/@values; false primordial claims
removed ("only supports -flag val pairs, not solo options" / "only the
last value is used")
- parse -help example fixed (dict exists bracketing was broken) and header
dofilestuff example fixed (stray trailing 1 after -type existingfile);
punk::parse -> punk::args::parse; typos
- goal cross-refs annotated at code sites (G-053 -multiple ranges, G-084
values/leaders parsekey incompleteness); wrong test-name comments corrected
to parsekey_repeat_ordering
Flagged, deliberately unfixed (comment only, no pins exist): suspected bug in
the multi-member clause "stringstartswith(*" arm of get_dict_can_assign_value
- it matches against $tp (the type string) instead of $rv (the value), so it
always succeeds.
All inserted/updated comments carry 2026-07-14 Agent-Generated/Agent-Updated
marks. punk::args buildversion 0.12.1 -> 0.12.2; project 0.12.29 + CHANGELOG.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
A word rejected by an OPTIONAL argument's allocation screen (G-071
restricted-choice screen, or the basic int/double/bool/number/dict type
screen) that subsequently overflows the argument list now reports the
specific rejection instead of "Received more values than can be assigned":
- choiceviolation naming the word and allowed choices, with -badarg/-badval
and the final-validation message template (Leading/Trailing argument
attribution), for choice-screen rejections
- typemismatch naming the declared (normalized) type, with -badarg/-badval,
for basic-type screen rejections
Mechanism: private::get_dict_can_assign_value returns a 'rejection' record
(single-member clauses only; literal-category mismatches deliberately not
recorded); get_dict_form collects first-rejection-per-word from the three
allocation call sites (leader/value split trial, leaders loop, values loop)
into a per-form store; the two toomanyarguments raise sites prefer a
recorded rejection for the unassignable word. Genuinely-surplus words still
report toomanyarguments; words consumed by later slots parse unchanged;
multiform selection (rank_form_failures) untouched.
errorselection.test _GAP pins flipped to the pointed expectations (pre-fix
behaviour pinned in the previous commit). punk::args buildversion
0.12.0 -> 0.12.1.
Closeout: G-082 flipped achieved + archived (index -> GOALS-archive.md,
detail -> goals/archive/ with mechanism/verification/residue recorded);
G-073 note updated to point at the new rejection-record seam;
src/modules/AGENTS.md authoring-ergonomics bullet re-documented (-optional 0
workaround no longer needed on >= 0.12.1; make.tcl keeps it - it runs
against the bootsupport 0.12.0 snapshot). Project version 0.12.28 +
CHANGELOG.
Verified: full punk/args suite 228/224/0-failed (4 intended skips) and
punk/ns 57/57 under Tcl 9.0.3; errorselection.test 7/7 under Tcl 8.7;
make.tcl frobnicate still pointed; goals_lint clean.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Set G-082 active (user-directed). New errorselection.test pins current
single-form error selection before the fix:
- _GAP pins: an optional choice-restricted leader (make.tcl dispatch shape),
an optional choice-restricted value, and an optional typed (int) value each
reject a word that then overflows - all report generic
{toomanyarguments N index I} with no -badarg/-badval attribution,
discarding the specific rejection (regression vs punk::args 0.5.0)
- reference pins: the -optional 0 equivalents report pointed choiceviolation
with -badarg/-badval and Leading/Trailing argument messages
- guards: genuine surplus stays toomanyarguments; a rejected word consumed by
a later slot parses cleanly; multiform failures still route through
rank_form_failures (noformmatch)
Verified: 7/7 pass against the unfixed module under Tcl 9.0.3.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
The detail files carried richer scope text than the index (user-directed
resolution: enrich the index rather than trim the files). goals_lint now clean.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
The 0.12.22 -> 0.12.23 bump landed in 49ec22a9 (doc: restore 'dev
doc.validate' via wrapped bin/dtplite.cmd, 2026-07-13) without a CHANGELOG
entry, and the doc-tooling/punknative/fontprep work committed between the
0.12.23 and 0.12.24 bumps was likewise unrecorded. Adds the 0.12.23 entry
(dtplite wrap, summarized from the original commit message) and a second
0.12.24 bullet covering the between-bump doc-tooling work and the G-078..G-081
goal drafting - both marked as retrospective additions.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
New proposed goal G-082: single-form parse error selection should prefer an
optional argument's specific choice/type rejection over the generic
"more values than can be assigned" overflow (regression vs punk::args 0.5.0
found while dogfooding in G-030; the -optional 0 workaround sites in make.tcl
and the AGENTS authoring-ergonomics bullet are flagged for revisiting when it
ships).
Sibling notes parked in existing proposed goals:
- G-028: make.tcl's name-only process-sweep misidentification (unrelated
same-named executables) - full-path matching rides the same
process-identification infrastructure as the locker report.
- G-025: punk-exe-hosted make.tcl runs the kit's pre-loaded module copies, not
bootsupport snapshots - a self-report surface for running-interp module
provenance would make the mixing observable.
- G-073: reciprocal relation to G-082 (both want the "surface the specific
rejection" seam in validation error reporting).
- G-075: synopsis renders the definition id rather than @cmd -name - consider
a fallback for non-command ids when reworking id rendering.
goals_lint: unchanged (only the pre-existing G-078/G-081 index-mirror findings).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Fixes the two user-reported symptoms of '<punkexe> src/make.tcl shell':
Missing prompts: app-punkscript correctly forces ::tcl_interactive 0 for
script execution, and punk::repl is already loaded at kit boot - so the shell
branch's 'package require punk::repl' re-loads nothing and the repl's
load-time interactivity probe (repl line ~44) never re-runs, leaving prompts
suppressed on a real console. The shell branch now recomputes
::tcl_interactive via punk::repl::is_interactive against the actual input
channel before repl::init/start. Piped behaviour unchanged (probe still
reports non-interactive).
Raw-mode 'invalid command name struct::set': the accelerator-reload block
(package forget + rename of sha1/md5/struct::* so reloads can pick up
accelerators from the restored full paths) relied on later package-require
chains to restore the forgotten packages. Under a kit, the consumers
(punk::lib, punk::du, punk::nav::fs, flagfilter) were pre-loaded at kit boot,
their requires never re-fire, and the first repl-thread-side call of a
struct::set-backed punk::lib proc (lunique_unordered's self-redefining first
invocation, reached via flagfilter/nav::fs in interactive/raw paths) hit the
destroyed command. The block now records which of the forgotten packages were
actually loaded and re-requires them immediately. Verified by thread-probe:
the repl-thread interp now shows struct::set 2.2.5 + sha1 2.0.5 restored
identically under tclsh90 and punk91 (previously absent under punk91); the
codethread was always unaffected (it re-requires its own package list).
src/AGENTS.md: new note - punk-exe-hosted make.tcl runs in a pre-loaded
interp, not a virgin one (no-op requires, load-time state like the
tcl_interactive probe, kit-vs-bootsupport module provenance mixing); interp
surgery in make.tcl must restore deliberately and be tested under both tclsh
and a punk exe. Layout make.tcl copies re-synced via the punkcheck channel.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Self-build guard (user-identified gap): when make.tcl is driven by a built punk
executable, the kit whose deployed executable is the running interpreter is now
skipped before any build work, with guidance to rerun under tclsh or a
different kit. Previously the pre-deploy process sweep matched processes by
executable name only and would taskkill the build's own process mid-run with
-k (suicide: remaining kits unbuilt, no recap, dangling punkcheck event);
without -k only the polite kill's refusal on console processes saved the run,
and the deploy delete of the running image would fail with a cryptic message.
The sweep additionally excludes the build's own pid unconditionally, covering
a same-named copy driving the build from a non-deploy path. Informational and
update subcommands were already safe (they exit before the kit machinery);
building other kits from a punk exe remains supported. Verified live:
'punk91 src/make.tcl vfs -confirm 0' skips punk91.exe with the warning and
processes the other kits normally; plain tclsh runs unaffected.
libunknown quietening: 'punkexe src/make.tcl <cmd>' emitted
"punk::libunknown::init already done - unnecessary call? info frame -1: ..."
because punk_main.tcl had already initialised libunknown (renaming ::package)
and make.tcl unconditionally re-sourced the bootsupport copy and called init
again, tripping init's load-bearing rename guard. make.tcl now leaves an
already-active libunknown in place (no re-source - also avoids silently
swapping the running copy for the bootsupport copy when versions diverge);
plain tclsh runs source+init exactly as before. Verified: no diagnostic from
'punk91 src/make.tcl projectversion'.
src/AGENTS.md build guidance updated (punk-exe-driven builds and the self-kit
skip); layout make.tcl copies re-synced via the punkcheck channel.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Fixes the constructed-definition indent leak user review spotted in
'make.tcl help' output (@cmd -help Description first line rendered less
indented than continuations): HELPTEXTS and the inline shell/help/toplevel
help values are now block-form (first value line whitespace-only) with a bare
@normalize in every constructed definition, per G-045's preferred style. The
braced option fragments never leaked (literal block-shaped parts get
file-style normalization) and head-form values are contractually untouched by
@normalize. Also adds a define-time capability probe (punk::args::get_spec on
representative ids inside the guarded block) so a stale punk::args that
accepts the raw text but cannot resolve a mechanism (e.g pre-@normalize
snapshots) degrades to the plain fallback immediately instead of erroring at
first parse.
AGENTS infrastructure for future documentation work: src/modules/AGENTS.md
"Documentation And Comments" gains a "punk::args definition authoring
ergonomics" subsection (G-045 mechanisms: @normalize + block form for
constructed defs, -unindentedfields, -& record continuation, container
quoting rules; plus the G-030 lessons: explicit @form -synopsis for
script-level ids, -optional 0 for pointed choiceviolation errors);
src/AGENTS.md cross-references it and names ::punkboot::argdoc as the
in-tree constructed-def exemplar. Follow-up recorded in the G-030 archive.
Verified: flush Description rendering for toplevel/subcommand/shell/help
usage; dispatch, degraded mode (PUNKBOOT_PLAIN) and usage-error paths
re-verified; layout make.tcl copies re-synced via 'make.tcl bootsupport
-confirm 0' (punkcheck channel); goals_lint unchanged (only the pre-existing
G-078/G-081 index-mirror findings).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Snapshot refresh via 'make.tcl modules -confirm 0' + 'make.tcl bootsupport
-confirm 0' after punk::args joined the bootstrap-tracked staleness set
(bootsupport had 0.5.0 vs source 0.12.0 - the new gate tripped as designed).
Superseded versions pruned by punkcheck records; project-layout bootsupport
trees and layout make.tcl copies followed via the established punkcheck
channel (punk.basic, punk.project-0.1, punk.shell-0.1).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
All 15 make.tcl subcommands are punk::args-declared ((script)::punkboot +
(script)::punkboot::<sub>, built from single-source summary/help/opt dicts with
shared option fragments and explicit @form -synopsis). Bare make.tcl / -help,
'help ?subcommand?' and '<subcommand> -help' render tabled usage; invalid
flags/subcommands raise punk::args usage errors (exit 1) with unambiguous
prefix resolution; 'shell' is a declared passthrough.
Prompt policy: new -confirm 0|1 (default 1) on every confirmation point
(bootsupport minor-staleness gate, vfscommonupdate REPLACE, kit-type mismatch).
Default prompts only on an interactive terminal and aborts fast with guidance
on non-interactive stdin (::punkboot::lib::stdin_is_interactive; Tcl 8.6 lacks
the -inputmode probe and is assumed interactive). -confirm 0 proceeds
(staleness, REPLACE) or skips the mismatched kit. The piped-y agent workflow is
retired; the old vfscommonupdate decline half-abort (printed done, exit 0) now
exits 1.
Degrade rule: punk::args/punk::ansi/textblock boot requires guarded; definition
load caught with fallback to a self-contained scan (-k/-dirty-abort/-confirm
supported) + plain punkboot_gethelp (availability NOTE factored into
punkboot_availability_note, shared by both paths); rendering degrades
independently of parsing (errorstyle minimal / plain help when the ANSI stack
is unhealthy); PUNKBOOT_PLAIN=1 forces degraded mode. punk::args joins the
bootstrap-tracked _runtime_deps staleness set (parsing contract only - the
rendering stack degrades instead; contract docs updated in
src/bootsupport/AGENTS.md + src/modules/AGENTS.md, workflow docs in
src/AGENTS.md + src/tests/shell/AGENTS.md).
Verified: usage/help/error paths, vfscommonupdate fail-fast and -confirm 0 runs
with closed stdin, staleness flag route live (args 0.5.0->0.12.0 tripped the
gate), degraded-mode help/check/info, full 'project -k -confirm 0' kit build.
Dogfooding finding recorded in the goal file: punk::args 0.12.0 reports generic
overflow where 0.5.0 reported choiceviolation for optional-choice mismatches
(worked around with required leaders; candidate error-selection improvement).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Formalize the follow-on work identified during the punk::args documentation
trials and ansi->html prototyping (2026-07-13/14):
- G-078 punk::ansi ANSI->HTML rendering api: promote the prototype ansi2html
converter + headless ansicat flatten entry into a documented punk::ansi
api (palette option, art mode with bold-as-bright and block sealing,
-assets embed|link|none using the vendored punkdoc-mono; docs-screenshot
enabler)
- G-079 default-colour semantics: renderspace replay output bakes explicit
SGR 40 for never-set backgrounds; preserve default fg/bg semantically so
html can theme-match any page background (current workaround: black pre)
- G-080 deterministic cell-grid rendering mode: inline-block cell emulation
to eliminate the residual sub-pixel seams the span-mode recipe accepts
- G-081 argdoc build pipeline: dev doc.* generation of doctools + punk-native
html + markdown from punk::args definitions against dev modules, incl.
per-form table rendering, deeper-namespace pages, @package surfacing
(G-075) and shared doc-set font assets
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Improvements from user review of the punk::ansi trial page:
- command index <nav> at page top (doctools-style jump list); hook to
display @package info above it when specs carry package_info (guarded -
key absent in older punk::args spec dicts)
- multiform commands: punk::args::usage renders all form synopses but only
the default form arg table (terminal UX expects i -form N) - the page now
adds a per-form ARG TABLE for each form that has arguments, trimmed at the
table-header separator so the command/description/synopsis panel is not
repeated per form (renderspace lines are colour-self-contained, so slicing
at a line boundary is safe); argument-less forms are skipped
- pre backdrop is now true black (page body stays dark grey):
overtype::renderspace overlay output emits explicit SGR 40 and example
helpers treat default background as the terminal backdrop - on black they
blend as in a terminal (proper fix = default-colour semantics, goal)
- row pitch line-height:17px (integer, just under the 17.85px inline
background height at font-size 15.36px) - the fractional 1.15em pitch put
row boundaries at fractional positions and showed periodic hairline gaps
through solid-bg runs
- explicit package require punk::lib (tstr placeholder resolution uses
punk::lib::undent; previously loaded only by transitive luck when usage
ran before get_spec)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Outcome of empirical seam/aspect testing of the ansi2html rendering against
src/testansi cp437 art (chrome + firefox, user-verified):
- punkdoc-mono.woff2 rebuilt with STOCK line metrics. The interim
normalize-to-win-metrics experiment (hhea/typo := win) is REJECTED and now
documented: it grows browsers'"'"' inline content area to 1.3213em, so at row
pitches below that every span'"'"'s background bleeds ~2.5px into neighbouring
rows - wrong-coloured bars under solid blocks. Kept as commented-out
normalize_line_metrics() in make_punkdoc_mono.py with rationale.
- font builds are now byte-reproducible: TTFont(..., recalcTimestamp=False)
keeps the source head.modified (two consecutive builds hash identically).
README records the new output sha256 and the reproducibility note, plus a
cell-geometry section for css authors (0.5859375em advance -> integer 9px
cells at font-size 15.36px; blocks paint the 1.3213em win cell) and the
settled art recipe: stock metrics + tight line-height:1.0 pitch +
transform:scaleY(1.3212890625) for terminal aspect (accepted trade-off:
minor residual sub-pixel seams; deterministic future fix would be
inline-block cell-grid emulation). Larger pitches (17.85/20px) tested and
rejected - stark row seams.
- punkargs_punknative.tcl: font-size 13px -> 15.36px (integer cell width -
fractional advances round differently at styled-span boundaries and seam
block/border glyphs); line-height stays 1.15 for text-oriented doc tables.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Repo policy is LF for text files; the as-downloaded copy had CRLF (also
tripped the fossil crlf warning on checkin).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
src/scriptapps/tools/fontprep/: pinned monospace web font for the punk-native
html doc pipeline, closing the glyph-coverage gap the probe identified -
unicode 16 octants (U+1CC00-1CEBF) and Symbols for Legacy Computing
(U+1FB00-1FBFF) needed by ansicat/textblock rendering are absent from
commonly-installed monospace fonts (even system Cascadia copies are commonly
pre-2404 and lack them).
- punkdoc-mono.woff2: glyph subset of Cascadia Mono v2404.23 VARIABLE ttf
(wght axis 200-700 kept - true bold from one ~80KB file; 1561 cmap
codepoints incl. full CP437 repertoire, box/blocks/geometric, braille,
legacy computing + octants). Coverage verified 157/160 against the measured
glyph inventory - identical to the full source font (misses are deliberate:
em-space + fullwidth forms are converter-normalization/fallback territory).
- renaming is REQUIRED by licence: Cascadia's SIL OFL 1.1 declares Reserved
Font Name "Cascadia Code" and subsetting is modification. Name table is
surgical: family records renamed, Microsoft copyright preserved verbatim
(OFL requirement), trademark record removed (a renamed copy must not assert
Microsoft's mark), OFL statement/url in licence records, provenance credit
in the description record.
- LICENSE-cascadia-OFL.txt (from the v2404.23 tag), punkdoc-unicodes.txt
(whole-block ranges with headroom), make_punkdoc_mono.py (fonttools subset+
rename script), README.md (full provenance: source/output sha256s, exact
command, regeneration steps). Python fonttools is one-time prep tooling
only - the doc build never invokes it; generators just read the .woff2.
Track licence in the G-063 inventory.
punkargs_punknative.tcl: new -assets option consuming the vendored font:
embed (default) = data-uri @font-face, fully self-contained page;
link = copy punkdoc-mono.woff2 beside the output and reference relatively
(shared+cached across a doc set); none = css stack only, with
warn-and-degrade when the asset is missing. font-weight 200 700 descriptor,
punkdoc-mono prepended to the fallback stack.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Two G-candidate prototypes under src/scriptapps/tools/ for punk::args as the
documentation source of truth (run against bootsupport modules with a plain
tclsh, e.g: tclsh punkargs_to_doctools.tcl punk::path out.man):
- punkargs_to_doctools.tcl: package's punk::args definitions -> doctools .man
(Tcl-standard [arg]/[opt] synopsis conventions by design - punk s-style is
out of scope for the doctools target). Covers @cmd summary/help, per-form
[call] synopses, argument detail lists in argspace order (leaders/options/
trailing values), constraint sentences (defaults/choices/ranges), id aliases
as cross-references. Validated via 'dtplite validate' (bin/dtplite.cmd);
punk::path trial output renders clean through the html/text/nroff engines.
- punkargs_punknative.tcl: doctools-free pipeline faithful to punk s-style.
html = punk::args::usage ANSI output converted via a minimal SGR->CSS
ansi2html (16/256/truecolour, bold/dim/italic/underline/strike/reverse) on
punk::ansi::ta::split_codes - candidate for a punk::ansi public api (also
the docs-screenshot enabler for ansicat/textblock output). markdown = GFM
with verbatim s-style synopsis fences + per-form argument tables.
-boxmap none (default) keeps faithful heavy box glyphs; -boxmap light maps
to the light set as a portability fallback.
Key findings baked into the code comments:
- css font stacks must be set on the pre element itself - the UA stylesheet's
'pre {font-family:monospace}' overrides body inheritance (cause of an
initially-misdiagnosed overlong-border rendering bug)
- glyph coverage probe over src/testansi + textblock::periodic/frames +
punk::args::usage (160 codepoints incl. unicode 16 octants and legacy
computing blocks): Cascadia Mono >=2404.23 covers 157/160; embedding or
referencing a pinned punkdoc-mono font remains the plan for doc-set output
since octant/legacy glyphs are absent from commonly installed fonts.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
'dev doc.validate' invokes a bare dtplite command which the repl
unknown-handler resolves via auto_execok on PATH - no dtplite executable
existed (only the legacy misnamed bin/dtplite_run.bat), giving
"invalid command name dtplite".
- src/scriptapps/dtplite.tcl: LF-converted (polyglot payloads are embedded
verbatim into the LF-only multishell output); fall back to the
project-vendored tcllib (src/vendorlib_tcl9/<arch>/tcllib*) when the
invoking tclsh lacks the dtplite package
- src/scriptapps/dtplite_wrap.toml: new scriptset config (tclsh nextshell
on all platforms)
- bin/dtplite.cmd: generated polyglot via
punk::mix::commandset::scriptwrap::multishell (checkfile-clean, LF-only)
- src/tests/shell/testsuites/binscripts/dtplite.test: artifact pins
(LF-only, checkfile, byte-identical round-trip re-wrap) + execution
usecases (usage error, validate good/bad file, validate directory tree,
html generation) - 8/8 pass under runtests
- AGENTS.md (bin, src/scriptapps): record the dtplite scriptset, its test
pin and the LF-only payload requirement
- punkproject.toml: 0.12.22 -> 0.12.23
Verified: 'dev doc.validate' in punk902z src validates all 64
src/doc .man files clean. The doctools pipeline remains interim -
punk::args is the intended doc source of truth.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
make.tcl modules + vfscommonupdate; old versions (args-0.11.2, tclcore-0.3.3)
removed by the update. All four kits (punkbi, punksys, punk91, punk902z)
rebuilt from the updated vfs and verified in kit mode: punk902z (9.0.2) and
punksys (8.6.13) both load args=0.12.0 tclcore=0.3.4, punk::args::formcheck
reports ::after as 6 forms / 15 pairs / 1 sanctioned structural overlap
(cancelid/cancelscript, zero unsanctioned) and ::lseq as the two unsanctioned
type_weakness findings ({range start_count} {range count}) on both runtimes.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
New punk::args::formcheck <id> ?-return dict|summary?: reports the form pairs
of a multiform definition that some argument list could cleanly match
simultaneously (the multipleformmatches inputs). Static pairwise pass over the
resolved FORMS enumerates each form's positional word-slot chains (leaders then
values; -optional and ?-wrapped clause-member branching; -multiple capped;
options excluded - order-free), screens aligned equal-length chains per
position (discriminator words via choiceword_match, type witnesses from a
small table), and CONFIRMS every candidate witness arglist with a real
single-form parse against both forms (parse_status - no raise, no
user-supplied words, no define/resolve cost). Findings are therefore witnessed
- discriminated form pairs cannot false-alarm; misses (exotic types,
option-requiring forms, enumeration caps) are the documented conservative
direction. Classes: type_weakness (discriminator aligned with a permissive
non-validating type - any/none/string/ansistring/globstring/expr/script) vs
structural (forms genuinely share an argument shape).
New @form key -overlapallowed <formname-list> sanctions a KNOWN overlap on
either pair member: the finding reports with sanctioned 1 and leaves the
result's unsanctioned list (the actionable/gate subset). Parse behaviour is
never affected; unknown form names are rejected at definition resolve
(end-of-forms cycle). @form directive doc updated.
tclcore moduledoc 0.3.4: the documented after cancelid/cancelscript overlap
(runtime-liveness ambiguity, 0.3.0 record) sanctioned on the cancelid form -
::after now reports zero unsanctioned findings while 'after cancel <id-shaped>'
still raises multipleformmatches. ::lseq deliberately unsanctioned: formcheck
reports range/start_count (witness {1 count 1}) AND range/count (witness
{1 by 1}) - both real, both rooted in the expr-typed end slot, kept visible
pending an expr syntax-validating type (G-069/G-070).
Tests: new args/formcheck.test (7) - no-finding cases (parse withid/withdef
pair, afterish/sharedform fixtures), class+sanction fixture, unknown-form
rejection, -return summary, ::lseq/::after pins with witnesses re-verified,
sanction parse-neutrality. punk::args suite 210/210, punk::ns 57/57.
G-074 achieved and archived (acceptance review in the detail file); G-055
verification gate gains the formcheck step (unsanctioned must be empty for
regenerated multiform commands).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Root AGENTS.md User Preferences gains the binding agent-agnostic rule (an
agent editing .gitignore discovers it at the root without walking into
.fossil-settings/): .gitignore canonical, ignore-glob hand-derived, same work
unit, semantic differences translated per .fossil-settings/AGENTS.md. That
child doc's sync procedure is retitled from "when asked" to mandatory for
any ignore-rule change, with the one-sided-change prohibition stated.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Proposed goal, wording user-approved 2026-07-13: make the perl/python/node -e
reflex work on punk kits (top-level and script subcommand forms) instead of
erroring, without ever reproducing stock-tclsh argv-swallow/stdin misparse
behaviour. Companion to the tclsh -e guardrails commit (AGENTS.md entry +
claude deny hook).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Stock tclsh treats any leading-dash arg other than -encoding as argv for an
interactive/stdin session: on a console it hangs at a prompt, with piped stdin
it exits 0 silently ignoring the supposed one-liner. Agents habitually reach
for -e (perl/python/node reflex), wasting time waiting on hung processes.
- root AGENTS.md User Preferences: document the misparse, the correct patterns
(temp .tcl file or script via stdin) and the defensive stdin-redirect habit
- .claude/settings.json: PreToolUse hook denying Bash/PowerShell invocations
of tclsh with -e, anchored to command position (start/pipe/semicolon/paren)
so prose mentions in commit messages or grep args do not false-positive;
corrective guidance returned to the agent
- .gitignore: narrow the .claude ignore so shared settings.json is tracked
while session-local files stay ignored
- .fossil-settings: ignore-glob hand-derived per the no-negation rule
(.claude/* wholesale + explicit fossil add of settings.json), exception set
in AGENTS.md updated; fossil extras under-ignore check verified empty
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
make.tcl modules + vfscommonupdate + project: all four kits rebuilt
(punkbi, punksys, punk91, punk902z). Kit-mode verification: punk902z
loads args=0.11.2 ns=0.5.0 tclcore=0.3.3 lib=0.4.3 punk=0.2.6, 'i join'
emits no bad-@dynamic warning; punksys (8.6) loads args=0.11.2 and the
after cancel-id discrimination resolves 'cancel someid' to the
cancelscript form (harvested id prefix working on 8.6).
A stale tcl_oauth2_library install leftover in the project's root lib/
tree (its src/vendorlib source was removed 2026-07-12; the libs install
step does not prune targets whose source vanished) was swept into the
vfs lib by vfscommonupdate and removed again before the kit build - it
is deliberately not part of the vendored set yet (G-065..G-068
first-test candidate). The root lib/ leftover itself was removed after
this commit was first authored; its rows remain in lib/.punkcheck.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
New goal G-076 (active): adopt the upstream tcl9 dead-console fix (ticket
f10d91c2d3, root-caused in G-039) via a single version gate shared by the
'help tcl' warning and the repl watchdog arming.
- punk::lib 0.4.3: has_tclbug_console_deadspin + pure classifier
tclbug_console_deadspin_applies, gate variable
check::tclbug_console_deadspin_fixed_in (empty = all Tcl 9 windows
runtimes affected). Buginfo dicts gain a mitigated/mitigation axis
orthogonal to level; the deadspin check reports mitigated when
punk::repl >= 0.5.0 is available to the runtime.
- punk::repl 0.5.1: repl::start arms the dead-console watchdog only when
the check reports the runtime affected - recording a verified fixed
release once flips both consumers.
- punk 0.2.6: 'help tcl' renders mitigated warnings subdued (term-grey)
with a "(mitigated)" annotation and the mitigation text; severity
classification unchanged.
- checkbugs.test: classifier truth table, live-check consistency, and
mitigated-axis validation in the generic every-check sweep.
The fixed_in flip is deliberately deferred until a released Tcl passes
the G-039 kill-procedure re-verification (watchdog disabled, clean
script-visible eof exit) - recorded as the goal's remaining acceptance
item.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
User-directed removal of the three tags the 0.3.2 sweep identified.
None has round-2 substitution content and their placeholders are all
display styling/examples, so the definitions now resolve as ordinary
static definitions with display expansion cached per raw definition
(previously @dynamic forced re-expansion on every render). Sweep of
all 462 registered ids now emits zero bad-@dynamic warnings; renders
and parses verified unchanged for all three. punk::args 203/204 (1
pre-existing skip), punk::ns 57/57.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
User-reported: 'i join' emitted the bad-@dynamic warning 4 times ('i
join test' 6). Root cause was a real inefficiency, not just noisy
logging: a @dynamic definition whose round-1 tstr output has no
round-2 parameters bypassed argdefcache_unresolved entirely, so every
resolve in the 'i' flow (doc walk, advisory parse, get_spec for the
render, synopsis) redid display masking plus the full round-1 tstr and
re-warned. Such definitions now cache as a zero-param unresolved entry
- consistent with the round-1 freezing legitimately dynamic
definitions already get - so later resolves take the cheap cached
branch and the warning emits once per definition per interp.
Warning wording corrected: @dynamic is not a complete no-op for a
parse-inert definition - deferred display fields still re-expand per
render instead of caching - so the message says removal is appropriate
only if that display behaviour is unintended. Sweep of all 462
registered ids: ::split, ::array, ::join carry the parse-inert tag
(left in place pending the display-refresh decision).
Incidental finds from the sweep: the malformed ::tcl_startOfNextWord
tclcore definition (double-quoted -help with unescaped quotes in the
embedded man-page example) failed to resolve at all - now braced per
the define quoting rules, 'i tcl_startOfNextWord' works; and resolve's
malformed-record error path printed dev diagnostics (full records dump
+ ::testrecord global) - commented per the stray-debug convention.
Tests: new args/dynamic.test (warn-once + cached parse, legit-dynamic
round-2 freshness; stderr captured via channel transform). punk::args
203/204 (1 pre-existing skip), punk::ns 57/57. Live-verified: 'i join'
and 'i join test' each emit the warning exactly once (punk902z src).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
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
Documentation-only. The define -help gains two sections: 'Registration
styles' (direct define vs deferred lappend-PUNKARGS registration - the
lazy scan/on-demand definition mechanics, why moduledocs and the module
template use it, PUNKARGS_aliases, punk::args::status timings) and
'Interpolation (tstr placeholders) and the defspace' (display-field
deferral vs parse-field expansion vs @dynamic; the defspace rule with
the argdoc-child subtlety; the silent-literal fallback; safe patterns
for load-time-computed values). Motivated by the ::after id-shape
harvest misattribution - corrected record in goals/G-055.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
The 0.3.0 record claimed tstr placeholders in non-display fields are not
expanded for plain PUNKARGS definitions. Controlled probes show all
authoring styles (direct define, registered PUNKARGS, argdoc-subns
variable) DO expand parse-field placeholders - the ::after attempt
failed because update_definitions resolves registered definitions in
the argdoc subnamespace whenever one exists, while the harvest variable
had landed in the parent namespace, and an unresolvable param is left
silently literal. Module comment, buildversion changelog line and the
G-055 workflow note corrected; the silent-literal fallback noted in
G-055 as a diagnostic gap (a literal placeholder reaching a parse field
is always an authoring error and could warn).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
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# on 8.6.11
and 9.0.3) - substituted via a build-time %AFTERIDPREFIX% string map
because tstr placeholders in non-display fields are not expanded for
plain PUNKARGS definitions (G-046 defers display fields only; noted in
G-055 for the regeneration workflow). -typesynopsis id keeps the
synopsis as the man page's 'id'; both ids gain man-page-derived help.
Effect under G-041 form candidacy: 'i after cancel someid' resolves
cleanly to the cancelscript form, matching real Tcl's silent
script-match no-op; 'after info <non-id>' is model-rejected where real
errors at runtime (parity-true). An id-SHAPED word after cancel stays
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.
Tests: three parity pins added in tclcoreparity.test (id-shape harvest
agreement with live ids, cancel discrimination incl the liveness
ambiguity witness, info error-vs-ok parity + accepted dead-id
divergence). punk::args 201/202 (1 pre-existing skip), punk::ns 57/57.
Live-verified on punk902z src ('i after cancel someid' renders info
scheme).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com