- run no longer launches the last runtime alphabetically: resolution is PUNK_ACTIVE_RUNTIME env override -> bin/runtime/<platform>/active.toml (constrained single-key toml, written by the new 'use <name>' subcommand, marked with * in list, covered by the existing bin/* VCS ignores) -> sole installed candidate -> error listing candidates. First fetch sets active only when none recorded; later fetches never steal it. Stale active (file removed) reported with reselect guidance.
- bash/zsh payload fetch reaches checksum parity with the powershell payload: fetches sha1sums.txt, locates the stored hash, skips when the local copy matches, downloads to .tmp and installs only on sha1 match; sha1 tool detection (sha1sum/shasum/sha1/openssl) with refusal of unverified downloads when none present; optional runtime-name argument added (fetch <name>)
- fixes: powershell "stored hash from sha1sums.txt" printed an undefined variable ($storedhash -> $stored_sha1); bash MSYS branch invalid assignment (interp = ...); candidate listings for run/list/use exclude .tmp leftovers and non-runtime files (a stray .txt could previously be selected by run)
- runtime scriptset sources relocated to src/scriptapps/bin (proper home alongside getzig.*); bin/runtime.cmd regenerated via scriptwrap from the new location - roundtrip test path updated, suite green on 9.0.3 (8 pass + unix-gated skip), runtime.bash syntax-checked under bash and zsh
- verified live on windows: multiple-runtimes-no-active errors with candidates (previously silently launched tksfe-twapi-x64), use/list/run against tclsfe-x64 and env override to tclsh902z, fetch no-steal + first-fetch auto-set, checksum-match no-download path
- docs: bin/AGENTS.md documents the fetch/use/run contract and updated generated-polyglot workflow paths; src/scriptapps/AGENTS.md scriptset-home wording updated
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- tclsfe-x64.exe is not a tk runtime and the tk-having sfe variants are wish-based (unsuitable), so punk91 moves off punk9win_for_tkruntime.vfs (which is intended for kits that already carry Tk) to punk9win.vfs - Tk loads as an extension per the punkbin runtime convention
- commented-out tksfe-twapi-x64 experiment recorded: the kit builds and can run tk apps (punkwish91 lib:tkhello) but the tk console does not display; noted todos - get the builtin tk console showing for exploration (unsuitable long-term: no ansi handling) and consider a textwidget-based virtual console capable of hosting the punk shell (relates to G-001 pluggable console backends)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- new src/tests/modules/punk/mix/testsuites/scriptwrap/multishell.test (9 tests, green on tcl 9.0.3 + 8.7, unix exec case constraint-gated): scriptset wrap via the module-provided punk.multishell.cmd template (payloads + _wrap.toml fixture) - output produced, MULTISHELL magic line, payloads embedded verbatim, configured win32 nextshell present, LF-only endings, byte-identical re-wrap determinism; checkfile 512-byte label/boundary validation reports no label location errors for a fresh wrap AND for the committed bin/runtime.cmd; the runtime scriptset ROUND-TRIP pin - re-wrapping src/scriptapps runtime.ps1+runtime.bash+runtime_wrap.toml reproduces bin/runtime.cmd byte for byte (verified identical before pinning), so hand-edits to the output or unregenerated payload changes both fail; execution smoke - cmd.exe runs the polyglot and dispatches the powershell payload (windows), sh payload execution gated to unix (on windows the shell layer deliberately relaunches via the win32 nextshell)
- guidance strengthened so "fix bin/<name>.cmd" routes to sources: root AGENTS.md bin/ index entry now states the .cmd polyglots are scriptwrap-generated from src/scriptapps; bin/AGENTS.md gains a "Generated polyglot .cmd scripts - never edit in place" section with the 4-step fix workflow (edit payloads/toml, re-wrap with multishell -askme 0, heed checkfile ERRORs, commit source + regenerated output together); src/scriptapps/AGENTS.md documents the scriptset->bin/*.cmd relationship and regeneration command; tests index updated
- context: the polyglot technique is deliberately maintained despite fragility (user direction 2026-07-10) until hiding techniques close in the underlying languages - these pins are the guardrails
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- punk_main.tcl captures the runtime static baseline at kit boot: empty-filename [info loaded] prefixes probe-loaded in a throwaway interp (configurable denylist tk*/vfs/mk4tcl/vlerq/tdbc* for side-effecting/composite inits), recording only packages a load actually PROVIDES (diff on provides, not package names - probe loads can trigger index scans) into ::punkboot::static_packages/static_prefixes; the boot interp is seeded with ifneeded {load {} <prefix>} mappings
- punk::lib interp_sync_package_paths/snapshot_package_paths propagate the baseline and seed fabricated interps/threads; punk::repl codethread init (new %staticprefixes%/%staticpackages% scriptmap) and shellthread worker init do the same - the repl code interp on a static runtime resolves Thread again (punk91 = tclsfe-x64 + punk9win.vfs failed with "can't find package Thread"; the appended vfs replaces the stub's //zipfs:/app mount taking its static pkgIndex files with it, and the tclsfe PostInit hook seeds per-interp auto_path only)
- punk::packagepreference static-vs-bundled policy: requiring a baseline package ensures the static mapping and triggers the package unknown scan BEFORE resolution so bundled copies register and the highest version wins (newer bundled reachable, static not masked by older bundled); static registrations excluded from the loaded-shared-object same-version pinning
- composite statics defer correctly: static twapi provides nothing under the probe (script layer lived in the replaced stub zip) so the bundled complete twapi is the resolution; punk91 vfs/vfs::zip warning re-diagnosed as kit content (tclsfe bundles no tclvfs) - recorded in the goal detail, not a seeding defect
- tests: staticseed.test (4) + staticpolicy.test (4) un-gated simulated-baseline units (green 9.0.3 + 8.7); staticruntime.test (4) kit integration behind the capability-probed statickitavailable constraint (probes ::punkboot baseline incl Thread; env PUNK_STATICKIT_TEST_EXE override, default bin/punk91.exe) - 4/4 against the rebuilt punk91; punk902z/full shell tree/full source-tree suite baselines unchanged (exec-14.3 only)
- verification runtime tclsfe-x64.exe pinned in the punkbin artifact repo (separate repo, commit b2dbbe6) with sha1sums + new AGENTS.md scaffolding
- punkshell 0.6.0: CHANGELOG entry; AGENTS updates (src/vfs capture contract, tests indexes incl new punk/lib + punk/packagepreference entries, shell staticruntime contract)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
punkcheck-managed regeneration propagating the G-049 module releases (parse-status data model, machine-parsable cmdhelp returns) into src/bootsupport/modules, the punk.project-0.1 and punk.shell-0.1 layout bootsupport copies, and _vfscommon.vfs/modules.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- new punk::args::parse_status: documented status structure from a parse attempt (overall ok / status valid|invalid|incomplete / scheme / message / errorcode minus -argspecs / failureclass / badarg / form / receivednames + per-argument argstatus with class, status ok|bad|unparsed, received count/positions, value-in-effect incl -default fill); validation failures reported in the structure, not raised
- arg_error -parsestatus: both renderers derive goodarg/badarg row marking and choice value-in-effect highlighting from the structure (transient goodargs/badarg locals replaced; built internally from -badarg/-parsedargs when not supplied)
- per-render scheme colour resolution: documented -scheme choice value 'nocolour' now takes effect and scheme renders no longer mutate the shared arg_error_CLR array (strike-only goodarg leak fixed)
- parse: new -caller option overriding the %caller% frame-walk substitution (included in parse cache key); get_dict missingrequiredvalue/missingrequiredleader now carry -badarg (type/allocation failures get badarg marking, not just choice violations)
- punk::ns::cmdhelp: -return dict {origin docid cmdtype args_remaining parsestatus}; advisory parse via parse_status on both alias and main paths; explicit -scheme honoured on the parse-failure render; failure messages name the queried command instead of leaking the internal parse source line at top call depth
- tests: parsestatus.test new (12); usagemarking.test G-049 GAP pins flipped + -parsestatus parity/badarg render tests (17); cmdhelp.test G-049 GAP pins flipped + cmdhelp_return_dict_* (25); punk/args + punk/ns trees green on Tcl 9.0.3 and 8.7; full source-tree suite baseline unchanged (exec-14.3 only)
- punkshell 0.5.0: CHANGELOG entry; src/tests/modules/AGENTS.md test-index blurbs updated
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Argument resolution no longer tstr-expands display-only content (-help on
@cmd/@examples/argument records, @formdisplay bodies): ${...} there is masked with
inert tokens (spec key DISPLAY_DEFERRED) and expanded at display time via
private::expand_display_fields (hooks in arg_error/eg/resolved_def/@default copyfrom;
separate display cache for non-dynamic defs; @dynamic re-expands per render preserving
provider refresh; reentrancy guard substitutes raw sources when expansion of the same
id re-enters). First parse of heavily documented commands drops accordingly
(punk::ansi::mark_columns first call ~4.3s -> ~12ms; tclcore ::lseq resolve
~184ms -> ~2ms) and -help content that calls punk::args-parsing commands - including
against its own definition id - resolves cleanly instead of stalling or looping.
-choicelabels stays eager (punk::ns reads it from parse specs in the subcommand walk).
Record splitter factored to private::split_definition_records.
Also per G-046 acceptance:
- @dynamic multiline -help substitutions align at the insertion column
(rendering_atdynamic_multiline_help_insertion GAP flipped)
- prefix/alias choice normalization keeps single-element-clause values plain-string
({\Deleted} list-wrap shape bug; choicegroups_imap_prefix_shape GAP flipped)
- -return string renderer aligns cmd-help continuations under the Description: label
(characterization updated) and its Example: line shows the example, not the doc url
- punk::ns cmdhelp shows plain info-scheme usage when no argument words are supplied
for a leader/value-requiring definition ('i string is', 'i punk::args::define') -
advisory parse runs -errorstyle minimal so the discarded error no longer renders a
second usage table (timing parity with pre-change: ~5.3s/4.1s on punk91 src)
Tests: deferredhelp.test (new); suites green on tcl903 + tcl87; full source-tree run
baseline-clean (exec-14.3 only). GOALS.md: G-046 -> achieved 2026-07-10 (activated
this session at user direction); design + verification evidence in the detail file.
Project 0.4.15.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
First 'i <ensemble>' in a fresh shell (e.g 'i ansistring') rendered the autogenerated
ensemble help without subcommand-help markers/choicelabels: ensemble_subcommands_definition
tested punk::args::id_exists against argdocs whose registered namespace
(::punk::args::register::NAMESPACES) had not yet been lazily loaded - the punk::ns
doc-lookup entry path only loads the ensemble command's parent namespace. The generator
now runs update_definitions for the namespaces its id checks could resolve in.
Self-healed on later calls (autodef regenerates per 'i' invocation), which is why the
defect went unnoticed; no prior test pinned the load ordering.
Tests: punk/args ensembledef.test (new), punk/ns cmdhelp.test
cmdhelp_ensemble_lazy_registered_argdocs. Project 0.4.14.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Output of src/make.tcl refreshing bootsupport, vfs, and project_layouts
bootsupport trees to match the recently-bumped module versions.
Assisted-by: harness=opencode; primary-model=openrouter/z-ai/glm-5.2; api-location=openrouter.ai
- Repeated -include-paths flags previously overrode each other silently (standard
non-multiple punk::args opt semantics), quietly narrowing the test selection
while reporting green - a recurring misuse driven by Unix-CLI accumulate-flag
muscle memory (gcc -I / curl -H style).
- The option is now -multiple 1: repeated flags accumulate, the single
space-separated quoted list form is unchanged, and the unreceived default {**}
passes through (the consumer flattens occurrences with concat). Help text
documents both forms as equivalent.
- src/tests/AGENTS.md work-guidance updated. Known remaining sibling issue:
multiple trailing file-tail globs still silently match 0 files.
- Verified: repeated-flag and single-list invocations select identical file sets
(20 files / 189 tests) on Tcl 9.0.3, repeated-flag accumulation on 8.7, and a
no-flag default run with a file-tail glob unchanged. The general strict-arity
mechanism is proposed as G-053.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- punk::args level (usagemarking.test, 14 tests): pins -parsedargs/-badarg/-scheme
marking primitives, goodchoice highlighting of selected and default-in-effect
choice words, and scheme border selection - asserted by SGR-parameter subset
against the live colour arrays. GAP pins for the documented -scheme choice
'nocolour' falling through to 'na' (renders with the previous scheme's leftover
colours) and the dash-spelling '-nocolour' leaking strike-only goodarg into the
shared colour array (G-049 candidates).
- punk::ns level (cmdhelp.test, 19 tests): pins scheme selection (error scheme on
failed parse of supplied args, info scheme + goodarg marking on success), badarg
marking via choiceviolation, the early alias-resolution branch, -return string
parity, goodchoice highlighting through cmdhelp, and the cmdinfo result shape.
GAP pins: pseudo-command cmdtype 'notfound' despite resolved docid + space-form
docid exact-word-only jump (G-051; real 'string is true'/'is tr' pins behind the
have_tclcoredocs constraint), TclOO undocumented-method class-summary fallback
with info-scheme parse of bogus args (G-052), synopsis marking absence and
curried-alias braced-target substitution (G-050), explicit -scheme ignored on
the parse-failure path and caller-attribution leaking cmdhelp's internal parse
source line at top-level call depth (G-049).
- punk::ns 0.1.3 (doc-only): cmdhelp 'subcommand' argument help rewritten - was
described as ensemble-subcommands-only; now covers tcl::oo methods and argument
words, and documents the info/error scheme display driven by argument validity.
- Verified: full punk::args + punk::ns suites green on Tcl 9.0.3 and 8.7.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
project::new documented -choices on -layout with -choiceprefix defaulting
to true (prefix matching promised), but the manual dict merge parser
accepted arbitrary values with no validation or normalization. Added
tcl::prefix::match against the documented layout choices so the parser
now validates and normalizes -layout values, matching the PUNKARGS
documentation.
Assisted-by: harness=opencode; primary-model=openrouter/z-ai/glm-5.2; api-location=openrouter.ai
install documented -choices on -overwrite, -source-checksum and
-punkcheck-folder with -choiceprefix defaulting to true, but the manual
dict merge parser validates option names only and never normalizes choice
values. PUNKARGS now declares -choiceprefix 0 so documented behaviour
matches the implementation (exact match required).
Assisted-by: harness=opencode; primary-model=openrouter/z-ai/glm-5.2; api-location=openrouter.ai
d/ documented -choices on the v argument with -choiceprefix defaulting
to true, but the manual parser uses an exact string comparison. PUNKARGS
now declares -choiceprefix 0 so documented behaviour matches the
implementation (exact match required).
Assisted-by: harness=opencode; primary-model=openrouter/z-ai/glm-5.2; api-location=openrouter.ai
renderspace -bias and -format, renderline -info and -overtext_type all
documented -choices with -choiceprefix defaulting to true, but the manual
switch-based parsers accept arbitrary values with no prefix matching.
PUNKARGS now declares -choiceprefix 0 so documented behaviour matches
the implementation (exact match required).
Assisted-by: harness=opencode; primary-model=openrouter/z-ai/glm-5.2; api-location=openrouter.ai
console_fact_get and console_fact_set documented -choices on the key
argument with -choiceprefix defaulting to true (prefix matching promised),
but the manual positional parser feeds key directly into a dict lookup
with no prefix normalization. PUNKARGS now declares -choiceprefix 0 so
documented behaviour matches the implementation (exact match required).
Assisted-by: harness=opencode; primary-model=openrouter/z-ai/glm-5.2; api-location=openrouter.ai
The testinterp replaces auto_path wholesale (project src/lib dirs, root
lib_tcl<N>/<arch>, [info library] + parent). Under a kit executable that
parent is e.g //zipfs:/app, and tclPkgUnknown scans only an entry plus its
immediate children - so kit-bundled packages under //zipfs:/app/lib_tcl<N>/
<pkg> (e.g tcllib's tcl::chan::fifo2, required by the shellrun harness at
testinterp setup) were unreachable. Unnoticed in punkshell because the
project's own root lib_tcl9/win32-x86_64 carries tcllib2.0; projects
without that payload (e.g tomlish) failed every file at
'package require shellrun' when run under a punk kit exe.
Now the kit's internal lib/lib_tcl<N> dirs are appended when present,
mirroring the internal-path classification in src/vfs/_config/punk_main.tcl:
zipfs app mount, tclkit ::tcl::kitpath exe dir-shadow, cookfs //cookit:/
volume (default mount name only, as per punk_main.tcl). No-op under native
tclsh. Verified: punk902z + tclsh902z suites pass; classification probed OK
under tclkit 8.6.17, punk9cook (cookit), punk91 (zipfs 9.1), native Tcl903;
tomlish suite under punk902z/punk91 goes 0 -> 149/149 with the same edit.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
makeFile/makeDirectory output previously landed in the working directory -
aborted core exec.test runs littered src/tests with untracked helper files
(cat, echo, gorp.file ...). Unless -tcltestoptions supplies -tmpdir, a
fresh directory from 'file tempdir' (TMPDIR/TEMP/TMP + pid fallback for
tcl 8.6) is configured and deleted before exit; a hard-aborted run now
leaves litter in the OS temp area rather than the source tree.
Takes effect in single-process mode; multi-process mode still does not
forward tcltest options to child processes (pre-existing todo).
Contract documented in src/tests/AGENTS.md.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
TIP 746 (Tcl 9.1) removed lseq's expr-operand behaviour. punk::lib::range
(lseq branch) now normalizes int[+-]int offsets via offset_expr so callers
like `range 0 [llength $list]-1` keep working (punk::ansi::grepstr broke
under 9.1b0, taking example-block highlighting and the punk::args
examples.test with it). The lseq branch is also aligned with the tcl8
fallback contract: default 'by' now infers direction (descending ranges
previously returned empty under tcl9) and 'by 0' returns empty (Tcl 9.1
lseq changed by-0 to return one element).
Direct lseq expression operands expr-wrapped: punk::lib lzipn_tcl9b/c,
cols, cols2; punk::args zero_based_posns.
check::has_tclbug_safeinterp_compile falls back to interp invokehidden
tcl:unsupported:disassemble - Tcl 9.1 safe interps hide tcl::unsupported::*.
New modules/punk/lib range.test pins the range contract on 9.0 and 9.1;
core tests AGENTS.md documents native-tclsh vs punk-exe exec.test baselines.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Tests-first coverage pass (user-directed) before any changes to the
definition-parsing/indentation machinery. punk/args suite: 128 tests, 15 files.
- rendering.test (15): nesting independence (plain + tstr definitions render
identically regardless of source-block indentation), relative-indent
preservation (2-space help and 1-space choicelabel conventions, single and
multiline - preservation, not enforcement), -unindentedfields (arg -help
works left-margin; @cmd accepts-but-ignores it - GAP pinned), constructed
(string-built) definition absolute-indent characterization ('constructed'
to avoid confusion with the @dynamic directive), multiline ${[cmd]}/${$var}/
resolved_def insertions (aligned at insertion column, nesting-independent),
and @dynamic ${$DYN_X} double substitution: stable across repeated
resolutions, provider refresh honoured in help text, choice display and
parse acceptance, textblock::frame-style art choicelabels byte-aligned -
but multiline -help insertions land lines 2+ at insertion-4 (GAP pinned:
the second-round subst path lacks paramindent re-alignment)
- choicegroups.test (5): group/-choices union parsing, cross-group prefixes,
duplicate-entry dedup, per-group titled choice tables (the punk::imap4
multi-table display), imap4-style {\Deleted}/{$MDNSent} values with labels
incl. through tstr-bearing blocks; GAP pinned: prefix-normalization
list-quotes special-character choices (exact '\Deleted' stores the plain
string, prefix '\De' stores a one-element list) - value shape depends on
input style
- defquoting.test (3): container quoting rules pinned - braced values fully
literal ($, [], two-char \n, bare backslashes), quoted values get Tcl
backslash semantics (\n -> newline, \ -> \) with $/[] still literal, and
\$\{...\} renders a literal ${...} in tstr-processed blocks
- examples.test (6): helpers::example box/title/-tstr toggle (no-dedent
direct-call contract pinned), ${[example ...]} insertion into @cmd help
(bars at insertion column, content +2, automatic 'Example: eg <id>' row),
@examples -> punk::args::eg with #<nodisplay>/@#<nodisplay> stripping
(ANSI-tolerant), strip_nodisplay_lines unit, live tclcore ::lseq smoke
- punk::args 0.3.1: helpers::example -title path called bare [a] (resolvable
only where a punk shell's global alias exists - errored under plain tclsh);
now punk::ansi::a. Found by examples.test in the runtests environment.
- investigated punk::ansi::mark_columns first-call slowness (3.94s first,
60us after): argdoc -help calls a proc that calls mark_columns, whose parse
resolves the same still-resolving definition - hazard already documented
in-source with candidate fixes; recorded in CHANGELOG for goal
consideration, deliberately not patched
- project 0.4.11 (CHANGELOG entry)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Activated and completed under user direction (2026-07-08). Aliases for choice
values are now first-class: accepted at parse, normalized to their canonical
choice in results, folded into the canonical entry in usage display, and
resolved identically by the punk::ns doc-lookup walk.
- punk::args 0.3.0:
* -choicealiases {alias canonical ...} on argument lines; dict shape
validated in the option switch, alias->existing-choice and no-collision
cross-validated after spec merge (surfaces at resolve time - define is
lazy)
* the choice-word matching chain extracted from get_dict into the shared
resolver punk::args::choiceword_match {word nocase allchoices
choicealiases choiceprefix denylist reservelist} -> {matched exact
canonical}; get_dict delegates to it (writeback/lset logic stays at the
call site) - one implementation for parser and doc walk
* exact aliases match under any -choiceprefix/-nocase setting; alias names
join the prefix-calculation pool when -choiceprefix is true; deny applies
to the matched NAME (denied alias requires the full alias; a canonical
reached via its alias is exempt from the canonical's own deny entry)
* display: aliases fold into the canonical entry as an '(alias: x)' /
'(aliases: x|y)' label note (single convergence point covers all render
paths); alias names join the display prefix calculation so highlighted
minimal prefixes stay truthful
* define doc gains the -choicealiases section
- punk::ns 0.1.2: cmd_traverse resolves subcommand words via choiceword_match
- -choiceprefixdenylist/-choiceprefixreservelist now honoured in doc lookup
(the two pinned parity GAP tests flipped to agreement), aliases normalize
before choiceinfo lookup, -nocase honoured in the walk
- punk 0.2.3: punk::help adopts the feature - 'i help' lists one entry per
registered topic (topics+help, tcl, env+environment, console+term|terminal);
topic words accept aliases and unique prefixes with the user-decided
minimum-prefix policy recorded in ::punk::helptopic: denylist {help}
(h/he/hel stay command words), reservelist {c to tc} (fall through to
command lookup; e/en and te/ter fall through naturally as ambiguous).
Argless 'help' output byte-identical to the strict-80 baseline.
- tests: choices.test +5 (alias exact/prefix normalization, noprefix exact-
only, nocase, the punk::help policy matrix, resolve-time validation);
cmdflow.test parity tests flipped from GAP to agreement + alias-traverse and
display-fold assertions (+flowaliased fixture)
- suites: punk/args 98 pass + 1 skip, punk/ns 28 pass, punk/lib 21 pass
- G-040 flipped active -> achieved 2026-07-08; detail file records
implementation and verification
- project 0.4.10 (CHANGELOG entry)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
From interactive review of the help restructure: 'i help' / 'i help <topic>'
usage tables rendered ~160 columns wide. Only the argless 'help' overview is
strictly 80-column - but the rest should stay reasonable, and the usage tables
do not yet wrap to terminal width, so punk::args source line lengths directly
set table width.
- helptopic argdoc help texts manually folded at ~70 columns ('help tcl' was
the worst offender at ~230 chars on one line)
- generated ::punk::help/::punk::help_chunks definitions: basehelp/topichelp/
chunks-note folded; topic choice grid rendered with -choicecolumns 2 (the
largest width contributor)
- dynamically built definitions don't get the file-sourced indentation
normalization - topichelp's continuation over-indented, fixed with an
explicit \n join
- results: i help 68 cols, per-topic 61-65; argless 'help' output byte-
identical to the strict-80 baseline; punk/ns 26 and punk/args 93+1 suites
pass
- future improvement noted in CHANGELOG: automated (potentially language-
dependent) wrapping of help text to available width, e.g. via tcllib's
text-adjust facilities
- project 0.4.9 (CHANGELOG entry)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
New 'make.tcl vfslibs' step (also run inside 'make.tcl project' before kit vfs
assembly): propagates declared vendored platform-library packages into kit vfs
lib_tcl<N> trees, ending the hand-copying that let kits ship a known-buggy
tcludp through three make invocations (the G-036/G-037 motivating case).
- declaration: src/runtime/vendorlib_vfs.toml, parsed with bootsupport tomlish
(no ad-hoc parsing; G-024's mapvfs toml conversion NOT required first - this
file is recorded as a candidate for absorption into it). [install.<name>]
entries: source (relative to src/), targets (kit vfs lib folders relative to
src/vfs/), optional supersedes (folder names removed from each target before
install - explicit, no silent mixed-version provision per G-035 concerns).
- participation is per-package per-kit; authority is the declaration - nothing
syncs undeclared, so the known vendorlib/kit twapi drift is deliberately
untouched (resolves the direction/authority design question).
- punkcheck provenance at src/vfs/.punkcheck (gitignored, outside kit
payloads); skip-if-unchanged proven (rerun: 0 copied, 9 skipped per target);
malformed/unresolvable entries fail the build naming the entry (exit 3).
- 8.6 kits upgraded (the G-036 loose-end decision, recorded): tcludp 1.0.13 is
dual-generation (pkgIndex selects tcl9udp1013.dll on 9+, udp1013t.dll on
8.5+) so the single vendorlib_tcl9 source serves punk8win.vfs/lib_tcl8;
superseded udp1.0.12 removed.
- verified on rebuilt kits: punksys/punkbi (8.6.13) and punk902z (9.0.2) all
report package require udp == 1.0.13 loading the generation-appropriate dll
from their vfs; no udp1.0.12 remains under src/vfs; punksys piped-shell exit
smoke clean.
- G-037 flipped proposed -> achieved 2026-07-08 under user direction (no
intermediate active stop).
- includes project-build byproducts via established channels: bootsupport
snapshots refreshed to punk-0.2.1 / punk::lib-0.3.0 (from today's version
bumps) and make.tcl synced into layout copies.
- project 0.4.8 (CHANGELOG entry)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Completes G-036's remaining acceptance item - version-based detection of the
vulnerable combination (tcludp < 1.0.13 on Tcl 9 Windows: the per-thread exit
handler closes the process-global sockListLock/waitForSock events, silently
freezing every other udp-using thread's event loop after any udp-loaded thread
exits; root-caused and fixed-by-upgrade in 0.4.3).
- punk::lib 0.3.0: has_libbug_udp_threadexit gathers live facts (loaded udp
version, else best available registered version discovered without loading
the binary via an unsatisfiable package require triggering the index scan)
and delegates the verdict to the pure classifier
libbug_udp_threadexit_applies (facts in, verdict out - testable). has_libbug_*
is the new check family for bundled/vendored library bugs; buginfo dicts may
carry a full 'url' reference key for non tcl-core trackers.
- punk 0.2.1: 'help tcl' scans has_libbug_* alongside has_tclbug_*, renders the
url key when present, and no longer errors on a triggered check carrying a
reference without a description (latent unset-indent fix).
- new checkbugs.test: classifier combination matrix, live-check dict shape and
classifier consistency, and a buginfo-contract test across all existing
check procs.
- verified: current kit (udp 1.0.13) reports bug=0 with no warning; a simulated
triggered has_libbug_* check renders description + url in the help tcl
warning block. Suites: punk/lib 21 pass, punk/ns 26 pass.
- G-036 flipped to achieved 2026-07-08 (index acceptance REMAINING annotated
DONE; detail file gains the Detection section). Open non-gating decisions
recorded in the detail file: punk8win (8.6) kit udp 1.0.12 swap; optional
upstream tickets for residual tcludp trunk weaknesses.
- project 0.4.7 (CHANGELOG entry)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- commented out five development trace puts in the doc-lookup machinery:
"PROC auto def"/"ENSEMBLE auto def" (generate_autodef) were emitted on STDOUT
and polluted 'i'/'s' output in script/exec contexts (agent-facing per the
G-015 piped-call pattern); "cmd_traverse - skipping to documented subcommand"
(space-form id path), "---> cmd_traverse ensembleparam" (ensemble -parameters
traversal) and "cmd_traverse 10 ... - review" (fallthrough return) were
stderr noise. No functional change.
- cmdflow.test: 8 new tests characterizing ensemble -parameters handling in the
doc-lookup flow (the punk::netbox::man pattern - leading apicontextid before
the subcommand at every ensemble level):
* generate_autodef models each -parameters entry as a -ensembleparameter 1
leader with @leaders min/max = nparams+1 (single- and two-param fixtures)
* cmd_traverse consumes parameter words and resolves the subcommand's
explicit or autodef docid, re-presenting the parameter in args_remaining
so the subcommand's own leader-based definition (as punk::netbox::man's
explicit defs model the curried parameter) can parse it
* nested sub-ensembles with their own -parameters re-consume the curried
parameter at each level
* punk::args::parse treats -ensembleparameter leaders as ordinary named
leaders; cmdhelp renders a documented subcommand's usage through the
parameter (end-to-end smoke)
- suites: punk/ns 26 pass (was 18), punk/args 93 pass + 1 skip (unchanged)
- project 0.4.6 (CHANGELOG entry)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- fixed "@opts -any 1" (arbitrary/adhoc option passthrough): an option not in
the definition crashed get_dict with "can't read argname" at the name-mapping
stage - adhoc opts now fall back to their raw supplied name, under which
argstate/arg_checks were already populated at scan time (real consumers:
vendored tomlish definitions declare -any 1)
- commented out a debug puts stderr (">>>_get_dict_can_assign_value NOT
alloc_ok...") that fired on every failed clause type assignment (visible on
any multiform parse miss; its happy-path twin was already commented)
- new forms.test (8 tests): @form spec structure (form_names, shared
{a b}-prologue blocks), explicit -form by name/index with cross-form
rejection, multiform synopsis rendering, and GAP pins for G-041 - default
parse is effectively form-0-only, -form rejects the documented list-of-forms
usage, and @form -synopsis overrides are stored in the spec but ignored by
synopsis rendering
- new validation.test (11 tests): -type validation matrix, -range/-minsize/
-maxsize, -nocase choice normalization, option -multiple accumulation,
required-option enforcement, unknown-option rejection vs -any adhoc
passthrough (the bugfix's regression test), -- end-of-options marker,
@values -max enforcement, received-key clause ordinals, solos list, and
default-vs-received distinction
- src/tests/AGENTS.md: note that tcltest compares the -body return value -
bodies ending in a loop must end with an explicit 'set result'
- suites: punk/args 93 pass + 1 skip (was 70+1), punk/ns 18 pass; full
source-tree run 569/581 with only the known exec-14.3 baseline failure
- project 0.4.5 (CHANGELOG entry)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- GOALS.md: G-040 punk::args choice aliasing with parse normalization, display
folding, and doc-lookup parity (user-approved); detail file records the
prefix/deny/reserve collapse analysis from the punk::help work, the
cmd_traverse divergence, the c/to/tc minimum-prefix policy question, and the
test-first sequencing
- new punk/ns cmdflow.test (10 tests): cmdwhich/cmdinfo/cmd_traverse docid
flow - direct, via-alias and subhelp-choiceinfo resolution (exact + unique
prefix), unknown-word fallback to parent docid, -choiceprefix 0 exact-only,
space-delimited ids, cmdhelp -return string smoke; the parse-vs-doc-walk
divergence (deny/reserve lists honoured by punk::args::parse, ignored by the
cmd_traverse walk) pinned as GAP tests pending G-040
- choices.test: 4 new unrestricted-mode (-choicerestricted 0) tests - prefix
normalization to the canonical choice, unknown/ambiguous passthrough,
denylist and reservelist passthrough, and the punk::help collapse recipe
(prefix+deny+reserve on a -multiple leader)
- punk::args 0.2.2 (doc-only): define doc -choice* fields now state the
-choicerestricted 0 passthrough semantics, -choiceprefix result
normalization + minimal-prefix display highlighting, denylist behaviour for
shorter prefixes in both restricted modes, and the reservelist phantom-entry
idiom for per-choice minimum-prefix control
- suites: punk/args 74 pass + 1 skip (was 70+1), punk/ns 18 pass (was 8)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- each topic (topics|help, tcl, env|environment, console|term|terminal) is a
handler proc returning {channel text} chunks, with its own punk::args
definition (id ::punk::helptopic::<topic>) so 'i help <topic>' renders
documented usage
- registering a topic (re)generates the ::punk::help / ::punk::help_chunks
definitions, so 'i help' shows a documented topic table that always matches
the registry (previously an autogenerated stub); the registry is the seam
for future subshell-declared topics
- 'help topics' derived from the registry (all aliases listed, fits 80 cols)
- no-arg overview and command-fallthrough output byte-identical to before;
80-column layout preserved; verified on tcl 9 and 8.6 kits via the script
and shell subcommands
- 'help env' degrades to a one-line notice when punk::config is not
initialised (e.g. script contexts) instead of an error stack + exit 1
- shared table_block helper destroys table objects after print (previously
leaked several per help invocation)
- project 0.4.4 (CHANGELOG entry)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
The version-mismatch and missing-header warning branches interpolated the
literal [X.Y.Z] inside double-quoted puts strings, so Tcl attempted command
substitution and the check died with `invalid command name "X.Y.Z"` the
first time a warning branch actually fired (surfaced by a manual
projectversion check during the 0.4.2 bump, whose CHANGELOG entry was
missing at the time). Brackets now backslash-escaped. Layout copies of
make.tcl synced by the project build included.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Piping a script into shell mode then exiting the restarted interactive repl
intermittently froze the shell: shellfilter::run's teardown made a bare
synchronous thread::send (settings-reset in shellthread::manager::unsubscribe)
to the shellfilter-run syslog log worker, whose event loop had silently
stopped servicing events (G-036: Tcl 9-only console+udp worker wedge -
root-cause tracked separately).
- shellthread 1.6.3: unsubscribe settings-reset send is now -async (sends to
a thread are FIFO, so a later reuse still sees the reset applied first);
shutdown_free_threads keeps its timeout timer armed across all vwait
iterations (was cancelled on the first response, leaving later waits
unbounded); get_tag_config's bare sync send flagged as same hazard class.
- shellfilter 0.2.4: shellfilter::run honors its -syslog option (default
empty -> noop runtag log worker, no thread created) instead of the
hardcoded 127.0.0.1:514 debug leftover. Syslog remains supported when
explicitly configured.
Verified: automated repro (hidden console, piped script scheduling a delayed
::punk::repl::exit in the restarted repl) hung before, exits cleanly after -
including with syslog force-enabled against a genuinely wedged worker.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
New suite src/tests/modules/punk/libunknown/testsuites/shadowing/
(7 tests, all pass) pins the .tm same-version shadowing rules verified
experimentally 2026-07-06/07: tcl:™️:add PREPENDS each argument (last
arg ends at the head of tcl:™️:list); the head of tcl:™️:list wins
exact-version ties (single and separate add calls); a higher version
wins from any position (order only arbitrates exact ties); and
punk::libunknown preserves all of it (scanner parity). Shipped behaviour
depends on these rules - the runtests testinterp tm ordering (bootsupport
over vendormodules) and punk_main''s package-mode block precedence that
G-033''s proj:/kit-first design builds on - so a Tcl or libunknown change
that shifts the tie-break now goes red here instead of silently
mis-resolving.
Goal G-035 (proposed): characterise mixed .tm / pkgIndex.tcl provision of
the same package - same or differing versions, under standard package
unknown, punk::libunknown and punk::packagepreference, across
scan-trigger orderings, forget/re-require crossing forms, and 8.6/9.
Converts the standing informal rule ("avoid mixing provision forms for
one package - unexpected behaviour even with libunknown''s improvements")
from folklore into either substantiated AGENTS.md guidance with named
failure modes, or retirement if mixing proves predictable. Discovery
work, so goal-specified rather than written now; extends the shadowing
suite when implemented.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Adds src/tests/shell/testsuites/punkexe/scriptexec.test (12 tests, all
pass) covering app-punkscript / the script subcommand (G-015): piped
execution and honest exit codes, error->exit 1, exitcode propagation,
stdin result echo, default-env dev alias present without boilerplate,
file-form argv passing, the lib:<name> scriptlib mechanism (subpath,
extensionless, bare-arg reclassification, not-found location listing),
and the tclsh-matching Tk main-loop behaviour (0.4.1): a serviced
after-callback runs and exits, a no-exit GUI script stays alive, an
errored GUI script exits promptly without hanging.
Reuses the shellexit.test built-exe harness pattern (pipe stdin,
half-close for EOF, timeout+force-kill) so runs are hang-proof; Tk cases
gated by an auto-detected punktk constraint (probes whether the kit loads
Tk). Full shell suite: 19 pass / 2 skip (console-only punkgoals) / 0 fail.
lib:/Tk cases resolve committed fixtures under scriptlib/_punktest/ - a
lib:-resolvable fixture cannot live in a tcltest temp dir since resolution
only searches scriptlib locations relative to the exe. The subfolder is
test-owned (README + do-not-edit fixture headers); root AGENTS.md carves
it out as the one agent-manageable exception to scriptlib being
user-only. No project version bump - tests/docs/fixtures only, no shipped
shell behaviour change.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Root cause: Tk registers a main loop (Tcl_SetMainLoop); tclsh''s Tcl_Main
services it after the script, so `tclsh gui.tcl` behaves like wish. The
script subcommand sourced-then-exited, so a Tk script''s after-callbacks
never ran (scriptlib/tktimer.tcl flashed and died; shell was the
workaround). Confirmed: native tclsh tktimer 1 blocks ~1.35s; script did
0.38s.
Fix: after the script body (success paths only), if a Tk main loop is
registered (info exists ::tk_version && winfo exists .), tkwait window .
services the event loop until the main window closes, then exit - the
script-level equivalent of Tcl_Main. A script that exits from a callback
(tktimer countdown) terminates directly. Console scripts have no main
loop and exit at once; a script error exits at once (no hanging window).
Verified both generations: tktimer via script now blocks the countdown
(punk902z 1.49s, punksys 1.57s - Tk present in both) exit 0; tkhello_exit
immediate; tk-script-that-errors exit 1 in 0.37s (no hang); tkhello (no
self-close) blocks like wish; console script unaffected. Enables G-020
GUI automation via `script` instead of `shell` (noted in G-020 detail).
Project version 0.4.1.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
app-punkscript accepts lib:<name> (with or without .tcl - extensionless
appends it) as the shell subcommand does: `punkexe script lib:hello` or
bare `punkexe lib:hello` via reclassification. The prefix always wins; a
literal path beginning lib: (pathological - colon is illegal in Windows
filenames) is reachable via ./lib:... Not-found errors list every
searched location; only .tcl runs via the script subcommand.
Resolution policy is factored into punk::path::scriptlib_resolve
(PUNKARGS-documented) rather than copied inline: kit-internal
app/scriptlib first and deliberately not externally overridable, then
scriptlib dirs relative to the executable - the same policy
app-punkshell encodes inline (todo noted: refactor the shell path onto
the shared proc). Share definitions via modules, not launcher control
flow.
Verified on both generations (punk902z, punksys): lib:hello
extensionless and explicit, bare-arg reclassification, not-found
candidate listing (bin/scriptlib then <root>/scriptlib for a bin/ exe).
Project version 0.4.0 (backward-compatible behaviour addition).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
app-punkscript loads punk::args::moduledoc::tclcore (catch-guarded,
mirroring the punk::repl code-interp setup) so core Tcl commands are
documented in script runs as at the repl: `''i list'' | punkexe script`
now renders the doc table instead of "Undocumented command". Cost ~40ms;
kit script startup ~0.33s. REVIEW note added: the default-punk-shell
package set is now expressed in app-punkscript AND the repl codethread
setup - consolidation into one shared definition is the G-015-noted
follow-on.
punk::packagepreference 0.1.1: moduledoc auto-load success notice moved
stdout -> stderr (stdout must stay machine-clean for script/exec
contexts; the failure branch already used stderr).
Verified on both generations (punk902z, punksys): i list doc table on
stdout, notice on stderr, timing unchanged. Build-order footnote: lib
changes need make.tcl libs before vfscommonupdate - the modules-only
first rebuild shipped a stale app-punkscript payload.
Project version 0.3.1.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
New lean app package src/lib/app-punkscript behind the (previously stub)
script subcommand in punk_main.tcl:
- `<commands> | <punkexe> script` runs piped stdin to EOF - no trailing
exit needed; `<punkexe> script <file> ?args?` runs a file with
conventional ::argv0/::argv (bare non-subcommand first arg still
reclassifies to the file form). Script may itself read stdin in the
file form.
- Default punk shell environment via shared definitions (package punk
registers deck aliases e.g dev; punk::aliascore::init adds the utility
aliases) - no local alias lists, no boilerplate in piped one-liners.
- Honest exit codes: 0 success, 1 on error with errorInfo on stderr,
script''s own exit honoured. No shellfilter stacks/transforms, no
logging side effects, no interactive fallback (terminal stdin with no
scriptname is a usage error, never a blocking read or shell).
- Stdin form echoes the script''s final result when non-empty (one-shot
eval ergonomics: return-value commands like `dev projects.work <glob>`
emit their table without a puts wrapper); file form keeps pure script
semantics.
Verified on both generations (punk902z/tcl9 from _build - bin deploy was
file-locked; punksys/tcl8.6 from bin): piped success/error/exit-code,
file form with args and stdin passthrough, missing-file error, motivating
one-liner `dev projects.work *tomlish*` emitting the project table with
exit 0, `src` mode combination (dev modules load), and `shell` subcommand
regression smoke. G-015 flipped to achieved 2026-07-07 per GOALS.md
rules; goal detail records the verification and the manual console
spot-check item. Project version 0.3.0 (new user-visible shell
capability - minor bump).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
vendorupdate pulls tomlish 1.1.10 from a clean tomlish checkout: the
library API punk::config will consume now carries punk::args (PUNKARGS)
documentation - from_toml, to_dict, from_dict, to_toml,
update_tomlish_from_dict - completing the G-014 precondition
(documentation added upstream in the tomlish project, landed here by
re-vendoring, never by editing src/vendormodules copies). 1.1.10 also
carries the explicit tomlish::cmdline_main entry point (wrapped-exe
startup fix; the load-time argv side effect is gone).
vfscommonupdate refreshes the punkshell kit payload to the current built
modules: shellfilter 0.2.3 (channel-restoration try/finally), shellrun
0.1.4, punkboot::utils 0.1.1, punk::console 0.7.1 content catch-up, and
tomlish 1.1.10.
Project version 0.2.7.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com