Build/promotion commands (project packages modules libs vfs vfslibs bin
bootsupport vfscommonupdate) now warn when src/ has uncommitted fossil/git
changes - artifacts built from dirty src have no committed provenance.
Warn-only by default; new -dirty-abort flag makes the check aborting.
With '<builtexe> src' available for evaluating uncommitted source directly,
building is the promotion step this treats it as.
- punkboot::utils 0.2.0: vcs_dirty_warnings gains optional scope arg so only
changes under a subpath (src) count; unscoped vendorupdate call unchanged.
- Warnings print with a plain column-0 PROVENANCE-WARNING: token (greppable
in redirected output) + ANSI colour, shared with vendorupdate's dirty
source-project check, and are recapped at end-of-run via a wrapped ::exit
so they survive scrolling chatty build output.
- Interactive terminal runs (stdin -inputmode probe, tcl 8.7+/9) get a 3s
ctrl-c grace countdown before a dirty build proceeds; piped/agent/CI runs
pay no delay.
- 'make.tcl check' reports src provenance status and what the build
commands would do.
- Guarded require: stale/missing punkboot::utils snapshot degrades the check
to a skip notice, but -dirty-abort then aborts rather than silently losing
the requested strictness.
Project 0.10.2 -> 0.10.3.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Verified nothing depends on the old version or filename before renaming:
all 'package require punk::libunknown' sites are unversioned; punk_main.tcl
and punk::repl locate the module by globbing libunknown-*.tm and picking the
highest by vcompare (so the renamed dev copy now also outversions the stale
bootsupport and project.vfs 0.1 copies deterministically, instead of relying
on tm path order for a same-version tie); bootsupport's
include_modules.config lists it by name only.
The 0.2.0 number retroactively versions the two changes shipped against the
0.1 filename earlier today (register_all_tm deep discovery - a minor API
addition - and the source_pkgindex sourcing-scope fix). A version-history
comment block in the module header now substitutes for the buildversion.txt
changelog other modules carry.
src/modules/AGENTS.md: the punk::libunknown exception bullets now spell out
the manual-versioning mechanics for agents - same Patch/Minor/Major bump
rules as buildversion-tracked modules, executed as a file rename plus Meta
line, manpage_begin, provide-block and version-history updates, with a
check for exact-version/filename dependents before any first bump.
discovery.test/libsearch.test: the source-tree libunknown locator now picks
the highest libunknown-*.tm by vcompare instead of plain lsort (robust to
future version bumps). Verified live in punk902z src: package provide
reports 0.2.0, register_all_tm present, lib.search deep discovery intact.
Suites green under tclsh90 and tclsh87. Build artifacts synced (root
modules/ and _vfscommon.vfs carry only libunknown-0.2.0.tm).
punkshell 0.10.2. Stale 0.1 copies remain in src/bootsupport/modules
(pending 'make.tcl bootsupport') and src/vfs/project.vfs (pending layout
maintenance) - both now cleanly outversioned at runtime.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
zipfs_tclPkgUnknown previously declared 'global dir' and executed pkgIndex.tcl
scripts via 'namespace eval ::' - the pkgIndex $dir contract was met by
writing the global namespace's dir variable, silently overwriting any user
global named 'dir' whenever a package require fell through to the pkg unknown
handler (observed misdirecting a file write into the user's Tcl installation
during test development), and each index's stray unqualified variables (ver,
pkg, script, _CawtSubDirs, ...) leaked into ::.
Index scripts now execute in a source_pkgindex proc frame: $dir is a formal
parameter and auto_path/env are global links - exactly the environment stock
tclPkgUnknown documents (its indexes see dir as the handler's proc-local plus
'global auto_path env'), without stock's incidental exposure of all its other
proc locals. tcl::Pkg::source uplevels into the caller's frame, so the scheme
works unchanged for the 8.6 pre-tip459 fallback. The legacy non-epoch branch
already sourced in the handler frame and simply gains stock parity from the
'global dir' removal.
Reviewed against tcllib 2.0's vendored index behaviours (all compatible):
qualified '$dir ni $::auto_path'/'lappend ::auto_path' extension in the
top-level index, its own apply-scoped subindex sweep (the same isolation
idiom), vsatisfies guards with early return, 'package provide' during index
sourcing (try/pkgIndex.tcl on 9+), and critcl-generated ifneeded strings
(md5c, tcllibc) whose proc definitions run at require time, not source time.
One deliberate divergence from stock: each index file gets its own frame,
so unqualified variables no longer persist between index files within one
sweep (stock shares its handler frame across the sweep) - nothing in tcllib
relies on that.
discovery.test: the GAP pin flips to libunknown_pkgunknown_preserves_global_dir,
plus new pins for the index contract - $dir-based ifneeded/source works,
unqualified 'lappend auto_path' extension reaches the real ::auto_path, and
stray index-script sets don't leak to ::. Verified live in punk902z src:
::dir no longer set at startup, user dir global survives package activity,
tcllib md5/struct::set/json load through the new frame, lib.search deep
discovery unaffected. Remaining startup globals def/pkg/ver come from
punkshell's own boot code (punk_main.tcl foreach loops), not the handler.
punkshell 0.10.1. punk::libunknown remains manually versioned at 0.1;
vfs artifact synced, bootsupport copy untouched as before.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
punk::libunknown gains register_all_tm: walks every tcl:™️:list path
recursively and registers ifneeded scripts for .tm modules at every
namespace depth (the tm unknown handler only registers siblings at the
requested depth, leaving never-requested subfolders like test::* invisible
to 'package names'). First registration wins - parity with the handler and
the shadowing pins. Reuses/populates the per-epoch directory index cache
(zipfs whole-tree listing; per-dir globs for filesystem paths; #modpod-*,
#tarjar-* and _build skipped) and runs at most once per tm epoch per
interp - the guard is the interp-local tm_fullscan variable, deliberately
not in the shareable epoch dict since ifneeded registrations are
interp-local.
punk::mix::commandset::loadedlib 0.2.0: search runs register_all_tm plus
the auto_path pkgIndex sweep by default when punk::libunknown is active,
so 'dev lib.search test' now shows deep modules without -refresh.
-refresh is repurposed to mean a genuine filesystem re-scan: package epoch
incr (invalidating the scan caches) then rediscovery - picking up .tm
files added/removed on disk. Without punk::libunknown (or with an older
copy lacking register_all_tm) -refresh falls back to the previous
dummy-require deep walk. Dependency fixes: highlight ansi computed only
when -highlight 1 via qualified punk::ansi with inline require (previously
relied on the shell-global a+ alias unconditionally and errored in bare
interps), inline requires for punk::path (fallback walk) and textblock
(table output).
New test suites (green under tclsh90 and tclsh87):
- modules/punk/libunknown/testsuites/discovery/discovery.test: lazy-depth
sibling registration, register_all_tm all-depths/cached-per-epoch/
head-wins-parity, package epoch shape and trace-driven increments,
epoch-cache short-circuit + incr recipe, and a GAP pin of
zipfs_tclPkgUnknown clobbering a user global 'dir' variable (stock
tclPkgUnknown keeps dir proc-local; fix candidate).
- modules/punk/mix/testsuites/loadedlib/libsearch.test: search match
semantics, default deep discovery, -refresh re-scan contract, highlight
without shell-global ansi aliases.
Both suites source the source-tree punk::libunknown directly by path:
'package require' would tie-break the same-version bootsupport copy by tm
path order (testinterp order favours bootsupport; child-interp
'tm::add {*}list' reverses precedence).
Verified live in 'punk902z src'. Build artifacts synced via make.tcl
packages + vfscommonupdate (loadedlib-0.2.0.tm, libunknown-0.1.tm).
Note: src/bootsupport copy of punk::libunknown intentionally untouched -
becomes stale until the next 'make.tcl bootsupport' sync; loadedlib
degrades gracefully against it. punk::libunknown remains manually
versioned at 0.1.
punkshell 0.10.0 (see CHANGELOG.md; also records the 0.9.1 doc-only
-refresh help rewrite).
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- src/tests/AGENTS.md: agent-added tests carry a one-line provenance comment `#added <YYYY-MM-DD> (agent[, G-<id>])[ - <short note>]` - immutable facts only (never "G-NNN will flip this" expectations, which live in _GAP_ test names and goal files); user-added tests optional; no bulk retrofit of existing suites. Rationale: provenance must survive where VCS history does not travel - packaged test modules (G-029), git+fossil dual history, file regeneration/moves
- GOALS.md maintenance rules: marking a goal superseded/abandoned includes a test sweep - grep G-<old id> (at minimum src/tests) plus the tests named in the goal's detail-file Acceptance, and record each test's disposition (transfers to the superseding goal / stands down to plain characterization / lapses) in the appropriate detail file; the sweep never deletes, skips, or weakens tests on its own
- the provenance lines are what keep the supersession sweep's grep reliable over time; closes the dangling-GAP-pin risk analyzed for superseded goals
- no project version bump: governance docs only
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- GOALS.md collapsed to summary entries (ID/status/title/Scope/detail pointer): 104.6KB -> 23KB so a full read stays cheap; Scope lines kept verbatim so path-grep goal discovery still works
- canonicality split: index canonical for ID/status/title/Scope, detail file canonical for Goal:/Acceptance:; all 61 detail-file headers reconciled from the index (fixes stale copies, e.g. G-036 detail acceptance predated the achieved rework), bodies preserved untouched
- detail files created for the three index-only goals G-017, G-034, G-054; every goal now has one
- 10 achieved goals (G-007 G-015 G-036 G-037 G-040 G-046 G-049 G-054 G-058 G-059) archived: summary records appended to GOALS-archive.md, detail files moved to goals/archive/; archive-on-flip replaces the ~30-achieved-entries trigger
- GOALS.md preamble/rules reworked: read workflow (read index in full, then detail files of scope-intersecting goals before editing), contract spans both tiers with proposal-first unchanged, next-free-ID counts the archive, achieved flip judged against the detail-file Acceptance and includes archiving
- progress convention codified from G-001 increment-1 practice: index entries carry status only, never progress; incremental progress lives in the detail file's ## Progress section (landed/remaining); achieved flip requires the remaining-work list resolved - a partial increment never flips a goal
- goals/AGENTS.md rewritten for the split (required 4-field header, one detail file per goal, orphan rules covering archive/); root AGENTS.md Child DOX Index + Repo-wide Notes updated; GOALS-archive.md preamble updated
- stale detail-file paths updated to goals/archive/ in src/lib/app-punkscript/punkscript.tcl (comment), runtimebash_wsl.test, wslprobe.tcl and the G-042 detail file; propagated copies (root lib/, src/vfs/_vfscommon.vfs, src/_build) left to refresh via normal build steps
- no project version bump: governance docs and source comments only, no user-visible shell behaviour
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- three new backend modules under src/modules/opunk/console/, base ::opunk::Console and punk::console UNCHANGED (the G-001 acceptance constraint - verified diff-clean):
* opunk::console::test / ::opunk::TestConsole - deterministic channel-pair test double: fixed size via -columns/-rows, is_console_or_tty/can_respond 1 (settled value wins), at_eof = plain chan eof with NO probe (pending bytes never consumed - pinned). The console seam identified by the G-044 repl characterization work
* opunk::console::ssh / ::opunk::SshConsole - socket-carried terminal sessions: construction-time capability (detection is the wrong tool - per goal detail), chan-eof without byte-consuming probes, size via the registered ANSI size-query provider over the connection. Flagship test: a scripted remote terminal answers CSI 6n over a socket pair and size resolves 100x30 through punk::console machinery querying the socket
* opunk::console::tk / ::opunk::TkConsole - Tk text widget as terminal: widget path in the inherited in/out slots (documented non-channel reuse), terminal_class tk-text, size from widget char dims, at_eof via backend marker (opunk::console::tk::set_eof) or widget destruction; no Tk require at module load. Verified live under punk91 src (the tk-loading experiment kit)
- voo -extends subclassing pattern recorded in opunk/AGENTS.md: children inherit public accessors + field INDEX variables (not parent-private my.* accessors) - constructors initialise inherited private fields via index vars, method bodies use parent public accessor methods; virtual dispatch via the slot-0 tag needs no base changes
- tests: modules/opunk/console backends.test (8 tests: dispatch, spec_resolve acceptance of subclass values, probe-free eof, settled-capability precedence, ssh capability/eof/size-over-socket/settled-0-no-emission, tk gated behind env PUNK_TEST_TK=1) - 7 green + tk skip on tcl 9.0.3 and 8.7; tk case verified standalone under punk91
- goal detail records progress + remaining work (repl -console launch wiring, output-channel parameterization, interactive acceptance verification); goal stays active
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- the ::tcl::string::is definition harvests its class set at define time from the bad-class error message of a deliberately invalid probe (safe, side-effect-free) - accept/reject parity with the loading interpreter by construction: 8.6.13 = 21 classes (no dict - the previous static 9.0 list wrongly ACCEPTED string is dict there), 8.7a6 = 23 (+dict +unicode), 9.0.3 = 22 (unicode removed). Fallback to the 9.0 set if the message format ever changes
- hand-written man-page descriptions (kept verbatim, tstr-processed as before so the A_WARN highlights are unchanged) apply only to classes the runtime accepts; accepted-but-undescribed future classes get a generic label; static version notes added to dict (not in 8.6) and the new unicode entry (unreleased 8.7 only - removed in tcl 9). The per-class virtual docids (::tcl::string::is <class>) follow the harvested set automatically
- new tclcoreparity.test (4 tests, gated on have_tclcoredocs): choices equal the live-harvested set and every documented choice is really accepted; per-class docids exist for every class; error-vs-ok outcome agrees between real string is and parse_status across the 23-shape probe matrix (missing args, trailing flag-like str, option/class prefixes + ambiguity, unknown option/class, -failindex var consumption, per-version dict/unicode presence, divergent-classification shapes); version-note labels conditional on presence. Expectations derived from the LIVE interpreter, never version arithmetic - green on 9.0.3, 8.7a6 and (via a direct tcltest driver, since runtests infrastructure does not run under the plain 8.6 kit) 8.6.13
- full punk/args + punk/ns trees green (219 pass + 1 pre-existing skip) incl the have_tclcoredocs cmdhelp pins
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- new commandcomplete.test (6 tests, green 9.0.3 + 8.7): punk::lib::system::incomplete pure-function characterization - the info-complete quoting quirk (a quoted word with unbalanced braces e.g set x "{*}{" is complete standalone but needs extra closers inside a proc body; the pending-opener stack { " { " shrinks per typed closer), single openers, tabs in open braces, escapes, and an incomplete<->info-complete parity property over a case battery
- goals/G-044 detail: the user-specified preserve-list any repl refactor must honour (info-complete parity + quirk, closing-prompt hints incl the accepted single-candidate limitation pinned-not-fixed, raw-mode colour staging in-progress vs submitted, literal tab acceptance with raw-mode marker edit smarts, dim space dots display-only never leaking into submitted strings/history, up/down navigation of MULTILINE editbuf history with recalled entries editable) + testability findings: class_editbuf is console-coupled at its core (add_chunk renders via overtype::renderline against live terminal metrics - cursor-position size probing, DECRQPSR tabstops) so items 3-6 are not unit-characterizable until a console seam exists - G-001 (pluggable console backends / ::opunk::Console test double) is the enabling refactor, not just a feature goal
- goals/G-020 detail: repl interactive-behaviour verification recorded as a driving use case (near-term windows harness: keystroke injection + capture) pending the durable pseudoconsole expect-alternative
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- list -remote against an unreachable artifact server now warns and compares using the previously fetched sha1sums.txt instead of dying on an unhandled Invoke-WebRequest error (parity with the bash payload); no cached copy -> clear error + return
- the fetch path always fell through silently to a cached sha1sums.txt on download failure - it now announces "proceeding with cached copy" (behaviour unchanged, messaging explicit)
- fixed latent undefined-variable bug: the list -remote branch created the runtime folder with $container (only defined in the fetch branch) - now $archfolder
- bin/runtime.cmd regenerated (checkfile clean, roundtrip pin green 9 pass + 1 skip); verified live: unreachable PUNKBIN_URL -> cached-fallback table renders (tclsfe-x64 "Same version" against the punkbin pin), normal fetch path unchanged
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- new src/tests/testsupport/wslprobe.tcl (::punktest::wsl): memoized capability probe for the wsllinux constraint - default distro launches and unames as Linux, tools present (bash/mktemp/wslpath + sha1 tool), native tempdir file round trip; any failure -> skip with reason, so wsl.exe-present-but-unusable installs cannot false-positive. Staging helpers (staging_create/copy_in/run_in/cleanup) enforce the design: execution on the distro NATIVE filesystem only, shared /mnt used for one-way copy-in/out, the windows checkout never operated on from inside WSL (DrvFs slowness + cross-boundary stat differences that make git re-hash its index and fossil see phantom changes). Probe uses wsl -e invocations only (wsl --status/-l emit UTF-16)
- multishell.test: scriptwrap_multishell_exec_wsl_sh - the polyglot sh payload produces its marker under the distro bash from native staging (the direct isunix twin still skips on windows)
- new shell/testsuites/binscripts/runtimebash_wsl.test (8 tests): runtime.bash first-ever execution on real unix - multi-candidate run errors listing candidates, use/list/run round trip with argument passing, PUNK_ACTIVE_RUNTIME override, unknown/unselectable use errors, stale-active guidance, single-candidate fallback, offline list -remote (crafted cached sha1sums.txt + PUNKBIN_URL forced fetch failure), and the checkout-untouched guard (git status --porcelain identical before/after per acceptance)
- green on tcl 9.0.3 + 8.7 against Ubuntu-24.04/WSL2; suites skip cleanly when the probe fails; enablement/limitations in src/tests/AGENTS.md (known limitation: a present-but-HANGING wsl can stall the probe)
- GOALS.md G-059 flipped to achieved 2026-07-11; detail file records the outcome incl the UTF-16 discovery and the 512-byte label regression this work surfaced (caught by the guardrail suite as designed)
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- bash/zsh payload gains list -remote parity with the powershell payload: local-vs-server sha1 comparison table (Same version / UPDATE AVAILABLE / not listed on server, plus remote-only entries), falling back to a cached sha1sums.txt with a warning when the server is unreachable (the powershell version has no cached fallback - candidate backport)
- both payloads: artifact server base url overridable via PUNKBIN_URL (mirrors/testing - also what makes the new feature testable offline)
- first real catch for the 512-byte label guard: the payload growth pushed the template's :exit_multishell target label across a boundary from its callsite (checkfile ERROR at wrap time); fixed with a documented byte-alignment spacer comment at the end of runtime.bash, and the resize-the-spacer workflow recorded in bin/AGENTS.md
- bin/runtime.cmd regenerated via scriptwrap (roundtrip pin green; multishell suite 9 pass + 1 skip on 9.0.3 and 8.7); runtime.bash syntax-checked under bash and zsh; windows list/use/run sanity re-verified
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
- 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