You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 KiB
197 KiB
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.60.0] - 2026-08-08
punk::args-multiplegains occurrence-arity ranges (G-053):-multipleaccepts a{min max}range (max-1unbounded) alongside the legacy booleans, so a definition can declare at most once ({0 1}; a second occurrence is a parse error), bounded repetition ({2 4}), or one-or-more ({1 -1}) instead of choosing between silent last-wins (0) and unbounded collection (1). The spec compiler canonicalises once at resolve, keeping the stored-multipleboolean's hot-path meaning so legacy 0/1 stay byte-unchanged; range companions live in a separate per-formMULTIPLE_RANGESdict (not in ARG_INFO, so they surviveresolved_defspec round-trips). A newPUNKARGS VALIDATION occurrencecountfailure class fires per section (opts/leaders/values); over-max is a hard contradiction (invalid), under-min is pure exhaustion (incomplete, suppressed in the G-152 viability probe). The-optional/range-min reconciliation is enforced and documented at define time. The usage-table Multi column shows0-1/2-4/1+(greencheck stays for legacy 1), the string renderer emitsMULTI:0-1, and the synopsis distinguishes at-most-once (?arg?) from repeating (arg...).-multipleunique/-multipleuniquesetcompose unchanged.
[0.59.0] - 2026-08-08
punk::argsgains argument-relation vocabulary and parse-time enforcement (G-083): per-argument-conflicts <list>declares that named arguments (parsekeys or flag names) must not be received together with this argument, and@opts -parsekeymode override|error(per named group) makes a shared-parsekey group's distinct-member co-occurrence a parse-timeoptionconflict(override, the legacy last-wins default, or error). Both are cross-validated at definition resolve; a colliding-parsekeyis now a resolve error.optionconflictis a newPUNKARGS VALIDATIONfailure class, checked against received arguments only (defaults never conflict), over both the ordinary and mash (short-flag bundling) parse paths, and classified asinvalid(notincomplete). Usage/arg_error rendering surfaces the rules: aconflicts with: <targets>hint on each conflicted argument's help row and amutually exclusive (distinct members may not be combined)group header annotation for-parsekeymode errorgroups; the synopsis one-line form carries no conflict detail. Thelsearchtclcore moduledoc models its documented-sorted/-glob/-regexpand-bisect/-all/-notincompatibilities via per-arg-conflicts(the group staysoverrideso-glob/-regexpremain last-wins) and drops itspunk::args fixes requiredcaveat. Legacy behaviour is untouched by default (no-conflicts, defaultoverride, no colliding parsekey).
[0.58.0] - 2026-08-07
punk::pathgains machine-parsable return modes and a stable conflict verdict (G-173):-return text|dict|json(defaulttablebyte-unchanged) emit ANSI-free structured data;-context <namespace>(default the caller's namespace) makes the TCL-context conflict verdict stable for a machine consumer invoked through a wrapper;-conflicts 0skips conflict computation. The JSON form uses tcllibjson::writeand round-trips the dict form byte-for-byte.
[0.57.1] - 2026-08-07
- punk-runtime surfaces a recorded CPU instruction-set floor and gates launching a
runtime the local CPU cannot run (G-172). A runtime's artifact metadata toml
now carries
cpu_floor(the recipe's declared default floor) andcpu_model(the build's resolved CPU model);fetchreports them (ungated - a cross-platform fetch for another machine stays free),listannotates rows the host cannot run (!CPU-FLOOR:v<req>),use/runabort with a named diagnosis instead of a silent STATUS_ILLEGAL_INSTRUCTION (0xC000001D), andinfoshows the fields. The suite_tcl90 kit-family record writes the floor into each artifact's[provenance], and a new codegen audit toolscriptlib/developer/cpufloor_audit.tcldisassembles a binary and reports instructions above the declared floor (the acceptance instrument for the AVX-512 host-tuning regression). Floor-bearing published artifacts land at the next republication.
[0.57.0] - 2026-08-06
- Recognised-but-unavailable documentation landings are now surfaced distinctly
at the shell (G-166). On Tcl 8.6,
i string is dictandi string is truewere machine- and eye-indistinguishable: both reportedcmdtype doconlywith identical key sets, andi string is dict 5rendered exactly like a usable command line.punk::ns::cmdinfogains anunavailableresult key (empty, or the canonical-choiceunavailablename the resolution addressed) as a second axis besidecmdtype, andpunk::ns::cmdhelpmarks such subjects visibly in every return form - the dict form carries the key, table/string render the marking below the usage, and-return textleads with anUNAVAILABLE:line. i <cmd> <prefix>landing uniquely on an unavailable name now resolves that name's documentation (marked) instead of resolving nothing -i string is dicon 8.6 documents the dict class. Addressing documentation and word availability are separate axes; only the latter says the word is unusable.- The
string is <class>documentation for a class this runtime lacks now leads with the unavailability statement instead of ending with it.
[0.56.2] - 2026-08-06
make.tcl bake/bakehousenow end with aBAKE SUMMARYblock: a counts headline (deployed=N unchanged=N not-built=N failed=N, plus the number ofBAKE-WARNINGs recapped after it) and one line per kit underNOT BUILT/UNCHANGED/INSTALLED KITS/FAILED KITS, each with its reason. A full bake's per-kit output scrolls far out of reach, so the run previously ended with no answer to "what actually baked?" - the oldINSTALLED KITSandFAILED KITSblocks named only the kits the kit machinery touched and said nothing about the rest. The summary covers the whole configured matrix: kits skipped for an absent runtime,bake_default=false, a release-gated scheme output or a missing.vfsfolder never reach the kit machinery at all, and on a part-populated runtime store those are most of the rows. Rows are keyed by the kit's path relative tobin//src/_bake/so cross-target namesakes (kits/<platform>/<kit>- G-127) stay distinct; a selective bake summarises only the kits it was asked for. The block is stdout (contiguous under redirection) with failures echoed to stderr from the same ledger.make.tcl bake/bakehousenow exit 4 when the run completed with one or more FAILED kits. make.tcl's existing non-zero codes all mean "refused to do the work" (1 usage/gate refusal, 2 structural abort, 3 config error), so a run that DID the work and had kits fail exited 0 like a clean one - andpunk::mix::cli::make(dev make <subcommand>, the in-tree caller that branches on the exit code) reportedOK make finishedand returned true for a bake in which every kit failed. 4 keeps "could not run" distinguishable from "ran, produced failures". The not-built bucket deliberately never affects the exit code: an absent runtime,bake_default=falseor a release-gated output are normal states, and a part-populated runtime store is the common case.
[0.56.1] - 2026-08-06
make.tclproject-version bake gate: the kit-assembling subcommands (bake,bakehouse,bin) now treat the project version as the bake input it is (stamped into every kit'spunkkit-stamp.tomland used for versioned-scheme output names). A new-expect-projectversion <ver>flag asserts the version a bake is intended to ship - a mismatch withpunkproject.tomlaborts before any kit work. When a bump looks pending (src/ commits sincepunkproject.tomllast changed, none staged), an interactive run is prompted (y/N) and a non-interactive run aborts fast unless the version is asserted;-confirm 0deliberately does not waive the gate (agent runs pass it habitually - the gate interrupts habit with a checked claim). A staged-but-uncommitted bump passes (bake reads the working-tree value); degraded bootsupport reduces the gate to a NOTE, but a supplied assertion then aborts as unverifiable. Motivated by the 2026-08-06 bake-then-bump inversion, which stamped 0.55.1 into 11 kits and cost a full re-bake.make.tcl projectversionoverdue warning now states the consequence and prints the ready-made assertion command;make.tcl checkreports the gate state and current staleness verdict; the workflow text ([K2]/[K6], release-sequence steps (2)/(8)) and the help surfaces document the ordering and the flag. Root and srcAGENTS.md"Project Versioning" guidance updated to state the bake-input dependency and the bump-BEFORE-bake order.
[0.56.0] - 2026-08-06
- Coherent-set promotion + bake (the G-169 and G-170 follow-on promotion lines,
landed together): the launcher-family work of G-151, G-168, G-169 and G-170
reaches the kits. Promoted punk::args 0.18.0 and punk::ansi 0.2.0 into
bootsupport and
_vfscommon.vfs, plus punkboot core 0.7.0, punkexe moduledoc 0.9.0, punkboot moduledoc 0.3.1 and app-punkscript/app-punkscript-docs 1.3 into_vfscommon.vfs; 11 kits rebaked and deployed. User-visible in a deployed kit for the first time:<punkexe> help <sub> <formname|index>narrows a multi-form definition to that form's argument table, the multi-form synopsis lines carry actionablehelp <sub> %formname%labels instead of the suppressed column, any other tail on a parse-true subcommand is dry-run through its definition (accepted lines render the matched form plus the punk::args::parse_report landing report; rejected lines emit the parse diagnosis on stderr, exit 1), andhelp scriptrenders the app-owned definition that now lives beside the code implementing it. - The tclsh dry-run arm goes live with this bake: it requires the G-168 fence witness in the moduledoc actually served, so kits carrying the older 0.6.1 snapshot had kept the historic ignored-tail render. That mixed-vintage state is now resolved in the deployed kits.
[0.55.1] - 2026-08-05
- G-149 follow-on landed: make.tcl's multi-form definitions (tool, buildsuite) adopt '@cmd -formhint' - 'make.tcl help tool' / 'help buildsuite' now label each form's synopsis line with the actionable dry-run-help invocation plus a trailing ellipsis ('make.tcl help tool build ...' rendered with U+2026) instead of the repl-oriented ' i -form N ...' default, which is unreachable for make.tcl's (script) ids. The ellipsis honestly marks that help accepts the form's own command line after the action (the dry-run continuation). The labels also render on dispatch usage errors, where they are the discovery route to per-action help - the reason the column is configured rather than suppressed. Renders via the promoted bootsupport punk::args 0.17.0; degrades harmlessly (default labels) under older snapshots in generated projects. Layout make.tcl copies and the templates modpod synced via the established channels.
[0.55.0] - 2026-08-05
- G-149 achieved: punk::args configurable multi-form synopsis hint labels (punk::args 0.17.0, source module). The per-form hint labels rendered beside a multi-form definition's synopsis lines in usage/help tables (hard-coded " i -form N ..." previously) are now a template substituted per form: %formindex% is replaced with the form's 0-based ordinal, %formname% with its name, and an empty template suppresses the labels entirely. A definition supplies its own via the new '@cmd -formhint' directive key; callers of usage/arg_error may override with the new -formhint option. Unconfigured definitions render byte-identical to before. Motivation: script-level (script) definitions such as make.tcl's are unreachable from any repl, so the repl-oriented 'i' hint was not actionable there - make.tcl adoption follows a future bootsupport promotion. Kits pick this up at the next bootsupport/vfscommon refresh and bake.
[0.54.0] - 2026-08-05
- G-073 achieved: punk::args unavailable choices (punk::args 0.16.0, tclcore moduledoc 0.4.0, punk::ns 0.9.3, source modules). A definition can declare -choiceunavailable names - recognised but not available in the current runtime/context. They display among the choices under a dedicated "Unavailable (recognised, not selectable here)" group with their ordinary choicelabels notes, reserve prefixes (a prefix shared with an available choice is ambiguous), and are rejected with a tailored choiceunavailable error naming the entry and its note instead of the generic listed-values error; synopsis literals and value-in-effect marking never include them. The tclcore 'string is' model adopts the key via a curated forward-class list: on Tcl 8.6 the dict class now displays with its "(class not present in Tcl 8.6)" note, 'string is dict' is rejected informatively, 'string is di' is ambiguous (deliberately stricter than real 8.6, preparing users for 9.x - recorded as a sanctioned parity exemption), and 'i string is dict' documents the class via its per-class virtual id. The punk::ns doc walk honours unavailable names identically to parsing. Kits pick this up at the next bootsupport/vfscommon refresh and bake.
[0.53.1] - 2026-08-05
- G-082 follow-on landed (punk::args 0.15.2, source module): a REQUIRED typed value or leader whose supplied word fails the basic-type allocation screen (int/double/bool/number/dict) now reports the pointed typemismatch naming the word and argument (with -badarg/-badval) instead of the generic "Not enough remaining values" missingrequiredvalue/missingrequiredleader shortfall. Genuine shortfalls (end-of-input, literal-typed members, multi-member clause partials) report unchanged. parse_status for the affected shape now classifies invalid/typemismatch (the formstatus viability verdict was already invalid). Kits pick the fix up at the next bootsupport/vfscommon refresh and bake.
[0.53.0] - 2026-08-05
- Single help mechanism (user direction; boot core 0.5.0, moduledoc::punkexe
0.6.0, moduledoc::punkboot 0.3.0): all -help/--help flag interception is
withdrawn from the kit launcher and make.tcl - the leading
helpword is the one help entry (the punkshelli <cmd> ...idiom).<punkexe> help <subcommand> ?arg ...?now accepts and ignores trailing words, sohelpcan simply be prepended to an existing command line (previouslyhelp tclsh somefile someargconfusingly fell back to the top-level table). Help flags are ordinary arguments everywhere: a leading-helpearns the unknown-first-argument refusal (whose stderr usage answers it, exit 1),tclsh -helpregains full stock dash parity (the sole-argument carve-out is gone),buildinfo -helpis refused like any argument (exit 2), the experimental shellspy processor's own -help/--help handling is reachable again, andmake.tcl <subcommand> ... -helpfails whatever punk::args gate it meets (e.g.bakelist -help= the same choice-gate rejection asbakelist nonexistant) with the usage error itself carrying the documentation. launcherhelp.test and maketclhelp.test re-pinned to the revised contract.
[0.52.0] - 2026-08-04
- G-165 driver-invariant kit assembly: every make.tcl zip-kit bake now emits an ARCHIVE-relative kit regardless of the driving interpreter (legacy file-relative mkimg in 8.7/9.0.0, fixed mkimg in 9.0.1+, or no mkimg at all). Zip-kit assembly uses a single writer layer - punk::zip::mkzip (0.4.0), punkzip-accelerated when bin/punkzip resolves via the new write-path seam (mirroring the G-126 read-path accelerator), the pure-Tcl floor otherwise - with the runtime prefix concatenated outside the writer; tcl::zipfs::mkimg is retired from the pipeline. Four-driver acceptance on the reference machine (8.7a6, 9.0.3, native 8.6, msys2 8.6): identical member sets and CRCs, offsetstyle=archive, no BAKE-WARNINGs. File-relative kits remain available only as an explicit per-kit offsetstyle = "file" declaration in src/runtime/mapvfs.toml (the G-134 carve-out as configuration; the pin now warns only on undeclared file-relative output and exempts declared kits - specimen: [kit.punkfiledemo]).
- punkzip 2.4.0 re-vendored (G-165 enabling changes, upstream commit 2599127): build gains -b root-strip (mkzip -base semantics) and repeatable -x entry-name exclusion (Tcl string match syntax).
- make.tcl bake/bakehouse -force flag: re-assemble kits whose inputs are unchanged (bypasses the 'no change detected' skip).
- punkkit-stamp.toml is now written with pinned LF translation - the default text translation followed the driving interpreter (crlf vs lf), making the stamp's bytes driver-dependent (caught by the four-driver parity run).
[0.51.2] - 2026-08-04
- punk::tcltestrun 0.4.2 (G-161 follow-on, landed at user direction):
the closing-banner prefix match no longer treats the test NAME as a
glob pattern -
string match "$line_inner*"meant a name containing glob metacharacters (* ? backslash) never matched its own opening text, so the closing banner failed to close the failure section and every later event including the summary line was swallowed (the G-161 wedge class; latent, unexercised in the corpus). The check is now an exact byte prefix compare (string equal -length) - identical semantics for ordinary names. Pinned by new parsetestrun-globname-7.0/7.1 in the parser suite (8/8 green: tclsh90 both modes, tclsh86).
[0.51.1] - 2026-08-04
- punk::args 0.15.1 (G-164): the G-152 form-viability verdict's
valmin-reservation false negative is cured. When the reservation
seats a word that literally names a declared required option into a
required value slot at end-of-input, and the resulting optionmissing
stands in the viability probe (a value word consumed), the probe
re-parses with the stolen word(s) consumed as the options they name
and classes the form viable (incomplete) on pure end-of-input
exhaustion.
i hash -tnow marks the test form "(viable - needs more arguments)" (show_or_set/rehash/delete/help stay not viable) andi hash -dmarks delete viable - the 0.51.0 known issue is resolved. Runtime seating is unchanged: the trailing-flag-as-value rule is load-bearing and pinned unchanged, as is the runtime optionmissing ("missing flags") path; a stolen word naming a value-TAKING required option stays conservative (invalid). Options still missing after the re-seating are ordinary exhaustion in the re-probe. Verdict contract documentation updated in the parse/parse_status argdoc. Pins: formviability.test +7 tests (17/17), cmdhelp.test GAP pin cmdhelp_GAP_multiform_flagled_testform_false_notviable flipped to cmdhelp_multiform_flagled_testform_viable (45/45); args + ns subtrees green apart from the 2 pre-existing nsprimitives environment failures on this box.
[0.51.0] - 2026-08-04
i <command> ?word ...?form narrowing for flat multi-form commands (G-150): punk::auto_exec::hash's declaration is now selection-sound - its show_or_set name value carries-regexprefail {^-}, so a bare punk::args parse of a flag-led line selects the flag form uniquely instead of reporting an ambiguous match with show_or_set (which swallowed flag-like words as plain names).i hash -rnow renders only the rehash form's usage andi hash -d <name>only the delete form, driven by the declaration through cmdhelp's existing G-041 advisory-parse candidacy (no hash-handler involvement); no-word, ambiguous and no-match lines keep the whole-command render. hash's runtime behaviour is unchanged - a dash-led name now needs an explicit flag form at the declaration level too (accepted cost). cmdhelp's subcommand help documents the narrowing contract. Modules: auto_exec 0.1.1, ns 0.9.2 (documentation-only). Pins: 8 new tests in ns/testsuites/ns/cmdhelp.test (narrowing on -r/-d, bare-parse selection incl -t/plain/empty lines, the dash-led-name noformmatch cost, no-word and ambiguity whole-render fallbacks, unknown-flag noformmatch fallback, runtime-unchanged hash behaviour).- hash declaration/handler refinements (user direction, same batch):
the help form dropped
-anyoptsso--helpmust lead, and trailing words of any shape are consumed as ignored values - dash-led words included, via the new punk::args@values -flagvalues 1capability (punk::args 0.15.0, added for this: an unmatched dash-led word in option position seats as a trailing value instead of raising invalidoption; opt-in, off by default). A flag-led line (e.g.-t) no longer marks the help form viable via adhoc-option seating (the old "help (viable)" pointer fori hash -twas wrong). Known issue in the remaining verdict: the test form also reports "(not viable)" fori hash -t- a false negative, sincehash -t foois valid; the valmin reservation seats the lone-tas the required name value, after which the G-152 position guard lets the required-flag optionmissing stand. The intended refinement is an alternative- allocation re-probe in the G-152 probe layer (re-probe with the stolen word consumed as the option it literally names), NOT a runtime seating change - the trailing-flag-as-value rule is load-bearing and pinned unchanged. GAP-pinned in cmdhelp.test. Also the handler's multi-name-tmissing-name branch now prints "hash: : not found" (was an undefined-variable error "can't read hash"). Pins: args flagvalues.test (9) + cmdhelp.test (45/45 green).
[0.50.1] - 2026-08-04
- commandstack 0.7.1 (G-160 follow-on, landed at user direction): the reload contract - known_renamers and debug are now info-exists guarded like the rest of the module state (all_stacks, renamer_command_tokens, token_implementations), so a module re-source refreshes proc definitions only and no longer resets them while stacks/tokens survive. Previously the reset stranded live stacks: the removal forms gate on known_renamers membership, so the surviving records' renamers became unknown and removal errored. Contract documented in commandstack::help; pinned by commandstack_reload_preserves_state (suite 41/41 on tclsh90 9.0.3 and punk86 8.6; packagepreference consumer suites 6/6 on both). Minted as modules/commandstack-0.7.1.tm; bootsupport/vfscommon promotion left to the next promotion cycle (as with 0.6.0/0.7.0).
[0.50.0] - 2026-08-04
- punk executable -e one-liner support (G-077, achieved): ` -e