# Changelog All notable changes to the punkshell project version are documented here. The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.toml`. Entries are newest-first; one bullet per notable change. See the root `AGENTS.md` "Project Versioning" section for the bump policy. ## [0.12.1] - 2026-07-11 - G-062 achieved: canonical project license declared as BSD-2-Clause. `LICENSE.txt` added at the repo root with the standard BSD-2-Clause text (copyright Julian Marcel Noble, 2023-2026); `README.md` names BSD-2-Clause and points at `LICENSE.txt`; root `AGENTS.md` Repo-wide Notes names the license precisely; `punkproject.toml` `[project]` carries `license = "BSD-2-Clause"`. Repo-sweep found no top-level license claims contradicting it (vendored library licenses and module-level generated docs are not project-level claims). ## [0.12.0] - 2026-07-11 - punk::args 0.6.0: command synopses (`s `, `i `, usage headers) now display small restricted choice sets as literal alternates — an argument whose choice pool (`-choices` plus `-choicegroups` members) has 1–3 members and `-choicerestricted` true renders those words unitalicised joined by `|` (e.g. `after cancel` shows literal `cancel`; a 3-choice option shows `(left|centre|right)`), matching the existing display style of `literal()`/`literalprefix()` type-alternates. Larger or unrestricted choice sets keep the italicised argname/type display and an explicit `-typesynopsis` always wins, so e.g. `textblock::frame -type (choice|)` is unchanged. Synopsis rendering of type-alternates, `-typesynopsis` passthrough (including documenter ANSI styling), and multi-element clause display gained characterization test coverage (`synopsis.test`). ## [0.11.0] - 2026-07-11 - G-001 achieved: an interactive REPL can be launched against a non-detectable terminal-like device via an `::opunk::Console` subclass — `repl::init -console ` selects the console (channel pair, anchored instance name, or object value), `repl::start`'s input channel defaults to it, and the repl's output (prompts, results, and the code interp's stdout/stderr — diverted via shellfilter junction stacks and emitted per run) routes through the selected console's channels, with eof/size/capability answered by the subclass overrides (punk::repl 0.4.0; base `::opunk::Console` and `punk::console` untouched). Verified end-to-end by child-process driver tests: an `::opunk::SshConsole` socket session whose scripted remote terminal answers `CSI 6n` (size resolved over the socket) and a `::opunk::TkConsole` text widget wired as a live console by new `opunk::console::tk::console` (reflected output channel rendering into the widget + Return-binding input pipe, opunk::console::tk 0.2.0). Process-console behaviours (tcl_interactive prompt gating, stdin reopen on eof, raw-mode re-enable, the windows line re-decode experiment) now apply only when no foreign console is selected; default stdin/stdout repl behaviour is unchanged. ## [0.10.3] - 2026-07-11 - make.tcl 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 (G-026 direction). Warn-only by default; a new `-dirty-abort` flag makes the check aborting. Dirt outside `src/` is ignored (`punkboot::utils::vcs_dirty_warnings` gained an optional scope argument, utils 0.2.0). With ` src` / ` src shell` available for evaluating uncommitted source directly, building is the promotion step the warning treats it as. - Provenance warnings (dirty-src gate and vendorupdate's dirty source-project check) are presented with a plain column-0 `PROVENANCE-WARNING:` token for automated discovery in redirected output plus ANSI colour for humans, and are recapped at the very end of the run (via a wrapped `::exit`) so they survive scrolling chatty build output. Interactive terminal runs (Tcl 8.7+/9 stdin `-inputmode` probe) additionally get a 3-second ctrl-c grace countdown before a dirty build proceeds; piped/agent/CI runs pay no delay. `make.tcl check` now reports src provenance status and what the build/promotion commands would do. ## [0.10.2] - 2026-07-11 - punk::libunknown bumped 0.1 → 0.2.0 (file renamed `libunknown-0.2.0.tm`), retroactively versioning the register_all_tm and source_pkgindex additions and adopting full `major.minor.patch` form. Verified nothing requires it by exact version or filename: punk_main.tcl and punk::repl glob `libunknown-*.tm` picking the highest by vcompare (the dev copy now also outversions the stale bootsupport/project.vfs `0.1` copies deterministically), bootsupport's include_modules.config lists it by name only. The module header now carries a version-history block in lieu of a buildversion.txt, and `src/modules/AGENTS.md` documents the manual-versioning bump mechanics for agents (same Patch/Minor/Major rules as buildversion-tracked modules). ## [0.10.1] - 2026-07-11 - punk::libunknown: pkgIndex.tcl scripts are now executed in an isolated `source_pkgindex` frame providing the documented `$dir` variable plus `auto_path`/`env` global links (matching stock tclPkgUnknown's contract), instead of at `::` scope with a `global dir`. Fixes any user global named `dir` being silently overwritten whenever a `package require` fell through to the pkg unknown handler, and stops index scripts' helper variables (`ver`, `pkg`, `script`, `_CawtSubDirs`, ...) leaking into the global namespace. Verified against tcllib 2.0's index behaviours (auto_path extension, apply-scoped subindex sweep, critcl-style loaders); regression tests pin the `$dir`/auto_path/no-leak contract. ## [0.10.0] - 2026-07-11 - `dev lib.search` now performs deep module discovery by default: new `punk::libunknown::register_all_tm` registers `.tm` modules at every namespace depth across all tm paths (reusing/populating the per-epoch directory index cache, at most one scan per `package epoch` per interp), so namespaced modules in never-requested subfolders (e.g. `test::*`) appear in search results without `-refresh` (punk::mix::commandset::loadedlib 0.2.0). - `dev lib.search -refresh` repurposed to mean a genuine filesystem re-scan: it increments the package epoch (invalidating punk::libunknown's scan caches) and re-runs discovery, picking up `.tm` files added/removed on disk and re-sourcing `pkgIndex.tcl` files. Without punk::libunknown active, `-refresh` retains the previous dummy-require deep-walk behaviour. - lib.search dependency fixes: works in bare interps now — highlight ANSI codes computed only when highlighting (fully-qualified `punk::ansi` with inline require; previously errored on the shell-global `a+` alias even with `-highlight 0`), inline requires for `punk::path` (fallback walk) and `textblock` (table output). ## [0.9.1] - 2026-07-11 - `dev lib.search` `-refresh` help rewritten to document actual semantics (punk::mix::commandset::loadedlib 0.1.1, doc-only): the flag performs a deep tm discovery pass (registering `.tm` modules in never-requested namespace subfolders), it does not re-scan directories already indexed in the current `package epoch` (run `package epoch incr` first for a genuine filesystem re-scan), and tm/auto_path list changes are epoch-invalidated automatically. New characterization test suites pin the underlying punk::libunknown discovery/epoch-cache behaviour (`src/tests/modules/punk/libunknown/testsuites/discovery/`) and the lib.search match + `-refresh` contract (`src/tests/modules/punk/mix/testsuites/loadedlib/`), including a GAP pin of the pkg-unknown handler clobbering a user global `dir` variable (stock Tcl keeps it proc-local). ## [0.9.0] - 2026-07-11 - G-001 (in progress) increment 1: three pluggable `::opunk::Console` backend modules, with the base class and punk::console untouched — `opunk::console::test` (`::opunk::TestConsole`: deterministic channel-pair test double with fixed size and probe-free eof — the console seam the repl/editbuf characterization work needs), `opunk::console::ssh` (`::opunk::SshConsole`: socket-carried terminal sessions — construction-time capability, chan-eof without byte-consuming probes, and size resolved by punk::console's ANSI cursor-report provider *querying over the connection*, proven against a scripted remote terminal answering `CSI 6n` over a socket pair), and `opunk::console::tk` (`::opunk::TkConsole`: a Tk text widget as terminal — widget char dimensions as size, backend eof marker via `opunk::console::tk::set_eof`, verified live under the tk-capable punk91 kit). Subclass values dispatch through existing base-class holders and `console_spec_resolve` virtually. Remaining for the goal: repl launch-time console selection and output-channel parameterization. ## [0.8.2] - 2026-07-11 - G-054 achieved: tclcore moduledoc 0.2.0 — the `string is` class choices shown by `i string is` (and the per-class docids like `i string is digit`) are now harvested from the running interpreter at define time instead of a hand-maintained list, so the documented/parsed class set always matches what the interpreter accepts (previously the static Tcl 9.0 list wrongly accepted `dict` under 8.6 and rejected 8.7's `unicode`). Version notes render on classes that differ across releases; unrecognized future classes get a generic label instead of vanishing. Behavioural parity pinned by `tclcoreparity.test` with expectations derived from the live interpreter — green on Tcl 8.6.13, 8.7a6 and 9.0.3. ## [0.8.1] - 2026-07-11 - `runtime.cmd` powershell payload: cached `sha1sums.txt` fallback backported from the bash payload - `list -remote` against an unreachable server now warns and compares using the previously fetched copy instead of dying on an unhandled download error, and the `fetch` path's pre-existing silent fall-through to a cached copy now announces itself. Also fixed a latent undefined-variable bug creating the runtime folder in the `list -remote` branch. ## [0.8.0] - 2026-07-11 - `runtime.cmd list -remote` parity for the unix (bash/zsh) payload: local-vs-server comparison table (Same version / UPDATE AVAILABLE / not-listed, plus remote-only entries), falling back to a cached `sha1sums.txt` with a warning when the server is unreachable. Both payloads gain a `PUNKBIN_URL` env override of the artifact server base url (mirrors/testing). - The polyglot's 512-byte label guard caught its first real regression during this change: the payload growth pushed the template's `:exit_multishell` label across a boundary; fixed with a documented byte-alignment spacer comment in `runtime.bash` (workflow recorded in bin/AGENTS.md). - G-059 achieved: WSL capability probing + native-filesystem staging for driving unix-side tests from Windows (`src/tests/testsupport/wslprobe.tcl`, `wsllinux` constraint). The MULTISHELL sh payload and the runtime.cmd unix payload (`runtime.bash` — never previously executed on real unix) now run green under WSL: active/use/run resolution, env override, stale-active guidance, single-candidate fallback and offline `list -remote` — all staged to the distro's native filesystem, with a guard test asserting the Windows checkout's git state is untouched. ## [0.7.0] - 2026-07-11 - `bin/runtime.cmd` gains a `use` subcommand and an active-runtime selection: which runtime `run` launches is now the per-machine `bin/runtime//active.toml` (`active = ""`, shown with a `*` in `list`), resolved as `PUNK_ACTIVE_RUNTIME` env override → `active.toml` → sole installed candidate — with multiple runtimes and no selection, `run` errors listing candidates instead of silently launching the last name alphabetically. The first `fetch` sets the active runtime only when none is recorded; later fetches never steal it. - `runtime.cmd fetch` checksum parity for the unix (bash/zsh) payload: it now fetches `sha1sums.txt`, skips when the local file already matches, downloads to `.tmp` and installs only on sha1 match (tool detection across `sha1sum`/`shasum`/`sha1`/`openssl`; refuses unverified downloads when no tool is present), and accepts an optional runtime name like the powershell payload. Bug fixes: the powershell "stored hash" message printed an empty variable; the bash MSYS branch had an invalid `interp =` assignment; `.tmp` leftovers and non-runtime files no longer appear as run/list/use candidates. - The runtime scriptset sources moved to `src/scriptapps/bin/` (alongside `getzig.*`), the proper home for bin-deployed scriptsets; `bin/runtime.cmd` regenerated via the scriptwrap workflow (round-trip pinned byte-identical by the multishell test suite). ## [0.6.0] - 2026-07-10 - G-058: punkshell boot honours statically-linked runtime packages. Kits built on runtimes with static extensions (e.g `tclsfe-x64.exe`: Thread/twapi/sqlite3/tdbc — the punk91 kit) previously lost `package require` access to them in every interp/thread punkshell fabricates: the appended vfs replaces the runtime's own `//zipfs:/app` mount (taking its pkgIndex files with it) and punkshell's boot path replacement discards what remains — the repl code interp failed with `can't find package Thread` and punk::console couldn't load. Now `punk_main.tcl` captures the static baseline at boot (probe-loading each empty-filename `info loaded` prefix in a throwaway interp and recording only packages the load actually provides; configurable denylist for side-effecting/composite inits), and the baseline seeds `package ifneeded {load {} }` mappings in the boot interp, the repl codethread and code interp (punk::lib 0.4.0 `interp_sync_package_paths`/`snapshot_package_paths`, punk::repl 0.3.0), and shellthread 1.7.0 workers — introspectable at the repl via `::punkboot::static_packages`. - punk::packagepreference 0.2.0: static-vs-bundled resolution is version-aware — requiring a baseline package triggers the package-index scan before resolution so all candidates register and the highest version wins (a static copy is no longer masked by an older bundled dll, and a genuinely newer bundled copy remains reachable); static *registrations* (empty-filename `info loaded` entries) no longer trip the loaded-shared-object same-version pinning. Composite statics whose script layer lived in the runtime's replaced zipfs (e.g. static twapi) provide nothing under the probe and correctly defer to bundled complete copies. - Tests: `staticseed.test` + `staticpolicy.test` (un-gated, simulated baselines) and `staticruntime.test` (kit integration behind the capability-probed `statickitavailable` constraint). The verification runtime `tclsfe-x64.exe` is pinned in the punkbin artifact repository (win32-x86_64 + sha1sums) so the constraint is satisfiable on other machines. ## [0.5.0] - 2026-07-10 - punk::args 0.5.0 (G-049): new `punk::args::parse_status` - runs a parse attempt and returns a documented parse-status structure instead of raising on validation failure: overall `ok`/`status` (valid|invalid|incomplete)/`scheme`/`message`/`errorcode`/`failureclass`/`badarg`/`form`/`receivednames` plus per-argument `argstatus` entries (class, status ok|bad|unparsed, received count/positions, value-in-effect including `-default` fill). `arg_error` accepts the structure via `-parsestatus` and both its renderers derive goodarg/badarg row marking and choice value-in-effect highlighting from it. Scheme colours now resolve per-render: the documented `-scheme` value `nocolour` takes effect (previously it fell through to whatever colours the last scheme render left behind) and scheme renders no longer mutate the shared colour array (the strike-only goodarg style no longer leaks into later renders). `missingrequiredvalue`/`missingrequiredleader` failures now carry `-badarg`, and `punk::args::parse` gained `-caller` to control the `%caller%` attribution in failure messages. - punk::ns 0.2.0 (G-049): `i ` (cmdhelp) gains `-return dict` - a machine-parsable dict of resolution info (origin/docid/cmdtype/args_remaining) plus the parse-status structure of the supplied argument words. Display fixes: a value failing its `-type` check now badarg-marks the unfillable argument's row (previously only choice violations marked anything), an explicit `-scheme` is honoured on the parse-failure path, and parse-failure messages name the queried command instead of leaking cmdhelp's internal parse source line at top call depth. ## [0.4.15] - 2026-07-10 - punk::args 0.4.2 (G-046): argument resolution no longer expands display-only `${...}` content (`-help` on `@cmd`/`@examples`/argument records, `@formdisplay` bodies) - expansion is deferred to help-display time with its own cache. First use of heavily documented commands is dramatically faster (`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. Also fixed: `@dynamic` multiline `-help` substitutions now align at their insertion column; prefix/alias-normalized choice values keep the same plain-string shape as exact input (`\Deleted`-style values); the `-return string` renderer aligns cmd-help continuation lines under the `Description:` label and its `Example:` line shows the example reference instead of the doc url. - punk::ns 0.1.4 (G-046): `i ` for a command whose definition requires arguments (e.g `i string is`, `i punk::args::define`) shows plain usage instead of an internal-looking "Bad number of leading values for punk::args::parse ..." error prefix - and builds the usage table once, not twice (the discarded advisory-parse error no longer renders its own table; display timing for large argdocs stays at pre-change levels). ## [0.4.14] - 2026-07-10 - punk::args 0.4.1: first `i ` in a fresh shell now shows subcommand-help markers and synopsis choicelabels for documented subcommands whose argdocs are lazily registered (e.g `i ansistring`); previously the autogenerated ensemble help omitted them until another path loaded the ensemble's registered namespace (`punk::args::update_definitions`). ## [0.4.13] - 2026-07-09 - punk::args 0.4.0 tidy-up (pre G-046): dead developer-test procs removed (`define2`, `parseXXX`, the unused `::punk::args::TEST` definition); the interactive `Get_caller` diagnostics and `lib::tstr_test_one` renamed with leading `__` and unexported; internal parser/renderer helpers moved from the exported `punk::args` namespace into a new internal `punk::args::private` namespace (underscore prefixes dropped); every remaining exported command now carries a PUNKARGS definition, so `i`/`punk::args::usage` render documentation for the whole punk::args API (including `punk::args::lib` utilities and the `helpers`/`argdocbase` ANSI shorthands). ## [0.4.12] - 2026-07-09 - Tcl 9.1b0 runtime compatibility (first `punk91.exe` build on `tclsfe-x64`; the developed baseline remains Tcl 9.0.2): - punk::lib 0.3.1: TIP 746 removed `lseq`'s expr-operand behaviour in Tcl 9.1 — `punk::lib::range` (lseq branch) now normalizes `int[+-]int` offset operands itself, so callers like `range 0 [llength $list]-1` keep working (this broke `punk::ansi::grepstr` and with it example-block highlighting and the punk::args examples.test under 9.1). Also aligned the lseq branch with the tcl8 fallback contract: default `by` now infers direction (descending `range 5 1` previously returned empty under tcl9 — silently breaking documented reverse-range callers) and `by 0` returns an empty list (9.1's `lseq ... by 0` changed to return one element). Direct `lseq` expression operands wrapped in `expr` (`lzipn_tcl9b`/`lzipn_tcl9c`/`cols`/`cols2`). `check::has_tclbug_safeinterp_compile` falls back to `interp invokehidden tcl:unsupported:disassemble` — Tcl 9.1 safe interps hide `tcl::unsupported::*` instead of exposing it. - punk::args 0.3.2: `zero_based_posns` expr-wraps its `count-1` lseq operand (TIP 746). - New `modules/punk/lib` range.test pins the `range` contract (offset expressions, direction inference, `by 0`) under both 9.0 and 9.1. ## [0.4.11] - 2026-07-09 - punk::args 0.3.1: fixed `helpers::example` `-title` path calling bare `[a]` (resolvable only where a global `a` alias exists, e.g. punk shells — errored under plain tclsh); found by the new test coverage. - punk::args test suite grown to 128 tests across 15 files with four new characterization areas, added before any changes to the definition-parsing/indentation machinery (user-directed tests-first): - rendering.test (15): nesting independence (plain + tstr defs), relative-indent preservation (the 2-space help and 1-space choicelabel conventions, single and multiline), `-unindentedfields` (arg ✓; `@cmd` accepts-but-ignores — GAP pinned), constructed (string-built) definition absolute-indent characterization, multiline `${[cmd]}`/`${$var}`/`resolved_def` insertions (aligned, nesting-independent), and `@dynamic` `${$DYN_X}` double substitution (stable across resolutions, provider refresh honoured in help/choices/parse, textblock::frame-style art choicelabels byte-aligned — but multiline `-help` insertions lose insertion-column alignment for lines 2+, GAP pinned: the second-round subst lacks paramindent re-alignment). - choicegroups.test (5): group/`-choices` union parsing, cross-group prefixes, duplicate-entry dedup, per-group titled choice tables, and punk::imap4-style `{\Deleted}`/`{$MDNSent}` values with labels — plus a GAP pin: prefix-normalization list-quotes special-character choices (`\De` stores `{\Deleted}` where exact input stores plain `\Deleted`). - 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 structure/title/`-tstr` toggle (no-dedent direct-call contract pinned), `${[example ...]}` insertion into `@cmd` help aligned with the auto `Example:` row, `@examples` → `punk::args::eg` with `#`/`@#` stripping (ANSI-tolerant), strip_nodisplay_lines unit, and a live tclcore `::lseq` smoke (the heaviest example consumer). - Investigated punk::ansi::mark_columns first-call slowness (reproduced: 3.94s first call, 60µs thereafter): the argdoc `-help` contains `${[...num_columns_example]}` which itself calls `mark_columns`, whose parse resolves the same still-resolving definition — the in-source TODO already documents the recursion hazard and candidate fixes (reentrancy guard, or deferring `-help` processing out of argument resolution, which would also help general first-parse performance). Recorded for goal consideration rather than patched. ## [0.4.10] - 2026-07-08 - G-040 completed (activated and flipped to achieved same day, user-directed): punk::args 0.3.0 adds `-choicealiases {alias canonical ...}` — aliases accepted exact under any `-choiceprefix`/`-nocase` setting, participating in prefix calculation when prefixing is on, always normalizing to the canonical choice in parse results; deny semantics apply to the matched name (a denied alias needs the full alias; a canonical reached via its alias is exempt from the canonical's own deny entry); usage display folds aliases into the canonical entry as an `(alias: …)` label note. The choice-word matching chain was extracted from `get_dict` into the shared resolver `punk::args::choiceword_match`, now also consumed by punk::ns 0.1.2's `cmd_traverse` — closing the doc-lookup parity gap (deny/reserve lists were honoured by parse but ignored by the walk; the pinned GAP tests flipped to agreement) and normalizing aliases before choiceinfo lookup. - punk 0.2.3: punk::help adopts the feature — `i help` lists one entry per registered topic (`topics` +alias help, `tcl`, `env` +alias environment, `console` +aliases term|terminal), and topic words accept aliases and unique prefixes with the user-decided minimum-prefix policy: `h`/`he`/`hel`, `e`/`en`, `te`/`ter`, and the reserved `c`/`to`/`tc` all fall through to command lookup. Argless `help` output remains byte-identical to the strict-80 baseline. - Suites: punk/args 98 pass + 1 skip (5 new alias tests incl. the punk::help policy matrix), punk/ns 28 pass (parity tests flipped, alias traverse + display-fold assertions), punk/lib 21 pass. ## [0.4.9] - 2026-07-08 - punk 0.2.2 (doc-only, from interactive review of the help restructure): `i help` / `i help ` usage tables narrowed to reasonable widths (61-68 columns, previously ~160) by manually folding the helptopic argdoc help texts at ~70 columns and rendering the generated topic-choice grid with `-choicecolumns 2`. Only the argless `help` overview is strictly 80-column (unchanged, byte-identical); usage tables do not yet wrap to terminal width, so punk::args source line lengths directly set table width — automated (potentially language-dependent) wrapping, e.g. via tcllib's text-adjust facilities, is a possible future improvement. ## [0.4.8] - 2026-07-08 - G-037 completed (flipped to achieved): new `make.tcl vfslibs` step (also run inside `make.tcl project`) propagates declared vendored platform-library packages into kit vfs lib_tcl trees — declarations in `src/runtime/vendorlib_vfs.toml` (tomlish-parsed, per-package per-kit with explicit `supersedes` removal so no silent mixed-version provision; punkcheck provenance at src/vfs/.punkcheck, outside kit payloads; malformed entries fail the build naming the entry). G-024's mapvfs toml conversion was not required; the file is recorded as a candidate for absorption into it. Authority is the declaration — undeclared drift (e.g. the vendorlib/kit twapi divergence) is deliberately untouched. - 8.6 kits upgraded to tcludp 1.0.13 via the new step (the G-036 loose-end decision): the 1.0.13 folder is dual-generation (pkgIndex selects tcl9udp1013.dll or udp1013t.dll), so the single vendorlib_tcl9 source serves punk8win.vfs/lib_tcl8, replacing udp 1.0.12. Verified on rebuilt kits: punksys/punkbi (8.6.13) and punk902z (9.0.2) all load udp 1.0.13 from their vfs; no udp1.0.12 remains under src/vfs. ## [0.4.7] - 2026-07-08 - G-036 completed (flipped to achieved): `punk::lib` 0.3.0 adds `punk::lib::check::has_libbug_udp_threadexit` — version-based detection of the tcludp < 1.0.13 thread-exit bug on Tcl 9 Windows (the per-thread exit handler closes process-global event handles, silently freezing every other udp-using thread's event loop after any udp-loaded thread exits; root-caused and fixed-by-upgrade earlier in 0.4.3). `has_libbug_*` is the new check family for bundled/vendored library bugs; `help tcl` (punk 0.2.1) now scans it alongside `has_tclbug_*`, supports a full `url` reference key for non tcl-core trackers, and no longer errors on a triggered check carrying a reference without a description. Detection is loaded-version based, falling back to the best available registered version without loading the binary. New checkbugs.test covers the classifier matrix, the live check's dict shape/consistency, and a contract test over all existing check procs. Open decisions recorded in the G-036 detail: punk8win (8.6) kit's udp 1.0.12 swap; optional upstream tickets for residual tcludp trunk weaknesses. ## [0.4.6] - 2026-07-08 - punk::ns 0.1.1: five development trace `puts` removed from the command doc-lookup machinery — "PROC auto def"/"ENSEMBLE auto def" from generate_autodef were emitted on STDOUT and polluted `i`/`s` output in script/exec contexts (visible to agents piping `i ` per the G-015 pattern); "skipping to documented subcommand", "cmd_traverse ensembleparam" and the "cmd_traverse 10 ... review" fallthrough note were stderr noise on the space-form-id, ensemble-parameter and undocumented-tail traverse paths. No functional change. - punk/ns cmdflow.test: 8 new tests characterizing ensemble `-parameters` handling in the doc-lookup flow (the punk::netbox::man pattern — a leading apicontextid before the subcommand): generate_autodef models each parameter as a `-ensembleparameter 1` leader with @leaders min/max = nparams+1 (single- and two-parameter fixtures), cmd_traverse consumes parameter words and resolves the subcommand's explicit or autodef docid (re-presenting the parameter in args_remaining for the subcommand's own leader parse, matching punk::netbox::man's explicit defs), nested sub-ensembles with their own -parameters re-consume the curried parameter at each level, parse treats -ensembleparameter leaders as ordinary named leaders, and cmdhelp renders a documented subcommand's usage through the parameter. ## [0.4.5] - 2026-07-08 - punk::args 0.2.3: fixed `@opts -any 1` (arbitrary/adhoc option passthrough) — an option not present in the definition crashed parsing with "can't read argname" instead of passing through with its value (real consumers: vendored tomlish definitions declare `-any 1`); silenced a debug `puts stderr` that fired on every failed clause type assignment (visible on any multiform parse miss). Found while building the punk::args comprehensiveness pass. - punk::args test suite expanded (94 tests, was 71 at start of day): new forms.test characterizes multi-form (@form) parsing and pins the G-041 gaps — parse without -form is effectively form-0-only, -form rejects the documented list-of-forms usage, and the @form -synopsis override is stored but ignored by synopsis rendering; new validation.test covers the -type validation matrix, -range/-minsize/-maxsize, -nocase choice normalization, option -multiple accumulation, required-option enforcement, unknown-vs-adhoc option handling, the -- end-of-options marker, @values -max enforcement, received-key clause ordinals, solos, and default-vs-received distinction. ## [0.4.4] - 2026-07-08 - punk::args 0.2.2 (doc-only): the `punk::args::define` documentation for the -choice* fields now spells out the verified interplay semantics — `-choicerestricted 0` passthrough of non-matching/ambiguous/denied/reserved words, `-choiceprefix` normalization of accepted prefixes to the canonical choice in parse results (with minimal-prefix highlighting in usage display), and the `-choiceprefixreservelist` phantom-entry idiom for per-choice minimum-prefix control. Characterization tests added (choices.test unrestricted-mode tests; new punk/ns cmdflow.test for the cmdwhich/cmdinfo/cmd_traverse docid flow, pinning the parse-vs-doc-walk deny/reserve divergence as GAP pending G-040). - help system restructured onto a topic registry (punk module 0.2.0, `::punk::helptopic`): each topic (`topics|help`, `tcl`, `env|environment`, `console|term|terminal`) is a handler proc with its own punk::args definition, and the `::punk::help`/`::punk::help_chunks` definitions are (re)generated from the registry — `i help` now renders a documented usage table with topic choices and summaries, and `i help ` shows per-topic documented usage (previously an autogenerated stub). `help topics` is derived from the registry (lists all aliases, fits 80 columns). No-arg overview and command-fallthrough (`help `) output byte-identical to before; 80-column layout preserved; verified on both generations, script and shell subcommands. `help env` without an initialised punk::config (e.g. script contexts) degrades to a one-line notice instead of an error stack. The registry is the intended seam for future subshell-declared topics (punk::config-gated — not yet a goal). ## [0.4.3] - 2026-07-08 - vendored tcludp upgraded 1.0.12 -> 1.0.13 in the tcl9 kit vfs folders (punk9win.vfs, punk9win_for_tkruntime.vfs) and src/vendorlib_tcl9 — 1.0.12's Windows per-thread exit handler closed the process-global tcludp synchronization events, so the first udp-loaded worker thread to die froze every other udp-loaded thread's event loop (root cause of the G-036 wedge; fixed upstream in 1.0.13). Verified with the G-036 regression harness: run-2 syslog workers alive (baseline on 1.0.12: wedged 4/4). Note: punk8win.vfs still bundles udp 1.0.12 (8.6 appeared immune; pending decision). The manual vfs copy was required because `libs`/`vfscommonupdate`/`project` do not propagate vendorlib_tcl platform libraries into kit vfs lib_tcl trees — gap recorded as goal G-037. ## [0.4.2] - 2026-07-08 - piped-stdin exit/quit hang fixed (shellthread 1.6.3, shellfilter 0.2.4): repl/app teardown no longer blocks forever when a shellthread worker's event loop has wedged — unsubscribe's settings-reset send is now async, shutdown_free_threads keeps its timeout armed across all waits, and shellfilter::run honors its -syslog option (default empty -> no runtag log worker thread) instead of a hardcoded 127.0.0.1:514 debug leftover. Syslog remains supported when explicitly configured. Diagnosis and residual root-cause work tracked as goal G-036. ## [0.4.1] - 2026-07-07 - `script` subcommand now matches `tclsh` for GUI scripts: if the script leaves a registered Tk main loop (Tk loaded with a live main window), the event loop is serviced until the last window closes, instead of sourcing-then-exiting immediately. So a Tk script with no explicit `exit` (or one that exits from an `after` callback, like `scriptlib/tktimer.tcl`) stays alive and runs — previously it flashed and died, and `shell` was needed as a workaround (relevant to G-020 GUI automation). Console scripts are unaffected; a script that errors exits at once (no hanging window). Verified on both generations. ## [0.4.0] - 2026-07-07 - `script` subcommand supports `lib:` scriptlib scripts (with or without `.tcl` extension), matching the `shell` subcommand's scheme: `punkexe script lib:hello`, or bare `punkexe lib:hello` via reclassification. The prefix always wins — a literal path beginning `lib:` (pathological; illegal on Windows filesystems) is reachable via `./lib:...`. Not-found errors list the searched locations. Only `.tcl` runs via the script subcommand. - punk::path 0.2.2: new `scriptlib_resolve` — the shared `lib:` resolution policy (kit-internal `app/scriptlib` first and not externally overridable, then scriptlib dirs relative to the executable), PUNKARGS-documented. app-punkshell still carries its inline copy of the policy (refactor onto the shared proc noted as todo). ## [0.3.1] - 2026-07-07 - `script` subcommand: core-command documentation parity with the interactive shell — app-punkscript now loads `punk::args::moduledoc::tclcore` (catch-guarded, ~40ms) so e.g. `'i list' | punkexe script` renders the doc table instead of "Undocumented command". Verified on both generations; kit startup ~0.33s. - punk::packagepreference 0.1.1: the moduledoc auto-load success notice moved from stdout to stderr — stdout must stay clean for script/exec contexts (the failure branch already used stderr). ## [0.3.0] - 2026-07-07 - ` script` completed (goal G-015 achieved): runs a script file (`script ?args?`, conventional ::argv0/::argv) or the whole of piped stdin (` | script` — no trailing `exit` needed) in the default punk shell module/alias environment, so one-liners like `dev projects.work *name*` work with no boilerplate. Honest exit codes (0 success, 1 error with errorInfo on stderr, script's own `exit` honoured); no shellfilter transforms or logging side effects; never falls into an interactive shell. Implemented as the lean `app-punkscript` package; the stdin form echoes the script's final result when non-empty. Verified on both Tcl generations (punk902z, punksys). ## [0.2.7] - 2026-07-07 - vendored tomlish 1.1.10: the library API now carries punk::args (PUNKARGS) documentation (from_toml, to_dict, from_dict, to_toml, update_tomlish_from_dict) — the G-014 tomlish precondition is met (documented upstream, re-vendored from a clean checkout); plus the explicit `tomlish::cmdline_main` entry point. - `_vfscommon.vfs` kit payload refreshed to the current built modules: shellfilter 0.2.3, shellrun 0.1.4, punkboot::utils 0.1.1, punk::console 0.7.1 content catch-up, tomlish 1.1.10. ## [0.2.6] - 2026-07-06 - shellfilter 0.2.3: `shellfilter::run` now guarantees tee-stack removal (stdout/stderr restoration) via try/finally even when its body errors, and the decorative stack-status table rendering in `log::critical` blocks is catch-guarded. Previously an error there (observed: a stale same-version textblock snapshot calling the new punk::ansi `sgr_merge_singles` with old option syntax) aborted the run between channel diversion and restoration, leaving the process's stdout/stderr broken and all subsequent output silently lost. - runtests.tcl: testinterp tm path order fixed so the bootsupport snapshot wins same-version module ties over vendormodules (verified rule on Tcl 9.0.3, standard and punk::libunknown scanners: `tcl::tm::add` prepends, head of `tcl::tm::list` wins ties, so the last `test_tmlist` element wins); testinterp auto_path gains the parent of `[info library]` so binary packages such as Thread resolve in projects without a root `lib_tcl/` payload. ## [0.2.5] - 2026-07-06 - `make.tcl vendorupdate` now checks each vendor source project's fossil/git checkout state and warns (non-fatal, once per VCS root) when pulling from a dirty tree — vendored artifacts built from uncommitted source have no committed provenance. Enforcement policy (abort/override, bootsupport coverage) tracked by goal G-026. - punkboot::utils 0.1.1: the dirty-checkout check lives in the module as `vcs_dirty_warnings` (make.tcl calls it via a guarded lazy require and degrades to skipped if the bootsupport snapshot lacks it); bootsupport snapshots refreshed (punkboot::utils 0.1.1, shellrun 0.1.4, punk::console 0.7.1 content catch-up). ## [0.2.4] - 2026-07-06 - shellrun 0.1.4: added missing `package require punk::args` (the 0.1.3 migration left `runx`/`run`/`runout`/`runerr` calling `punk::args::parse` without requiring it, breaking shellrun in bare interps — including the `src/tests/runtests.tcl` testinterp, which made every source-tree test run fail). ## [0.2.3] - 2026-07-06 - shellrun: removed `-tcl` from `sh_run`, `sh_runout`, `sh_runerr`, `sh_runx` (was silently swallowed with no useful effect because `sh -c` is always an external shell; now rejected with a usage message). - shellrun: migrated `runx` from the legacy `get_run_opts` parser to `punk::args::parse` with a full PUNKARGS spec; `runx` now honours `-debug` and `--timeout=` (previously accepted but silently ignored). - shellrun: expanded PUNKARGS documentation for `run`, `runconsole`, `runout`, `runerr`, and `runx` with `@cmd` metadata and `-help` text on all opts/values. - shellrun: migrated `sh_run`, `sh_runout`, `sh_runerr`, `sh_runx` from `get_run_opts` to `punk::args::parse` with dedicated PUNKARGS specs. - shellrun: removed the internal `get_run_opts` proc; all callers now use `punk::args`. ## [0.2.2] - 2026-07-06 - punk::console: terminal queries fired while an 8.6-based shell waits at an idle line-mode prompt now fail fast with a discriminable errorcode (`PUNK CONSOLE QUERY HOSTAGE_COOKED_READ`) and emit nothing, instead of timing out (~500ms) and later corrupting input with the swallowed response (idle-reader hostage guard; raw mode and mid-command queries unaffected). Owner-routed queries preserve the owner-side errorcode. ## [0.2.1] - 2026-07-06 - `make.tcl projectversion`: new advisory subcommand verifying `CHANGELOG.md` matches `punkproject.toml` and warning if `src/` has commits since the last project-version bump. - Clarified project versioning policy: the `make.tcl` command interface is part of the product surface. ## [0.2.0] - 2026-07-05 - Initial tracking of the punkshell project version in `punkproject.toml`.