From 642fb265ea1bea6dfde319c6a2b14977d97663f4 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Sat, 8 Aug 2026 02:22:41 +1000 Subject: [PATCH] G-176 increment 1 + active flip: ::package override characterisation - the pre-migration safety net (packageoverride.test, 11 pins) G-176 marked active (user-directed). New src/tests/modules/punk/libunknown/testsuites/discovery/packageoverride.test pins the CURRENT punk::libunknown ::package override ahead of the increment-2 migration onto commandstack::rename_command. BEHAVIOUR pins (must stay green byte-identically through the migration): 'package epoch tm|pkg' single-arg forms, indexed ' ?key?' queries, the four error arms, the filtered forget arm (real-ifneeded forgotten, no-ifneeded filtered out, tcl/Tcl/tcl::oo/tk denied, mixed lists partially applied), abbreviation acceptance (ep/epo/epoc + fo routing, with the parked builtin's rejection of 'ep' pinned as the override's deliberate delta), delegation pass-through, and the re-init no-op contract (stderr notice captured via a child ::puts shim). MARKED-TO-FLIP pins (comment-fenced to the increment-2 commit and no other): the install shape (::package origin = the ::punk::libunknown::package namespace import; ::package:: parked and functional) and the two-override packagepreference interop children in BOTH real-world install orders (punkshell's pkgpref-first: stack count 1 + import origin; make.tcl's libunknown-first: stack count 1 + ::package proc origin). Children are fresh interps sourcing the source-tree libunknown/commandstack/packagepreference by path via a shared highest-version locator (discovery.test pattern). All 11 pins green on the first run against the live implementation. Tests AGENTS.md index updated; goal file gains its Progress entry. testbody_lint 1699 clean; goals_lint clean (80 active-index / 96 archived); modules tree 1346 total / 1335 pass / 11 constraint-skipped / 0 fail (zig-built tclsh90s 9.0.5). Claude-Session: https://claude.ai/code/session_01QgaxV27VZkmEec7oNbEVFc Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com --- GOALS.md | 2 +- goals/G-176-commandstack-doc-integration.md | 19 +- src/tests/modules/AGENTS.md | 2 +- .../testsuites/discovery/packageoverride.test | 289 ++++++++++++++++++ 4 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 src/tests/modules/punk/libunknown/testsuites/discovery/packageoverride.test diff --git a/GOALS.md b/GOALS.md index a80c7b6c..b25160ba 100644 --- a/GOALS.md +++ b/GOALS.md @@ -404,7 +404,7 @@ Detail: goals/G-174-cmdtraverse-word-accounting.md Scope: src/modules/punk/args-999999.0a1.0.tm (resolve @dynamic cache-skip, by-id entry points get_spec/parse_status/arg_error/synopsis); src/modules/punk/ns-999999.0a1.0.tm (cmdhelp render pipeline - the four independent by-id fetches); src/tests/modules/punk/args/testsuites/args/dynamic.test + src/tests/modules/punk/ns/testsuites/ns/cmdhelp.test (once-per-render counter pins + cross-render freshness pins, if the landing arm is taken) Detail: goals/G-175-punkargs-dynamic-resolve-multiplicity.md -### G-176 [proposed] commandstack doc integration - stack-tied overrider argdocs, override hints, and the libunknown package-override migration +### 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) 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 a719166a..cb95c221 100644 --- a/goals/G-176-commandstack-doc-integration.md +++ b/goals/G-176-commandstack-doc-integration.md @@ -1,6 +1,6 @@ # G-176 commandstack doc integration - stack-tied overrider argdocs, override hints, and the libunknown package-override migration -Status: proposed +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) 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. @@ -110,6 +110,23 @@ Increments land as separate commits, each leaving all suites green: that motivated the arc); cmdhelp.test pins; `i package epoch` verified in a punk shell. +## Progress + +- Increment 1 (characterisation) landed 2026-08-08: + src/tests/modules/punk/libunknown/testsuites/discovery/packageoverride.test - + 11 pins, all green first run against the current implementation. BEHAVIOUR + pins: epoch single-arg (tm/pkg) and indexed query forms, the four error arms, + the filtered forget arm (real-ifneeded forgotten, no-ifneeded filtered, + tcl/Tcl/tcl::oo/tk denied, mixed lists partially applied), abbreviation + acceptance (ep/epo/epoc + fo routing; the parked builtin rejects 'ep'), + delegation pass-through, and the re-init no-op contract. MARKED-TO-FLIP pins + (comment-fenced to the increment-2 commit): install shape (::package origin = + ::punk::libunknown::package import, ::package:: parked and functional) and the + two-override interop children (both real-world orders: pkgpref-first stack=1 + + import origin; libunknown-first stack=1 + ::package proc origin). Children + source the source-tree libunknown/commandstack/packagepreference by path via a + shared locator (discovery.test pattern). Tests AGENTS.md index updated. + ## Notes - Same-id shadowing: one live record per doc id is the documented constraint diff --git a/src/tests/modules/AGENTS.md b/src/tests/modules/AGENTS.md index fb17440f..f593099f 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 ::. 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) 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 diff --git a/src/tests/modules/punk/libunknown/testsuites/discovery/packageoverride.test b/src/tests/modules/punk/libunknown/testsuites/discovery/packageoverride.test new file mode 100644 index 00000000..0d95a58f --- /dev/null +++ b/src/tests/modules/punk/libunknown/testsuites/discovery/packageoverride.test @@ -0,0 +1,289 @@ +# -*- tcl -*- +# Characterization tests for the punk::libunknown ::package override itself - +# the install shape, the epoch/forget/abbreviation arms and delegation - as a +# pre-migration safety net for G-176 increment 2 (migration of the override +# 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. +# +# Children are fresh interps sourcing the SOURCE-TREE modules by path (same +# rationale and locator pattern as discovery.test in this directory). +# +# Run: tclsh src/tests/runtests.tcl -report compact -show-passes 0 -include-paths modules/punk/libunknown/** packageoverride.test + +package require tcltest + +namespace eval ::testspace { + namespace import ::tcltest::* + + variable script_dir [file dirname [file normalize [info script]]] + variable srcmodules [file normalize [file join $script_dir .. .. .. .. .. .. modules]] + + #pick the highest-version source-tree copy should multiple coexist + proc locate_srcmodule {globpat} { + variable srcmodules + set best "" + set bestver "" + foreach candidate [glob -nocomplain [file join $srcmodules {*}$globpat]] { + set thisver [file rootname [lindex [split [file tail $candidate] -] 1]] + if {$best eq "" || [package vcompare $thisver $bestver] == 1} { + set best $candidate + set bestver $thisver + } + } + return $best + } + variable libunknown_src [locate_srcmodule {punk libunknown-*.tm}] + variable commandstack_src [locate_srcmodule {commandstack-*.tm}] + variable pkgpref_src [locate_srcmodule {punk packagepreference-*.tm}] + + testConstraint pko_sources [expr {$libunknown_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)" + } + + # ------------------------------------------------------------------------- + # 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. + # ------------------------------------------------------------------------- + proc pko_probe {script} { + variable libunknown_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 $libunknown_src] + interp eval $i {punk::libunknown::init} + interp eval $i $script + } finally { + interp delete $i + } + } + + # ------------------------------------------------------------------------- + # Interop probe: both live ::package overrides in the given install order. + # pkgpref_first - punkshell boot order: packagepreference installs its + # wrapper via commandstack first, libunknown's plain + # rename+import lands on top. + # libunknown_first - make.tcl order: libunknown alone first, then + # packagepreference's commandstack wrapper on top. + # ------------------------------------------------------------------------- + proc pko_interop {order script} { + variable libunknown_src + variable commandstack_src + variable pkgpref_src + set i [interp create] + try { + interp eval $i {package prefer latest} + interp eval $i {tcl::tm::remove {*}[tcl::tm::list]} + if {$order eq "pkgpref_first"} { + interp eval $i [list source $commandstack_src] + interp eval $i [list source $pkgpref_src] + interp eval $i {punk::packagepreference::install} + interp eval $i [list source $libunknown_src] + interp eval $i {punk::libunknown::init} + } else { + 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} + } + interp eval $i $script + } finally { + interp delete $i + } + } + + #added 2026-08-08 (agent, G-176 increment 1) - pre-migration characterisation of the + #current ::package override. BEHAVIOUR pins below survive the migration unchanged. + + # -- 'package epoch' query forms (previously only the 0-arg form was pinned) -- + + test override_epoch_singlearg_forms {package epoch tm / package epoch pkg return a one-entry dict keyed by the current epoch, value carrying the epoch record}\ + -constraints pko_sources -body { + pko_probe { + set full [package epoch] + set curtm [dict get $full tm] + set curpkg [dict get $full pkg] + set dtm [package epoch tm] + set dpkg [package epoch pkg] + list [dict size $dtm] [expr {[lindex [dict keys $dtm] 0] eq $curtm}] [dict exists $dtm $curtm added]\ + [dict size $dpkg] [expr {[lindex [dict keys $dpkg] 0] eq $curpkg}] [dict exists $dpkg $curpkg added] + } + } -result {1 1 1 1 1 1} + + test override_epoch_indexed_queries {package epoch returns the epoch record; a trailing key path walks into it}\ + -constraints pko_sources -body { + pko_probe { + list [dict exists [package epoch tm 0] added]\ + [package epoch tm 0 added]\ + [dict exists [package epoch pkg 0] added] + } + } -result {1 {} 1} + + test override_epoch_error_arms {unsupported option, bad index, unsupported pair and missing key each raise their documented message}\ + -constraints pko_sources -body { + pko_probe { + set r [list] + lappend r [catch {package epoch bogus} m] [string match "*unsupported - known options: tm pkg incr*" $m] + lappend r [catch {package epoch tm 99} m] [string match "*unable to use index 99*" $m] + lappend r [catch {package epoch xx 5} m] [string match "*unsupported - expected 'pkg incr' or 'tm incr'*" $m] + lappend r [catch {package epoch tm 0 nosuchkey} m] [string match "*not found. Toplevel keys:*" $m] + set r + } + } -result {1 1 1 1 1 1 1 1} + + # -- the forget arm (previously untested) ------------------------------------ + + test override_forget_filtered {a provided package with a real ifneeded script is forgotten; a provided package with no ifneeded script is filtered out (not forgotten)}\ + -constraints pko_sources -body { + pko_probe { + package ifneeded pko_fake 1.0 {package provide pko_fake 1.0} + package require pko_fake + package provide pko_bare 1.0 + set r [list] + lappend r [package provide pko_fake] + package forget pko_fake + lappend r [package provide pko_fake] + package forget pko_bare + lappend r [package provide pko_bare] + set r + } + } -result {1.0 {} 1.0} + + test override_forget_denied_names {tcl/Tcl/tcl::oo/tk are never forgotten - an all-denied call is a silent no-op and a mixed call forgets only the allowed names}\ + -constraints pko_sources -body { + pko_probe { + package ifneeded pko_fake2 1.0 {package provide pko_fake2 1.0} + package require pko_fake2 + set r [list] + lappend r [expr {[package forget tcl Tcl tcl::oo tk] eq ""}] + lappend r [expr {[package provide Tcl] ne ""}] + package forget Tcl pko_fake2 + lappend r [expr {[package provide Tcl] ne ""}] [package provide pko_fake2] + set r + } + } -result {1 1 1 {}} + + # -- abbreviation acceptance (a behaviour delta the override introduces) ------ + + test override_abbreviations {ep/epo/epoc reach the epoch arm and fo routes to the filtered forget arm - while the parked builtin rejects the same abbreviation}\ + -constraints pko_sources -body { + pko_probe { + set full [package epoch] + set r [list] + foreach a {ep epo epoc} { + lappend r [expr {[package $a] eq $full}] + } + package ifneeded pko_fake3 1.0 {package provide pko_fake3 1.0} + package require pko_fake3 + package fo pko_fake3 + lappend r [package provide pko_fake3] + lappend r [catch {::package:: ep}] + set r + } + } -result {1 1 1 {} 1} + + # -- delegation pass-through -------------------------------------------------- + + test override_delegation_passthrough {subcommands outside the intercepted arms delegate to the previous implementation unchanged}\ + -constraints pko_sources -body { + pko_probe { + package provide pko_dele 2.5 + list [package vcompare 1.2 1.3]\ + [package vsatisfies 1.2 1.0-]\ + [package provide pko_dele]\ + [expr {[package provide Tcl] ne ""}] + } + } -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. + + test override_install_shape {current mechanism: ::package is an import of punk::libunknown::package and ::package:: holds the working previous implementation}\ + -constraints pko_sources -body { + pko_probe { + list [namespace origin ::package]\ + [llength [info commands ::package::]]\ + [expr {[::package:: provide Tcl] ne ""}] + } + } -result {::punk::libunknown::package 1 1} + + # -- 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. + + 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 { + pko_probe { + rename ::puts ::pko_origputs + proc ::puts args { + if {[llength $args] >= 2 && [lindex $args 0] eq "stderr"} { + append ::pko_captured [lindex $args 1] \n + return + } + tailcall ::pko_origputs {*}$args + } + set ::pko_captured "" + set second [punk::libunknown::init] + rename ::puts "" + rename ::pko_origputs ::puts + list [expr {$second eq ""}]\ + [string match "*already done*" $::pko_captured]\ + [namespace origin ::package] + } + } -result {1 1 ::punk::libunknown::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. + + test override_interop_pkgpref_first {punkshell order: both overrides live - epoch and require both work; stack sees only the managed record (flips to 2)}\ + -constraints pko_interopsources -body { + pko_interop pkgpref_first { + set r [list] + set e [package epoch] + lappend r [expr {[dict exists $e tm] && [dict exists $e pkg]}] + package ifneeded pko_x 1.0 {package provide pko_x 1.0} + lappend r [package require pko_x] + lappend r [llength [commandstack::get_stack ::package]] + lappend r [namespace origin ::package] + set r + } + } -result {1 1.0 1 ::punk::libunknown::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)}\ + -constraints pko_interopsources -body { + pko_interop libunknown_first { + set r [list] + set e [package epoch] + lappend r [expr {[dict exists $e tm] && [dict exists $e pkg]}] + package ifneeded pko_y 1.0 {package provide pko_y 1.0} + lappend r [package require pko_y] + lappend r [llength [commandstack::get_stack ::package]] + lappend r [namespace origin ::package] + set r + } + } -result {1 1.0 1 ::package} +} +tcltest::cleanupTests ;#needed to produce test summary line.