diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a8b1fb4..6a52a131 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,21 @@ The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.to Entries are newest-first; one bullet per notable change. See the root `AGENTS.md` "Project Versioning" section for the bump policy. +## [0.63.0] - 2026-08-08 + +- `punk::libunknown` 0.3.0 (G-176 increment 2): the `::package` override + (the `epoch` subcommand and controlled `forget`) now installs via + `commandstack::rename_command` instead of the former + `rename ::package ::package::` + namespace-import mechanism, so both live + `::package` overrides (libunknown's and punk::packagepreference's) share + one managed stack whatever the install order - stack removals and + third-party renames re-link correctly instead of operating on the wrong + command. Behaviour of every subcommand arm is unchanged (pinned by the + increment-1 characterisation suite; only its marked install-shape pins + flipped). `::package::` no longer exists (nothing in the tree called it); + libunknown's init now requires commandstack (a no-op when + packagepreference installed first, as in punkshell boot). + ## [0.62.2] - 2026-08-08 - `i ::tcl::prefix` no longer emits 12x "warning: update_definitions diff --git a/GOALS.md b/GOALS.md index b25160ba..52ec5acf 100644 --- a/GOALS.md +++ b/GOALS.md @@ -405,6 +405,6 @@ Scope: src/modules/punk/args-999999.0a1.0.tm (resolve @dynamic cache-skip, by-id Detail: goals/G-175-punkargs-dynamic-resolve-multiplicity.md ### G-176 [active] commandstack doc integration - stack-tied overrider argdocs, override hints, and the libunknown package-override migration -Scope: src/modules/commandstack-999999.0a1.0.tm (rename_command -punkargs option, stackdocs pending-docs namespace, remove_rename detach hook, help/argdoc how-to); src/modules/punk/libunknown-0.2.4.tm (package-override migration to commandstack - fixed-version file, renamed on bump); src/modules/punk/ns-999999.0a1.0.tm (cmdhelp/cmdinfo override-in-place hint); src/tests/modules/punk/libunknown/testsuites/discovery/ (override characterisation arms - discovery.test extensions or a sibling packageoverride.test); src/tests/modules/commandstack/testsuites/commandstack/commandstack.test (-punkargs lifecycle pins); src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test (override-hint pins) +Scope: src/modules/commandstack-999999.0a1.0.tm (rename_command -punkargs option, stackdocs pending-docs namespace, remove_rename detach hook, help/argdoc how-to); src/modules/punk/libunknown-0.3.0.tm (package-override migration to commandstack - fixed-version file, renamed from libunknown-0.2.4.tm at increment 2); src/modules/punk/ns-999999.0a1.0.tm (cmdhelp/cmdinfo override-in-place hint); src/tests/modules/punk/libunknown/testsuites/discovery/ (override characterisation arms - discovery.test extensions or a sibling packageoverride.test); src/tests/modules/commandstack/testsuites/commandstack/commandstack.test (-punkargs lifecycle pins); src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test (override-hint pins) Detail: goals/G-176-commandstack-doc-integration.md diff --git a/goals/G-176-commandstack-doc-integration.md b/goals/G-176-commandstack-doc-integration.md index cb95c221..c495233e 100644 --- a/goals/G-176-commandstack-doc-integration.md +++ b/goals/G-176-commandstack-doc-integration.md @@ -1,7 +1,7 @@ # G-176 commandstack doc integration - stack-tied overrider argdocs, override hints, and the libunknown package-override migration Status: active -Scope: src/modules/commandstack-999999.0a1.0.tm (rename_command -punkargs option, stackdocs pending-docs namespace, remove_rename detach hook, help/argdoc how-to); src/modules/punk/libunknown-0.2.4.tm (package-override migration to commandstack - fixed-version file, renamed on bump); src/modules/punk/ns-999999.0a1.0.tm (cmdhelp/cmdinfo override-in-place hint); src/tests/modules/punk/libunknown/testsuites/discovery/ (override characterisation arms - discovery.test extensions or a sibling packageoverride.test); src/tests/modules/commandstack/testsuites/commandstack/commandstack.test (-punkargs lifecycle pins); src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test (override-hint pins) +Scope: src/modules/commandstack-999999.0a1.0.tm (rename_command -punkargs option, stackdocs pending-docs namespace, remove_rename detach hook, help/argdoc how-to); src/modules/punk/libunknown-0.3.0.tm (package-override migration to commandstack - fixed-version file, renamed from libunknown-0.2.4.tm at increment 2); src/modules/punk/ns-999999.0a1.0.tm (cmdhelp/cmdinfo override-in-place hint); src/tests/modules/punk/libunknown/testsuites/discovery/ (override characterisation arms - discovery.test extensions or a sibling packageoverride.test); src/tests/modules/commandstack/testsuites/commandstack/commandstack.test (-punkargs lifecycle pins); src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test (override-hint pins) Goal: a command overrider using commandstack can attach punk::args doc blocks that live and die with its stack entry (supplied at rename_command time, active whether or not punk::args is loaded yet, removed by every removal path), cmdhelp renders an override-in-place hint on any commandstack-managed command's existing documentation (naming the renamers and any doc ids their records added), and the punk::libunknown ::package override - the flagship consumer, whose 'package epoch' subcommand gains real documentation this way - is migrated onto commandstack so both live ::package overrides share one managed stack. Acceptance: characterisation tests of the CURRENT libunknown override land before any behaviour change and stay green byte-identically through the migration (the epoch tm/pkg single-arg forms, indexed ?keys...? queries and their error arms, the forget arm incl the tcl/Tcl/tcl::oo/tk denial and epoch-scripts pruning, abbreviation acceptance (ep/epo/epoc, fo/for/forge), a delegation pass-through pin, the re-init guard, and a packagepreference interop child covering both install orders) with only explicitly marked install-shape pins (::package import link, ::package:: parked name) flipping when the migration lands; libunknown installs its ::package override via commandstack::rename_command with a stack-query install guard mirroring punk::packagepreference and no ::package:: residue; commandstack::rename_command accepts a -punkargs list of definitionlists whose docs are queryable while the record lives - including renames performed BEFORE punk::args loads (pinned in a bare child that loads punk::args afterwards) - and are removed by remove_rename, pop_rename, remove_renamer and restore_original alike (record key appended per the key-order contract, reload-contract guard on new state); punk::ns::cmdhelp shows an override-in-place notice for a commandstack-managed command (cmdinfo carrying it as a second-axis key in the G-166 (achieved, see goals/archive/G-166-cmdinfo-unavailable-surfacing.md) style) without a hard commandstack dependency, enumerating record-attached doc ids; 'i package epoch' resolves the libunknown-attached doc in a punk shell and the mechanism how-to is described in commandstack::help and the rename_command argdoc; punk/libunknown, commandstack, punk/ns and punk/packagepreference suites pass under the canonical runtests interpreter. @@ -127,6 +127,46 @@ Increments land as separate commits, each leaving all suites green: source the source-tree libunknown/commandstack/packagepreference by path via a shared locator (discovery.test pattern). Tests AGENTS.md index updated. +- Increment 2 (migration) landed 2026-08-08: punk::libunknown 0.3.0 (file renamed + from libunknown-0.2.4.tm - git mv + fossil mv). The ::package override installs + via commandstack::rename_command (renamer punk::libunknown): init does + 'package require commandstack' (a no-op when already provided - resolvable via + tm paths in every real init context; bootsupport and the common vfs both carry + commandstack-0.7.1) and re-entry is guarded by a stack query for our record + (same 'already done' notice). Body deltas exactly as enumerated in Context: + four epoch_incr_* calls qualified, the dead 'variable has_package_files' + declaration dropped, the two delegation sites moved to COMMANDSTACKNEXT, the + rename+import install replaced, the init argdoc reworded. All increment-1 + BEHAVIOUR pins stayed green unchanged; only the marked install-shape pins + flipped (origin ::package, no ::package:: residue, managed stack records - 1 + solo, 2 in both interop orders). discovery.test and packageoverride.test + children now pre-source the source-tree commandstack by path (their tm paths + are cleared); libsearch/shadowing children resolve commandstack via inherited + parent paths and needed no changes. Suites: libunknown tree + loadedlib 42/42; + full modules tree green. Project 0.63.0. + +## Follow-ons + +- Boot-site commandstack pre-sourcing (out of the current Scope - a Scope + amendment is needed if pursued): punkboot core.tcl, make.tcl, punk::repl's two + blocks and punk::lib's interp_sync child-seeding all pre-source LIBUNKNOWN by + path expressly to avoid triggering the old package unknown pre-init. init's + new 'package require commandstack' resolves in all of them, but when + commandstack is not yet provided it performs one ordinary unknown-path + resolution pre-init (a root-depth sibling scan lands in 'untracked' - + functional, marginally looser epoch bookkeeping; in punkshell app boot + packagepreference::install provides commandstack first so the require is a + no-op there). Extending the same glob+source technique to commandstack-*.tm at + those sites would preserve the minimal-untracked intent exactly. => open +- make.tcl guard modernization (out of the current Scope): the + 'info commands ::punk::libunknown::package' check that skips re-load when + make.tcl is driven by a built punk shell tests a proc the 0.3.0 install no + longer creates. Old kits (0.2.4) still satisfy it so nothing breaks today, + but once kits carry 0.3.0 the check never fires and make.tcl re-sources + libunknown into the driving shell (harmless - init's guard no-ops - but noisy + and wasteful). Switch it to a "[package provide punk::libunknown] ne {}" + check before or with the bootsupport/kit promotion of 0.3.0. => open + ## Notes - Same-id shadowing: one live record per doc id is the documented constraint diff --git a/punkproject.toml b/punkproject.toml index 8468b0c4..af4ef282 100644 --- a/punkproject.toml +++ b/punkproject.toml @@ -1,6 +1,6 @@ [project] name = "punkshell" -version = "0.62.2" +version = "0.63.0" license = "BSD-2-Clause" url = "https://www.gitea1.intx.com.au/jn/punkshell" #packager: declared identity for published artifacts (declarative, not proof - diff --git a/src/modules/punk/libunknown-0.2.4.tm b/src/modules/punk/libunknown-0.3.0.tm similarity index 96% rename from src/modules/punk/libunknown-0.2.4.tm rename to src/modules/punk/libunknown-0.3.0.tm index 9debffb8..e554d5ad 100644 --- a/src/modules/punk/libunknown-0.2.4.tm +++ b/src/modules/punk/libunknown-0.3.0.tm @@ -7,7 +7,7 @@ # (C) 2025 # # @@ Meta Begin -# Application punk::libunknown 0.2.4 +# Application punk::libunknown 0.3.0 # Meta platform tcl # Meta license MIT # @@ Meta End @@ -18,6 +18,23 @@ # from src/modules/AGENTS.md "Versioning And Releases" - bumping means # renaming the file AND updating the Meta line above, the manpage_begin line # below and the provide-block version, then appending a line here): +#0.3.0 - G-176 increment 2: the ::package override installs via +# commandstack::rename_command (renamer punk::libunknown) instead of the +# former 'rename ::package ::package::' + namespace-import mechanism, so +# both live ::package overrides (this + punk::packagepreference's) share +# one managed stack whatever the install order. init now does +# 'package require commandstack' (a no-op where commandstack is already +# provided, e.g whenever punk::packagepreference::install ran first) and +# re-entry is guarded by a stack query for our renamer record instead of +# the old ::punk::libunknown::package proc-existence check. The override +# body runs at :: (namespace-relative epoch_incr_* calls fully +# qualified; the dead 'variable has_package_files' declaration dropped - +# its only use was already commented out) and delegates via +# COMMANDSTACKNEXT (no ::package:: parking name; nothing in the tree +# called it). Behaviour of every arm is unchanged - pinned by +# tests/modules/punk/libunknown testsuites/discovery/packageoverride.test +# (the increment-1 characterisation; only its marked install-shape pins +# flipped with this change). #0.2.4 - register_all_tm: skip the G-155 workdirs _mint and _bake alongside # legacy _build, in both the filesystem directory walk and the tm-file # filter (the latter also guards the zipfs whole-tree listing) - loose @@ -71,7 +88,7 @@ # doctools header # ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ #*** !doctools -#[manpage_begin punkshell_module_punk::libunknown 0 0.2.4] +#[manpage_begin punkshell_module_punk::libunknown 0 0.3.0] #[copyright "2025"] #[titledesc {Module API}] [comment {-- Name section and table of contents description --}] #[moddesc {-}] [comment {-- Description at end of page heading --}] @@ -1509,10 +1526,12 @@ tcl::namespace::eval ::punk::libunknown { -summary\ "Install punk::libunknown's custom package unknown handlers and epoch system in the current interp."\ -help\ - "Renames the builtin ::package to ::package:: and installs a custom ::package proc - that intercepts 'forget' (controlled forgetting) and adds an 'epoch' subcommand - (for introspection). Sets package unknown to a two-handler chain: - zipfs_tm_UnknownHandler (tm paths) and zipfs_tclPkgUnknown (auto_path/pkgIndex). + "Installs a ::package override via commandstack::rename_command (renamer + punk::libunknown) that intercepts 'forget' (controlled forgetting) and adds an + 'epoch' subcommand (for introspection); all other subcommands delegate to the + previous implementation on the commandstack. Sets package unknown to a + two-handler chain: zipfs_tm_UnknownHandler (tm paths) and zipfs_tclPkgUnknown + (auto_path/pkgIndex). Installs traces on ::auto_path and ::tcl::tm::paths that increment the epoch when these change, invalidating stale scan cache entries. @@ -1571,14 +1590,18 @@ tcl::namespace::eval ::punk::libunknown { puts stderr "punk::libunknown::init - init while empty/unreadable tcl::tm::list and empty/unreadable ::auto_path" } - if {[info commands ::punk::libunknown::package] ne ""} { - puts stderr "punk::libunknown::init already done - unnecessary call? info frame -1: [info frame -1]" - return + #commandstack is the install mechanism for the ::package override (G-176). + #A no-op when already provided (e.g whenever punk::packagepreference::install + #ran first, or a boot context pre-sourced it); otherwise resolves via the + #standard package machinery - commandstack is lean and dependency-free. + package require commandstack + #re-entry guard: our override record on the ::package commandstack + foreach stackrecord [commandstack::get_stack ::package] { + if {[dict get $stackrecord renamer] eq "punk::libunknown"} { + puts stderr "punk::libunknown::init already done - unnecessary call? info frame -1: [info frame -1]" + return + } } - #if {[namespace origin ::package] eq "::punk::libunknown::package"} { - # #This is far from conclusive - there may be other renamers (e.g commandstack) - # return - #} @@ -1686,13 +1709,14 @@ tcl::namespace::eval ::punk::libunknown { trace add variable ::auto_path write ::punk::libunknown::epoch_incr_pkg trace add variable ::tcl::tm::paths write ::punk::libunknown::epoch_incr_tm - #set stackrecord [commandstack::rename_command -renamer punk::libunknown package {args} { - # #::package override installed by punk::libunknown::init - #} - proc package args { + #::package override - installed via commandstack (G-176) so both live + #::package overrides (this + punk::packagepreference's) share one managed + #stack whatever the install order. The body runs at :: - namespace-relative + #references must be fully qualified. + set stackrecord [commandstack::rename_command -renamer punk::libunknown package {args} { + #::package override installed by punk::libunknown::init (epoch/forget arms; all else delegates) switch -- [lindex $args 0] { fo - for - forge - forget { - variable has_package_files #experimental - silently disallow forgetting things that didn't involve sourcing files #What about static libs that also sourced files? #packages loaded by c extensions? @@ -1731,7 +1755,7 @@ tcl::namespace::eval ::punk::libunknown { } } if {[llength $ok_forgets]} { - return [uplevel 1 [list ::package:: forget {*}$ok_forgets]] + return [uplevel 1 [list $COMMANDSTACKNEXT forget {*}$ok_forgets]] } else { return } @@ -1756,8 +1780,8 @@ tcl::namespace::eval ::punk::libunknown { return [dict create $cur [dict get $epoch pkg epochs $cur]] } incr { - epoch_incr_pkg - epoch_incr_tm + ::punk::libunknown::epoch_incr_pkg + ::punk::libunknown::epoch_incr_tm } default { error "package epoch [lindex $epoch_args 0] unsupported - known options: tm pkg incr" @@ -1768,10 +1792,10 @@ tcl::namespace::eval ::punk::libunknown { set a2 [list [lindex $epoch_args 0] [lindex $epoch_args 1]] switch -- $a2 { {pkg incr} - {incr pkg} { - epoch_incr_pkg + ::punk::libunknown::epoch_incr_pkg } {tm incr} - {incr tm} { - epoch_incr_tm + ::punk::libunknown::epoch_incr_tm } default { set which [lindex $epoch_args 0] @@ -1813,13 +1837,10 @@ tcl::namespace::eval ::punk::libunknown { } } default { - return [uplevel 1 [list ::package:: {*}$args]] + return [uplevel 1 [list $COMMANDSTACKNEXT {*}$args]] } } - } - rename ::package ::package:: - #all lowercase procs already exported from ::punk::libunknown - namespace eval :: [list ::namespace import ::punk::libunknown::package] + }] #if {[info commands ::tcl::zipfs::root] ne ""} { # set has_zipfs_tm 0 @@ -2183,7 +2204,7 @@ namespace eval ::punk::args::register { package provide punk::libunknown [tcl::namespace::eval ::punk::libunknown { variable pkg punk::libunknown variable version - set version 0.2.4 + set version 0.3.0 }] return diff --git a/src/tests/modules/AGENTS.md b/src/tests/modules/AGENTS.md index f593099f..db2137bb 100644 --- a/src/tests/modules/AGENTS.md +++ b/src/tests/modules/AGENTS.md @@ -51,5 +51,5 @@ Unit tests for editable source modules under `src/modules/`, `src/modules_tcl8/` - `punk/lib/` — punk::lib tests (`testsuites/lib/`): range/index/parse/compat/interp_sync utilities, G-058 static-baseline seeding (`staticseed.test`: interp_sync_package_paths/snapshot_package_paths propagate a simulated ::punkboot static baseline and seed `load {} ` ifneeded mappings; no-op without a baseline), and the repl command-completeness engine (`commandcomplete.test`: punk::lib::system::incomplete pending-opener stacks - the info-complete quoting quirk progression (`set x "{*}{"` standalone vs in-proc-body), single openers, tabs, escapes, incomplete<->info-complete parity property; pre-repl-refactor characterization, see goals/G-044 detail preserve-list) - `punk/packagepreference/` — punk::packagepreference tests (`testsuites/packagepreference/`): G-058 static-vs-bundled policy (`staticpolicy.test`: require of a baseline package triggers the index scan before resolution so a newer bundled copy wins, static beats older bundled, exact requires of bundled versions stay reachable, missing static mappings get seeded), and the install re-install guard (`installguard.test`, G-160: second install returns 0 silently via the record `renamer` key - the historical `rename` read threw; uninstall/re-install cycle gets the next unique tokenid; fresh child interps source the SOURCE-TREE commandstack + packagepreference by path so no bootsupport/kit snapshot can answer) - `punk/zip/` — punk::zip tests (`testsuites/zip/zipreader.test`, G-124 - the module's first tests): the dependency-free reader over the three archive shapes (bare zip, executable-prefixed with archive-relative offsets, executable-prefixed with file-relative offsets) - archive_info's offsetstyle/dataoffset derivation incl. the nozip verdict for a plain binary carrying a stray PK\5\6, members' per-entry introspection (classification, sizes, method, crc, mtime, stored attributes) and glob/exclude selection, unzip's byte-identical CRC-verified extraction incl. the >2MB streaming path and partial extraction; the named refusals (encrypted, unknown compression method, zip64, crc mismatch, path escape) each asserting that no partial output is left behind; and the mkzip->read round trip - the first coverage that what punkshell WRITES is readable. Runs entirely on stock Tcl (verified on msys2's tclsh8.6 with no zipfs, no vfs::zip and no tcllib); two zipfs-gated tests cross-check against a real zipfs mount and record the answer to the standing directory-classification question (zipfs keys on the trailing slash, not the stored permission bits - so punk::zip::mkzip directories mount as directories). The real-runtime pin self-gates on `bin/runtime/win32-x86_64/tclsh90b4_piperepl.exe`, which is untracked -- `punk/libunknown/` — .tm same-version shadowing pin-tests (`testsuites/shadowing/`): tcl::tm::add prepend rule, head-of-tm-list wins exact-version ties, version beats order, punk::libunknown parity — shipped behaviour depends on these (runtests tm ordering, punk_main package-mode precedence, G-033); mixed .tm/pkgIndex.tcl characterization is goal G-035; discovery/epoch-cache characterization (`testsuites/discovery/discovery.test`): sibling .tm registration happens only at the requested namespace depth (deeper modules invisible to 'package names' until requested), register_all_tm deep discovery (all-depths registration, per-epoch cached no-op, head-of-tm-list precedence parity), 'package epoch' command shape, trace-driven epoch increments on tm/auto_path changes, the epoch-index short-circuit (a .tm added to an already-scanned dir needs 'package epoch incr'), and the pkgIndex.tcl sourcing-scope contract (source_pkgindex, fixed 2026-07-11 - formerly a GAP pin of the global-'dir' clobber): user global 'dir' survives pkg-unknown fallthrough, index scripts see $dir and reach the real ::auto_path (tcllib extension pattern), and their stray unqualified sets no longer leak to ::; plus ::package-override characterisation (`testsuites/discovery/packageoverride.test`, G-176 increment 1, 2026-08-08 - the pre-migration safety net for the commandstack migration: epoch single-arg/indexed query forms and their error arms, the filtered forget arm incl the tcl/Tcl/tcl::oo/tk denial and the no-ifneeded filter, abbreviation acceptance (ep/fo prefixes the parked builtin rejects - a deliberate override delta), delegation pass-through, the re-init no-op guard, and MARKED-TO-FLIP install-shape pins (::package namespace-import origin, ::package:: parked implementation, commandstack::get_stack seeing only the packagepreference record in the two-override interop children covering BOTH real-world install orders - punkshell's pkgpref-first and make.tcl's libunknown-first) that flip only with the G-176 increment-2 migration commit). Suite conventions learned the hard way: child probes source the SOURCE-TREE libunknown directly by path — 'package require punk::libunknown' tie-breaks the same-version copies (src/modules vs bootsupport) by tm path order, which favours bootsupport in the testinterp and machine env paths in children; and probe scripts must avoid global variable names the handlers use (notably 'dir') — that clobbering misdirected a fixture write into the real Tcl install during test development +- `punk/libunknown/` — .tm same-version shadowing pin-tests (`testsuites/shadowing/`): tcl::tm::add prepend rule, head-of-tm-list wins exact-version ties, version beats order, punk::libunknown parity — shipped behaviour depends on these (runtests tm ordering, punk_main package-mode precedence, G-033); mixed .tm/pkgIndex.tcl characterization is goal G-035; discovery/epoch-cache characterization (`testsuites/discovery/discovery.test`): sibling .tm registration happens only at the requested namespace depth (deeper modules invisible to 'package names' until requested), register_all_tm deep discovery (all-depths registration, per-epoch cached no-op, head-of-tm-list precedence parity), 'package epoch' command shape, trace-driven epoch increments on tm/auto_path changes, the epoch-index short-circuit (a .tm added to an already-scanned dir needs 'package epoch incr'), and the pkgIndex.tcl sourcing-scope contract (source_pkgindex, fixed 2026-07-11 - formerly a GAP pin of the global-'dir' clobber): user global 'dir' survives pkg-unknown fallthrough, index scripts see $dir and reach the real ::auto_path (tcllib extension pattern), and their stray unqualified sets no longer leak to ::; plus ::package-override characterisation (`testsuites/discovery/packageoverride.test`, G-176 increment 1, 2026-08-08 - the pre-migration safety net for the commandstack migration: epoch single-arg/indexed query forms and their error arms, the filtered forget arm incl the tcl/Tcl/tcl::oo/tk denial and the no-ifneeded filter, abbreviation acceptance (ep/fo prefixes the parked builtin rejects - a deliberate override delta), delegation pass-through, the re-init no-op guard, and MARKED-TO-FLIP install-shape pins (::package namespace-import origin, ::package:: parked implementation, commandstack::get_stack seeing only the packagepreference record in the two-override interop children covering BOTH real-world install orders - punkshell's pkgpref-first and make.tcl's libunknown-first) - FLIPPED 2026-08-08 with the landed increment-2 migration as marked: they now pin the commandstack-installed shape, real proc at ::package, no ::package:: residue, both overrides as managed stack records (count 2 in both interop orders)). Suite conventions learned the hard way: libunknown 0.3.0 installs its ::package override via commandstack, so cleared-tm-path child probes must pre-source the SOURCE-TREE commandstack by path before libunknown init (children inheriting parent paths resolve it via require); child probes source the SOURCE-TREE libunknown directly by path — 'package require punk::libunknown' tie-breaks the same-version copies (src/modules vs bootsupport) by tm path order, which favours bootsupport in the testinterp and machine env paths in children; and probe scripts must avoid global variable names the handlers use (notably 'dir') — that clobbering misdirected a fixture write into the real Tcl install during test development diff --git a/src/tests/modules/punk/libunknown/testsuites/discovery/discovery.test b/src/tests/modules/punk/libunknown/testsuites/discovery/discovery.test index d5ec1732..7aac63da 100644 --- a/src/tests/modules/punk/libunknown/testsuites/discovery/discovery.test +++ b/src/tests/modules/punk/libunknown/testsuites/discovery/discovery.test @@ -145,22 +145,35 @@ namespace eval ::testspace { set libunknown_ver $thisver } } - testConstraint libunknownavailable [expr {$libunknown_src ne ""}] - if {$libunknown_src eq ""} { - puts stderr "discovery.test: cannot locate src/modules/punk/libunknown-*.tm relative to [info script] (tests will be skipped)" + #libunknown 0.3.0 installs its ::package override via commandstack (G-176) - + #init requires it, and the children's tm paths are cleared, so the source-tree + #commandstack is sourced by path first (same highest-version rule) + variable commandstack_src "" + foreach candidate [glob -nocomplain [file join $srcmodules commandstack-*.tm]] { + set thisver [file rootname [lindex [split [file tail $candidate] -] 1]] + if {$commandstack_src eq "" || [package vcompare $thisver $commandstack_ver] == 1} { + set commandstack_src $candidate + set commandstack_ver $thisver + } + } + testConstraint libunknownavailable [expr {$libunknown_src ne "" && $commandstack_src ne ""}] + if {$libunknown_src eq "" || $commandstack_src eq ""} { + puts stderr "discovery.test: cannot locate src/modules/punk/libunknown-*.tm and/or src/modules/commandstack-*.tm relative to [info script] (tests will be skipped)" } # ------------------------------------------------------------------------- - # Probe: fresh child interp with the source-tree punk::libunknown active and - # tm paths set to exactly $tmdirs. Returns the script's result; child - # deleted after. + # Probe: fresh child interp with the source-tree commandstack + punk::libunknown + # active and tm paths set to exactly $tmdirs. Returns the script's result; + # child deleted after. # ------------------------------------------------------------------------- proc libu_probe {tmdirs script} { variable libunknown_src + variable commandstack_src set i [interp create] try { interp eval $i {package prefer latest} interp eval $i {tcl::tm::remove {*}[tcl::tm::list]} + interp eval $i [list source $commandstack_src] interp eval $i [list source $libunknown_src] interp eval $i {punk::libunknown::init} interp eval $i [list tcl::tm::add {*}$tmdirs] diff --git a/src/tests/modules/punk/libunknown/testsuites/discovery/packageoverride.test b/src/tests/modules/punk/libunknown/testsuites/discovery/packageoverride.test index 0d95a58f..b38879eb 100644 --- a/src/tests/modules/punk/libunknown/testsuites/discovery/packageoverride.test +++ b/src/tests/modules/punk/libunknown/testsuites/discovery/packageoverride.test @@ -5,12 +5,14 @@ # onto commandstack::rename_command). See goals/G-176-commandstack-doc-integration.md. # # Pin classes: -# - BEHAVIOUR pins: must stay green byte-identically through the migration. -# - MARKED-TO-FLIP pins: assert the CURRENT install mechanism (::package is a -# namespace import of ::punk::libunknown::package; the previous -# implementation is parked at ::package::; commandstack sees only the -# packagepreference record when both overrides are live). These flip in the -# G-176 increment-2 commit and in no other. +# - BEHAVIOUR pins: stayed green byte-identically through the migration. +# - MARKED-TO-FLIP pins: originally asserted the pre-migration install +# mechanism (::package as a namespace import of ::punk::libunknown::package; +# previous implementation parked at ::package::; commandstack seeing only +# the packagepreference record when both overrides were live). FLIPPED +# 2026-08-08 with the G-176 increment-2 migration commit as planned: they +# now assert the commandstack-installed shape (real proc at ::package, no +# ::package:: residue, both overrides as managed stack records). # # Children are fresh interps sourcing the SOURCE-TREE modules by path (same # rationale and locator pattern as discovery.test in this directory). @@ -43,23 +45,28 @@ namespace eval ::testspace { variable commandstack_src [locate_srcmodule {commandstack-*.tm}] variable pkgpref_src [locate_srcmodule {punk packagepreference-*.tm}] - testConstraint pko_sources [expr {$libunknown_src ne ""}] + #libunknown 0.3.0 init requires commandstack (the override's install mechanism) - + #children have cleared tm paths, so commandstack is sourced by path first + testConstraint pko_sources [expr {$libunknown_src ne "" && $commandstack_src ne ""}] testConstraint pko_interopsources [expr {$libunknown_src ne "" && $commandstack_src ne "" && $pkgpref_src ne ""}] - if {$libunknown_src eq ""} { - puts stderr "packageoverride.test: cannot locate src/modules/punk/libunknown-*.tm relative to [info script] (tests will be skipped)" + if {$libunknown_src eq "" || $commandstack_src eq ""} { + puts stderr "packageoverride.test: cannot locate src/modules/punk/libunknown-*.tm and/or src/modules/commandstack-*.tm relative to [info script] (tests will be skipped)" } # ------------------------------------------------------------------------- - # Probe: fresh child interp with the source-tree punk::libunknown sourced and - # init run (override installed). No tm paths are added - the epoch state stays - # at its init values, so epoch pins are deterministic. Child deleted after. + # Probe: fresh child interp with the source-tree commandstack + punk::libunknown + # sourced and init run (override installed). No tm paths are added - the epoch + # state stays at its init values, so epoch pins are deterministic. Child + # deleted after. # ------------------------------------------------------------------------- proc pko_probe {script} { variable libunknown_src + variable commandstack_src set i [interp create] try { interp eval $i {package prefer latest} interp eval $i {tcl::tm::remove {*}[tcl::tm::list]} + interp eval $i [list source $commandstack_src] interp eval $i [list source $libunknown_src] interp eval $i {punk::libunknown::init} interp eval $i $script @@ -91,9 +98,11 @@ namespace eval ::testspace { interp eval $i [list source $libunknown_src] interp eval $i {punk::libunknown::init} } else { + #commandstack sourced first in this order too (libunknown init needs + #it) - the OVERRIDE install order is what the order argument names + interp eval $i [list source $commandstack_src] interp eval $i [list source $libunknown_src] interp eval $i {punk::libunknown::init} - interp eval $i [list source $commandstack_src] interp eval $i [list source $pkgpref_src] interp eval $i {punk::packagepreference::install} } @@ -207,26 +216,28 @@ namespace eval ::testspace { } -result {-1 1 2.5 1} # -- install shape ------------------------------------------------------------ - #MARKED-TO-FLIP (G-176 increment 2): these pin the CURRENT rename+import install - #mechanism - ::package is a namespace import of ::punk::libunknown::package and - #the previous implementation is parked at ::package::. The migration commit - #flips them to the commandstack-installed shape (real proc at ::package, no - #::package:: residue) and nothing else may. + #FLIPPED 2026-08-08 (agent, G-176 increment 2) as marked: previously pinned the + #rename+import mechanism (::package an import of ::punk::libunknown::package, + #previous implementation parked at ::package::). Now pins the commandstack + #install: real proc at ::package, no ::package:: residue, the override present + #as a managed stack record for renamer punk::libunknown. - test override_install_shape {current mechanism: ::package is an import of punk::libunknown::package and ::package:: holds the working previous implementation}\ + test override_install_shape {commandstack mechanism: real proc at ::package, no ::package:: residue, the override a managed stack record}\ -constraints pko_sources -body { pko_probe { + set st [commandstack::get_stack ::package] list [namespace origin ::package]\ [llength [info commands ::package::]]\ - [expr {[::package:: provide Tcl] ne ""}] + [llength $st]\ + [dict get [lindex $st 0] renamer] } - } -result {::punk::libunknown::package 1 1} + } -result {::package 0 1 punk::libunknown} # -- re-init guard ------------------------------------------------------------ #The no-op contract (second init returns empty and leaves the install alone) is - #BEHAVIOUR and survives the migration. The message text and the import-shaped - #origin element are install-mechanism detail - MARKED-TO-FLIP with increment 2 - #if the migrated guard reports differently. + #BEHAVIOUR and survived the migration; the guard is now a commandstack stack + #query for the punk::libunknown record but emits the same 'already done' + #notice. Origin element FLIPPED 2026-08-08 with increment 2 as marked. test override_reinit_guard {a second init call is a no-op: empty return, an 'already done' stderr notice, install unchanged}\ -constraints pko_sources -body { @@ -247,18 +258,17 @@ namespace eval ::testspace { [string match "*already done*" $::pko_captured]\ [namespace origin ::package] } - } -result {1 1 ::punk::libunknown::package} + } -result {1 1 ::package} # -- packagepreference interop - both real-world install orders --------------- #punkshell boot: packagepreference (commandstack) first, libunknown on top. #make.tcl: libunknown alone (packagepreference may arrive later). #BEHAVIOUR pins: epoch answers and require chains down to the builtin in BOTH - #orders. MARKED-TO-FLIP pins: the commandstack ::package stack sees only the - #packagepreference record (1) while libunknown's rename is unmanaged, and in - #pkgpref_first order ::package is libunknown's import - increment 2 flips the - #stack count to 2 and the origin to the commandstack-installed proc. + #orders. Stack-count and origin elements FLIPPED 2026-08-08 with increment 2 + #as marked: both overrides are now managed stack records (count 2) and + #::package is a commandstack-installed proc in both orders. - test override_interop_pkgpref_first {punkshell order: both overrides live - epoch and require both work; stack sees only the managed record (flips to 2)}\ + test override_interop_pkgpref_first {punkshell order: both overrides live - epoch and require both work; both records on the managed stack}\ -constraints pko_interopsources -body { pko_interop pkgpref_first { set r [list] @@ -270,9 +280,9 @@ namespace eval ::testspace { lappend r [namespace origin ::package] set r } - } -result {1 1.0 1 ::punk::libunknown::package} + } -result {1 1.0 2 ::package} - test override_interop_libunknown_first {make.tcl order then packagepreference on top: epoch reaches through the wrapper's delegation and require works; stack sees only the managed record (flips to 2)}\ + test override_interop_libunknown_first {make.tcl order then packagepreference on top: epoch reaches through the wrapper's delegation and require works; both records on the managed stack}\ -constraints pko_interopsources -body { pko_interop libunknown_first { set r [list] @@ -284,6 +294,6 @@ namespace eval ::testspace { lappend r [namespace origin ::package] set r } - } -result {1 1.0 1 ::package} + } -result {1 1.0 2 ::package} } tcltest::cleanupTests ;#needed to produce test summary line.