Browse Source

runtests: version-floor the runner's requires (fossilized-kit rejection); concurrency child floor

Root cause of the punk86-kit runtests failure: a punk-kit interpreter's
boot pre-registers its kit-stamped package versions, and an unversioned
require satisfiable by such a registration never fires package unknown -
the freshly-added bootsupport tm path is never scanned. The old kit's
punk::args 0.1.0 won the require, then the versioned punk::path 0.4.0-
pulled a modern module whose load-time argdoc needs punk::args::define:
invalid command name.

Floors added: punk::args 0.12- (runtests.tcl toplevel), punk::lib
0.5.1- (both 8.6 lpop guards; punk::path 0.4.0- already floored in
discovery.tcl). Feature-era minimums, not latest pins - their purpose is
rejecting fossils so package unknown resolves the bootsupport copy.
Verified: the floors alone resurrect even the OLD fossilized punk86 kit
(discover-only + a 35/35 suite run); native 9 and 8.6 unaffected.

concurrency.test child preamble floors its punkcheck require (0.6-):
kit-hosted children boot with kit-stamped punkcheck PRELOADED, and a
stale kit (pre-vfscommonupdate _vfscommon) made children fail on
'invalid command name punkcheck:🔒:acquire' - the floor turns that
into a loud version-conflict in the child log.

src/tests/AGENTS.md records the floor rationale and the kit-hosted
child-suite requirement (vfscommonupdate-current kit content).

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 week ago
parent
commit
0eda40af32
  1. 3
      src/tests/AGENTS.md
  2. 9
      src/tests/modules/punkcheck/testsuites/punkcheck/concurrency.test
  3. 18
      src/tests/runtests.tcl

3
src/tests/AGENTS.md

@ -41,7 +41,8 @@ Top-level test harness and source-tree tests for ShellSpy/Punk. Tests here exerc
## Work Guidance ## Work Guidance
- Select an appropriate interpreter, such as `tclsh`, `tclsh90`, or a built Punk executable such as `punk902z`. - Select an appropriate interpreter, such as `tclsh`, `tclsh90`, or a built Punk executable such as `punk902z`.
- Native Tcl 8.6 tclsh is a supported runner interpreter (2026-07-21): `runtests.tcl` loads punk::lib's validated forward-compat command family (lpop etc - builtin parity pinned by `modules/punk/lib/testsuites/lib/compat.test`) at both boot levels (toplevel and testinterp) when the builtins are absent. The guards are no-ops on Tcl 8.7+/9, preserving the G-093 fast two-stage `-discover-only` boot. The punk86-kit route for runtests remains broken (kit/dev module skew loading dev punk::path) - use a native 8.6 tclsh for 8.6 legs. - Native Tcl 8.6 tclsh is a supported runner interpreter (2026-07-21): `runtests.tcl` loads punk::lib's validated forward-compat command family (lpop etc - builtin parity pinned by `modules/punk/lib/testsuites/lib/compat.test`) at both boot levels (toplevel and testinterp) when the builtins are absent. The guards are no-ops on Tcl 8.7+/9, preserving the G-093 fast two-stage `-discover-only` boot.
- Kit-hosted runner hardening (2026-07-21): the runner's own package requires carry minimum-version floors (punk::args in `runtests.tcl`, punk::path in `testsupport/discovery.tcl`, punk::lib in the 8.6 guards). Rationale: a punk-kit `[interpreter]` boot pre-registers its kit-stamped package versions, and an unversioned require satisfiable by such a registration never fires `package unknown` - the freshly-added bootsupport tm path is never even scanned, so a fossilized kit copy silently wins and then skews against modern modules pulled in by versioned requires (the historic punk86 failure: kit punk::args 0.1.0 loaded, then punk::path 0.4.0- died on `invalid command name "punk::args::define"`). Floored requires reject the fossil and resolve the bootsupport copy. The punk86 kit itself is mapped in `src/runtime/mapvfs.config` (8.6.17 dyn tclkit + punk8win.vfs, remapped 2026-07-21). Suites that spawn children of `[info nameofexecutable]` additionally need kit content to be current: kit children boot with kit-stamped modules preloaded from `_vfscommon.vfs`, so run `make.tcl vfscommonupdate` + `make.tcl vfs` after module changes such suites depend on (punkcheck's concurrency.test floors its child-side punkcheck require so a stale kit fails loud in the child log instead of mysteriously). Native tclsh remains the preferred runner interpreter.
- Run the full source-tree suite with `<tcl_interpreter> src/tests/runtests.tcl`. - Run the full source-tree suite with `<tcl_interpreter> src/tests/runtests.tcl`.
- Use `-include-paths` with directory glob patterns relative to `src/tests/`, forward slashes — `modules/punk/args/***` runs that whole subtree (`X/***` deliberately excludes files directly in `X`; bare `X` is files-directly-in-X only). The flag accepts a space-separated list of patterns and may also be repeated, with all occurrences accumulating (`-multiple 1` as of 2026-07-10; previously the last flag silently won). Single-file targeting: `-include-paths modules/punk/args/testsuites/args dynamic.test`. - Use `-include-paths` with directory glob patterns relative to `src/tests/`, forward slashes — `modules/punk/args/***` runs that whole subtree (`X/***` deliberately excludes files directly in `X`; bare `X` is files-directly-in-X only). The flag accepts a space-separated list of patterns and may also be repeated, with all occurrences accumulating (`-multiple 1` as of 2026-07-10; previously the last flag silently won). Single-file targeting: `-include-paths modules/punk/args/testsuites/args dynamic.test`.
- Use `-exclude-paths` (same syntax and accumulation) to remove directories from discovery — `**`|`***` tails prune the matched subtree, other matches drop only that directory's files, and exclusion wins for the same file. It gives broad runs a middle ground between full and targeted: e.g `-exclude-paths shell/***` when no built kit is present, or skipping a heavy suite family unrelated to the change. - Use `-exclude-paths` (same syntax and accumulation) to remove directories from discovery — `**`|`***` tails prune the matched subtree, other matches drop only that directory's files, and exclusion wins for the same file. It gives broad runs a middle ground between full and targeted: e.g `-exclude-paths shell/***` when no built kit is present, or skipping a heavy suite family unrelated to the change.

9
src/tests/modules/punkcheck/testsuites/punkcheck/concurrency.test

@ -48,14 +48,19 @@ namespace eval ::testspace {
try { puts -nonewline $fd $content } finally { close $fd } try { puts -nonewline $fd $content } finally { close $fd }
return $path return $path
} }
#script preamble reproducing this interpreter's module search state in a child process #script preamble reproducing this interpreter's module search state in a child process.
#The punkcheck require is floored: under a punk-kit [interpreter] the child boots with the
#kit-stamped punkcheck PRELOADED, and an unversioned require would silently accept a stale
#copy without the G-095 lock API (seen with a kit whose _vfscommon predated 0.6.0 - children
#failed on 'invalid command name punkcheck::lock::acquire'). The floor turns that into a
#loud version-conflict error in the child log instead.
proc child_preamble {} { proc child_preamble {} {
set preamble "package prefer latest\n" set preamble "package prefer latest\n"
foreach p [lreverse [tcl::tm::path list]] { foreach p [lreverse [tcl::tm::path list]] {
append preamble "tcl::tm::path add [list $p]\n" append preamble "tcl::tm::path add [list $p]\n"
} }
append preamble "set ::auto_path [list $::auto_path]\n" append preamble "set ::auto_path [list $::auto_path]\n"
append preamble "package require punkcheck\n" append preamble "package require punkcheck 0.6-\n"
return $preamble return $preamble
} }
#poll for a condition script returning true; fail with $what after $deadline_ms #poll for a condition script returning true; fail with $what after $deadline_ms

18
src/tests/runtests.tcl

@ -38,14 +38,24 @@ if {$libdir_arch ni $::auto_path} {
#-discover-only early exit below, keeping 'runtests.tcl -discover-only 1' a fast targeting #-discover-only early exit below, keeping 'runtests.tcl -discover-only 1' a fast targeting
#inspection tool (boot ~1s instead of ~11s) for humans, agents and the runner-targeting #inspection tool (boot ~1s instead of ~11s) for humans, agents and the runner-targeting
#regression suite (runner/testsuites/discovery/pathdiscovery.test). #regression suite (runner/testsuites/discovery/pathdiscovery.test).
package require punk::args #Minimum-version floors on the runner's own requires (punk::path has one in
#testsupport/discovery.tcl already): their purpose is to REJECT fossilized kit-stamped copies.
#Under a punk kit executable, the kit's boot registers its own (possibly ancient) package
#versions - an unversioned require satisfiable by such a registration never fires package
#unknown, so the freshly-added bootsupport tm path is never even scanned (observed: an old
#punk86 kit's punk::args 0.1.0 won the require, then the versioned punk::path 0.4.0- pulled a
#modern module whose load needs punk::args::define - invalid command name). A floored require
#rejects the fossil and resolves the current bootsupport copy instead. The floors are
#feature-era minimums for the runner's argdoc/parse usage, not latest-version pins.
package require punk::args 0.12-
if {[info commands ::lpop] eq ""} { if {[info commands ::lpop] eq ""} {
#Native Tcl 8.6 interpreter: no builtin lpop, which punk::args::parse (and later runner code) #Native Tcl 8.6 interpreter: no builtin lpop, which punk::args::parse (and later runner code)
#uses. punk::lib installs its validated forward-compat family (lpop, ledit, lremove, ...) as #uses. punk::lib installs its validated forward-compat family (lpop, ledit, lremove, ...) as
#global aliases whenever the builtins are absent - behavioural parity with the 8.7/9 builtins #global aliases whenever the builtins are absent - behavioural parity with the 8.7/9 builtins
#is pinned by modules/punk/lib/testsuites/lib/compat.test. Guarded: on Tcl 8.7+/9 this never #is pinned by modules/punk/lib/testsuites/lib/compat.test. Guarded: on Tcl 8.7+/9 this never
#runs, preserving the G-093 fast two-stage boot for -discover-only. #runs, preserving the G-093 fast two-stage boot for -discover-only.
package require punk::lib #(0.5.1- = the parity-validated compat era; also a kit-fossil rejection floor per above.)
package require punk::lib 0.5.1-
} }
#test-file discovery + -include-paths/-exclude-paths filtering (G-093) - shared with the #test-file discovery + -include-paths/-exclude-paths filtering (G-093) - shared with the
@ -1163,7 +1173,9 @@ foreach testfile_relative $testfiles {
#native Tcl 8.6 testinterp: shellrun's runx (the runner's capture mechanism) parses via #native Tcl 8.6 testinterp: shellrun's runx (the runner's capture mechanism) parses via
#punk::args::parse, which uses lpop. punk::lib installs the validated compat aliases #punk::args::parse, which uses lpop. punk::lib installs the validated compat aliases
#(parity pinned by modules/punk/lib compat.test). No-op on Tcl 8.7+/9. #(parity pinned by modules/punk/lib compat.test). No-op on Tcl 8.7+/9.
package require punk::lib #(floored like the toplevel requires - fossil-rejection; prefer-latest selects the dev
# module here anyway)
package require punk::lib 0.5.1-
} }
} }
testinterp eval {package require shellrun} testinterp eval {package require shellrun}

Loading…
Cancel
Save