Browse Source

make.tcl bootsupport re-vendor: punkcheck 0.5.0 + cli 0.5.1 (G-094), pending module syncs

Batched punkcheck-managed build outputs from 'make.tcl modules' +
'make.tcl bootsupport' after the G-094 landing: punkcheck 0.4.0 -> 0.5.0
and punk::mix::cli 0.5.0 -> 0.5.1 snapshots (per the G-094 prompt
re-vendor note - make.tcl now runs the unified lifecycle), plus
opportunistic catch-up of stale snapshots (libunknown 0.2.0 -> 0.2.1,
punk::config / punk::lib / punk::repl content syncs from current built
modules).

Verification under the new punkcheck: 'make.tcl modules' x2 and a full
'make.tcl project' complete exit 0 with skip/copy decisions unchanged
against pre-refactor .punkcheck state - copied: 0 on every batch phase,
kit installs skip 'no change detected' (the sole punk905 rebuild traces
to the newly promoted tclsh905 runtime, a genuinely changed source).

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 3 days ago
parent
commit
04193e5bd9
  1. 1
      src/bootsupport/modules/punk/config-0.2.0.tm
  2. 64
      src/bootsupport/modules/punk/lib-0.5.0.tm
  3. 25
      src/bootsupport/modules/punk/libunknown-0.2.1.tm
  4. 39
      src/bootsupport/modules/punk/mix/cli-0.5.1.tm
  5. 63
      src/bootsupport/modules/punk/repl-0.5.2.tm
  6. 803
      src/bootsupport/modules/punkcheck-0.5.0.tm

1
src/bootsupport/modules/punk/config-0.2.0.tm

@ -275,6 +275,7 @@ tcl::namespace::eval punk::config {
PUNK_APPS {type pathlist}
PUNK_CONFIG {type string}
PUNK_CONFIGSET {type string}
PUNK_FOSSIL_STORE {type string help "location of the punkshell-owned fossil clone store\n(source mirrors for buildsuites - e.g src/buildsuites/suite_tcl90/suite.tcl).\nDefault: ~/.punkshell/fossils (machine-level cache, outside deletable _build areas).\nMissing clones are seeded read-only from ~/.fossils when present, else network-cloned."}
PUNK_SCRIPTLIB {type string}
PUNK_AUTO_EXEC_MECHANISM {type string}
PUNK_AUTO_NOEXEC {type string default 0 help "set 1 to set Tcl's ::auto_noexec true.\nStops 'unknown' from running external programs"}

64
src/bootsupport/modules/punk/lib-0.5.0.tm

@ -252,11 +252,14 @@ tcl::namespace::eval punk::lib::check {
#G-076: version gate for the tcl9 dead-console defect (upstream ticket f10d91c2d3, root-caused
#in G-039). Shared by the 'help tcl' warning and the repl dead-console watchdog arming (both
#consult has_tclbug_console_deadspin). Empty = no released Tcl is known to contain the upstream
#fix, so every Tcl 9 windows runtime classifies as affected. Set this only after the G-039 kill
#procedure, re-run on the fixed released runtime with the watchdog disabled, shows a clean
#script-visible eof exit (see goals/G-076-tcl9-deadconsole-fix-adoption.md).
variable tclbug_console_deadspin_fixed_in ""
#consult has_tclbug_console_deadspin). Empty would mean no Tcl is known to contain the upstream
#fix (every Tcl 9 windows runtime classifies as affected). Set to 9.0.5 2026-07-20: the upstream
#fix (ticket closed 2026-07-14; merged to core-9-0-branch, whose head reports 9.0.5) passed the
#G-039 kill procedure on a zig-built core-9-0-branch runtime with the watchdog disarmed - the
#dead console arrived as a readable fileevent and the repl exited via the normal eof path
#(requires the punk::repl 0.5.2 repl_handler guard; an official release is not required per the
#amended acceptance - see goals/archive/G-076-tcl9-deadconsole-fix-adoption.md 2026-07-20 entries).
variable tclbug_console_deadspin_fixed_in "9.0.5"
#pure classifier, separated for testability - facts in, verdict out
proc tclbug_console_deadspin_applies {platform tclversion fixed_in} {
@ -8412,7 +8415,9 @@ namespace eval punk::lib {
if {$do_libunknown} {
if {[info exists ::punk::libunknown::epoch]} {
append script "namespace eval ::punk::libunknown {}\n"
append script "set ::punk::libunknown::epoch [list $::punk::libunknown::epoch]\n"
#reduced copy - a verbatim copy carries 'added' short-circuit state that is
#only sound in the donor interp (see libunknown_epoch_export)
append script "set ::punk::libunknown::epoch [list [libunknown_epoch_export]]\n"
}
#find and source libunknown, then call init
append script {
@ -8538,7 +8543,9 @@ namespace eval punk::lib {
if {$do_libunknown} {
if {[info exists ::punk::libunknown::epoch]} {
interp eval $interp {namespace eval ::punk::libunknown {}}
interp eval $interp [list set ::punk::libunknown::epoch $::punk::libunknown::epoch]
#reduced copy - a verbatim copy carries 'added' short-circuit state that is
#only sound in the donor interp (see libunknown_epoch_export)
interp eval $interp [list set ::punk::libunknown::epoch [libunknown_epoch_export]]
}
#use snapshot's libunknown sourcing logic via interp eval
set libunknown_script [punk::lib::snapshot_package_paths -libunknown 1]
@ -8578,6 +8585,49 @@ namespace eval punk::lib {
}
}
namespace eval argdoc {
variable PUNKARGS
lappend PUNKARGS [list {
@id -id ::punk::lib::libunknown_epoch_export
@cmd -name punk::lib::libunknown_epoch_export\
-summary\
"Reduced copy of the punk::libunknown epoch record, safe to seed into a fresh interp or thread."\
-help\
"Returns ::punk::libunknown::epoch with every per-epoch 'added' map emptied.
The 'added' maps record which package names an interp's own scans registered from
each searchpath during each epoch - zipfs_tclPkgUnknown / zipfs_tm_UnknownHandler
consult the CURRENT epoch's map to skip re-sourcing a searchpath's indexes for a
name the path didn't provide before. That short-circuit is only sound in the interp
that performed the scans: packages the donor obtained at an EARLIER epoch, or with
no package unknown call at all (e.g Thread baked into a thread::create'd thread),
are absent from the donor's current-epoch map, so a fresh interp inheriting the
record verbatim skips their pkgIndex/tm sourcing and fails requires that stock
Tcl's tclPkgUnknown would satisfy (G-098 tkconsole diagnosis 2026-07-20).
Epoch counters, untracked markers, ifneeded-script records and index-glob caches
are preserved; the recipient performs its own first sourcing pass per searchpath
and accumulates its own 'added' state. Errors if ::punk::libunknown::epoch is not
set - callers that tolerate an uninitialised libunknown should guard with
info exists as they would for the raw variable."
@values -min 0 -max 0
}]
}
proc libunknown_epoch_export {} {
if {![info exists ::punk::libunknown::epoch]} {
error "punk::lib::libunknown_epoch_export - ::punk::libunknown::epoch not set (punk::libunknown not initialised in this interp?)"
}
set e $::punk::libunknown::epoch
foreach domain {tm pkg} {
if {![dict exists $e $domain epochs]} {continue}
dict for {ep state} [dict get $e $domain epochs] {
dict set state added [dict create]
dict set e $domain epochs $ep $state
}
}
return $e
}
proc valcopy {obj} {
append obj2 $obj {}
}

25
src/bootsupport/modules/punk/libunknown-0.2.0.tm → src/bootsupport/modules/punk/libunknown-0.2.1.tm

@ -7,7 +7,7 @@
# (C) 2025
#
# @@ Meta Begin
# Application punk::libunknown 0.2.0
# Application punk::libunknown 0.2.1
# Meta platform tcl
# Meta license MIT
# @@ Meta End
@ -18,6 +18,14 @@
# 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.2.1 - zipfs_tm_UnknownHandler: the zipfs static-path can_skip_update state is
# written keyed by the SPECIFIC searchpath actually scanned (tm path +
# package namespace subdir) to match its reader and libunknown::init's
# indexbase keying. It was written keyed by the tm-list root, so resolving
# any subdir tm (e.g punk::args) poisoned the ROOT's skip map and later
# root-level requires (e.g commandstack) were skipped as known-absent -
# breaking 'shell' on zipfs kits (single //zipfs:/app/modules tm path;
# filesystem tm paths never take the skip branch). Diagnosed 2026-07-20.
#0.2.0 - register_all_tm: deep discovery proc registering ifneeded scripts for
# .tm modules at every namespace depth (once per tm epoch, interp-local
# tm_fullscan guard); used by 'dev lib.search' by default
@ -34,7 +42,7 @@
# doctools header
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++
#*** !doctools
#[manpage_begin punkshell_module_punk::libunknown 0 0.2.0]
#[manpage_begin punkshell_module_punk::libunknown 0 0.2.1]
#[copyright "2025"]
#[titledesc {Module API}] [comment {-- Name section and table of contents description --}]
#[moddesc {-}] [comment {-- Description at end of page heading --}]
@ -355,8 +363,10 @@ tcl::namespace::eval ::punk::libunknown {
# the token-to-directory mapping may have changed.
#JMN - review.
#dict set epoch tm epochs $pkg_epoch added $currentsearchpath $pkgname [dict create e $pkg_epoch v $pkgversion]
dict set epoch tm epochs $pkg_epoch added $currentsearchpath $pkgname $pkgversion e$pkg_epoch
#0.2.1: keyed by specificsearchpath (the subset actually scanned) - the
#can_skip_update reader and libunknown::init both key that way; keying by
#the tm-list root here let subdir scans poison root-level skip decisions.
dict set epoch tm epochs $pkg_epoch added $specificsearchpath $pkgname $pkgversion e$pkg_epoch
if {$must_scan} {
#however - if we know we're forced to scan all tm paths we can remove discovered sibling tms from tm untracked
dict unset epoch tm untracked $pkgname
@ -389,8 +399,9 @@ tcl::namespace::eval ::punk::libunknown {
#JMN
#store only once for each name, although there may be multiple versions of same package within this searchpath
#dict set epoch tm epochs $pkg_epoch added $currentsearchpath $pkgname [dict create e $pkg_epoch v $pkgversion]
dict set epoch tm epochs $pkg_epoch added $currentsearchpath $pkgname $pkgversion e$pkg_epoch
#0.2.1: keyed by specificsearchpath - see the matching note at the
#already-ifneeded branch above.
dict set epoch tm epochs $pkg_epoch added $specificsearchpath $pkgname $pkgversion e$pkg_epoch
#pkgname here could be the 'name' passed at the beggning - or other .tms at the same location.
#we can't always remove other .tms from 'tm untracked' because the search for name might skip some locations.
if {$must_scan} {
@ -2119,7 +2130,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.0
set version 0.2.1
}]
return

39
src/bootsupport/modules/punk/mix/cli-0.5.0.tm → src/bootsupport/modules/punk/mix/cli-0.5.1.tm

@ -7,7 +7,7 @@
# (C) 2023
#
# @@ Meta Begin
# Application punk::mix::cli 0.5.0
# Application punk::mix::cli 0.5.1
# Meta platform tcl
# Meta license <unspecified>
# @@ Meta End
@ -772,7 +772,6 @@ namespace eval punk::mix::cli {
-glob $fileglob
-max-depth 0
}]
#lassign [punkcheck::start_installer_event $punkcheck_file $installername $srcdir $basedir $config] _eventid punkcheck_eventid _recordset record_list
# -- ---
set installer [punkcheck::installtrack new $installername $punkcheck_file]
#set installer [punkcheck::installtrack new $installername $punkcheck_file stderr] ;#with debugchannel
@ -990,7 +989,6 @@ namespace eval punk::mix::cli {
} else {
puts -nonewline stderr "Z"
set did_skip 1
#set file_record [punkcheck::installfile_skipped_install $basedir $file_record]
$build_event targetset_end SKIPPED
}
$build_event destroy
@ -1180,7 +1178,6 @@ namespace eval punk::mix::cli {
} else {
puts -nonewline stderr "T"
set did_skip 1
#set file_record [punkcheck::installfile_skipped_install $basedir $file_record]
$build_event targetset_end SKIPPED
}
$build_event destroy
@ -1293,9 +1290,6 @@ namespace eval punk::mix::cli {
}
#------------------------------
#
#set target_relpath [punkcheck::lib::path_relative $basedir $target_module_dir/$basename-$module_build_version.tm]
#set file_record [punkcheck::installfile_begin $basedir $target_relpath $installername -eventid $punkcheck_eventid]
$event targetset_init INSTALL $target_module_dir/$basename-$module_build_version.tm
$event targetset_addsource $versionfile
$event targetset_addsource $current_source_dir/$m
@ -1306,24 +1300,11 @@ namespace eval punk::mix::cli {
$event targetset_addsource_virtual module_version $module_build_version
}
#set changed_list [list]
## -- --- --- --- --- ---
#set source_relpath [punkcheck::lib::path_relative $basedir $versionfile]
#set file_record [punkcheck::installfile_add_source_and_fetch_metadata $basedir $source_relpath $file_record]
## -- --- --- --- --- ---
#set source_relpath [punkcheck::lib::path_relative $basedir $current_source_dir/$m]
#set file_record [punkcheck::installfile_add_source_and_fetch_metadata $basedir $source_relpath $file_record]
## -- --- --- --- --- ---
#set changed_unchanged [punkcheck::recordlist::file_install_record_source_changes [lindex [dict get $file_record body] end]]
#set changed_list [dict get $changed_unchanged changed]
if {\
[llength [dict get [$event targetset_source_changes] changed]]\
|| [llength [$event get_targets_exist]] < [llength [$event get_targets]]\
} {
#set file_record [punkcheck::installfile_started_install $basedir $file_record]
$event targetset_started
# -- --- --- --- --- ---
set target $target_module_dir/$basename-$module_build_version.tm
@ -1338,7 +1319,6 @@ namespace eval punk::mix::cli {
#file copy -force $srcdir/$m $target
lappend module_list $target
# -- --- --- --- --- ---
#set file_record [punkcheck::installfile_finished_install $basedir $file_record]
$event targetset_end OK
} else {
if {$is_interesting} {
@ -1346,7 +1326,6 @@ namespace eval punk::mix::cli {
}
puts -nonewline stderr "m"
set did_skip 1
#set file_record [punkcheck::installfile_skipped_install $basedir $file_record]
$event targetset_end SKIPPED
}
prune_superseded_target_modules $event $basedir $target_module_dir $basename $module_build_version
@ -1358,17 +1337,6 @@ namespace eval punk::mix::cli {
continue
}
##------------------------------
##
#set target_relpath [punkcheck::lib::path_relative $basedir $target_module_dir/$m]
#set file_record [punkcheck::installfile_begin $basedir $target_relpath $installername -eventid $punkcheck_eventid]
#set changed_list [list]
## -- --- --- --- --- ---
#set source_relpath [punkcheck::lib::path_relative $basedir $current_source_dir/$m]
#set file_record [punkcheck::installfile_add_source_and_fetch_metadata $basedir $source_relpath $file_record]
## -- --- --- --- --- ---
#set changed_unchanged [punkcheck::recordlist::file_install_record_source_changes [lindex [dict get $file_record body] end]]
#set changed_list [dict get $changed_unchanged changed]
#----------
$event targetset_init INSTALL $target_module_dir/$m
$event targetset_addsource $current_source_dir/$m
catch {
@ -1382,7 +1350,6 @@ namespace eval punk::mix::cli {
|| [llength [$event get_targets_exist]] < [llength [$event get_targets]]\
} {
#set file_record [punkcheck::installfile_started_install $basedir $file_record]
$event targetset_started
# -- --- --- --- --- ---
if {$did_skip} {set did_skip 0; puts -nonewline stdout \n}
@ -1390,7 +1357,6 @@ namespace eval punk::mix::cli {
file copy -force $current_source_dir/$m $target_module_dir
puts stderr "Copied already versioned module $current_source_dir/$m to $target_module_dir"
# -- --- --- --- --- ---
#set file_record [punkcheck::installfile_finished_install $basedir $file_record]
$event targetset_end OK -note "already versioned module"
} else {
puts -nonewline stderr "f"
@ -1398,7 +1364,6 @@ namespace eval punk::mix::cli {
if {$is_interesting} {
puts stderr "$current_source_dir/$m [$event targetset_source_changes]"
}
#set file_record [punkcheck::installfile_skipped_install $basedir $file_record]
$event targetset_end SKIPPED
}
prune_superseded_target_modules $event $basedir $target_module_dir $basename $tmfile_versionsegment
@ -1687,6 +1652,6 @@ namespace eval punk::mix::cli {
## Ready
package provide punk::mix::cli [namespace eval punk::mix::cli {
variable version
set version 0.5.0
set version 0.5.1
}]
return

63
src/bootsupport/modules/punk/repl-0.5.2.tm

@ -1012,20 +1012,32 @@ proc repl::console_watchdog {inputchan} {
}
if {[catch {chan configure $inputchan -inputmode}]} {
#GetConsoleMode failed - the hosting console is gone.
#Close the channel (stops the tclWinConsole.c reader thread's error busy-loop)
#and finish the repl via the normal eof path. Do not attempt a console reopen:
#with the console dead, CONIN$ cannot be opened either (app-punkshell's eof
#handling makes the same discovery and exits cleanly).
array unset console_watchdog_afterids $inputchan
catch {chan event $inputchan readable {}}
catch {chan close $inputchan}
catch {puts stderr "|repl> console_watchdog: console unavailable for '$inputchan' - closing input and finishing repl (eof)"}
set ::repl::done [list eof $inputchan]
console_input_lost $inputchan "console_watchdog: console unavailable for '$inputchan' - closing input and finishing repl (eof)"
return
}
set console_watchdog_afterids($inputchan) [after $console_watchdog_ms [list [namespace current]::console_watchdog $inputchan]]
}
proc repl::console_input_lost {inputchan msg} {
#Shared terminal path for a dead/unusable console input channel (G-039/G-076),
#used by console_watchdog (polling rescue on defect-carrying tcl9 runtimes) and by
#repl_handler (runtimes carrying the upstream f10d91c2d3 fix deliver the dead
#console as a readable event instead).
#Close the channel (stops the tclWinConsole.c reader thread's error busy-loop)
#and finish the repl via the normal eof done-path. Do not attempt a console reopen:
#with the console dead, CONIN$ cannot be opened either (app-punkshell's eof
#handling makes the same discovery and exits cleanly).
variable console_watchdog_afterids
if {[info exists console_watchdog_afterids($inputchan)]} {
catch {after cancel $console_watchdog_afterids($inputchan)}
array unset console_watchdog_afterids $inputchan
}
catch {chan event $inputchan readable {}}
catch {chan close $inputchan}
catch {puts stderr "|repl> $msg"}
set ::repl::done [list eof $inputchan]
}
namespace eval repl::argdoc {
lappend PUNKARGS [list {
@id -id ::repl::console_get_size
@ -1852,7 +1864,18 @@ proc repl::repl_handler {inputchan readmore prompt_config} {
#According to DKF - -buffering option doesn't affect input channels
set rawmode 0
set waiting_needs_reader 0 ;#set when the 8.6-console waiting-chunks path skips its read on a drained channel - the readable handler must be armed (not the after-idle reinvoke) or the pending partial line could never complete
set original_input_conf [chan configure $inputchan] ;#whether repl is in line or raw mode - we restore the inputchan (stdin) state
if {[catch {chan configure $inputchan} original_input_conf]} {
#A runtime carrying the upstream tcl9 dead-console fix (f10d91c2d3) DELIVERS a
#dead console as a readable event, and this full option probe then errors
#(e.g 'couldn't read console mode: broken pipe'). Pre-fix runtimes never notify
#script level of a dead console (the console_watchdog poll covers those), so
#reaching here with a dead console requires the fixed driver. Terminal input
#loss - same teardown as the watchdog, no reopen attempt.
set in_repl_handler [list]
console_input_lost $inputchan "repl_handler: input channel unusable ($original_input_conf) - closing input and finishing repl (eof)"
return
}
#original_input_conf: whether repl is in line or raw mode - we restore the inputchan (stdin) state
if {[dict exists $original_input_conf -inputmode]} {
#review - when terminal is mintty and we switch to raw mode, the change isn't relected in chan configures '-inputmode' why?
@ -2923,11 +2946,19 @@ proc repl::repl_process_data {inputchan chunktype chunk stdinlines prompt_config
#to the selected console's channels (rputs maps stdout/stderr to conout/conerr).
#Interleaving between the two streams within a run is not preserved (single
#terminal stream; per-console err semantics are G-011).
#The vars are guarded: if the code-interp init aborted before installing the
#junction (e.g a package resolution failure) they don't exist - treat as no
#pending output rather than erroring here, which would leave the repl reader
#disabled and wedge the session (G-098 tkconsole diagnosis 2026-07-20).
set pending [thread::send $codethread {
interp eval code {
set _pendinglist [list $::codeinterp::console_pending_out $::codeinterp::console_pending_err]
set ::codeinterp::console_pending_out ""
set ::codeinterp::console_pending_err ""
if {[info exists ::codeinterp::console_pending_out]} {
set _pendinglist [list $::codeinterp::console_pending_out $::codeinterp::console_pending_err]
set ::codeinterp::console_pending_out ""
set ::codeinterp::console_pending_err ""
} else {
set _pendinglist [list "" ""]
}
set _pendinglist
}
}]
@ -3517,7 +3548,7 @@ namespace eval repl {
} %replthread_interp% [list $opt_callback_interp] {*}{
} %tmlist% [list [tcl::tm::list]] {*}{
} %autopath% [list $::auto_path] {*}{
} %lib_epoch% [list $::punk::libunknown::epoch] {*}{
} %lib_epoch% [list [punk::lib::libunknown_epoch_export]] {*}{
} %packageprefer% [list [package prefer]] {*}{
} %staticprefixes% [list [expr {[info exists ::punkboot::static_prefixes] ? $::punkboot::static_prefixes : ""}]] {*}{
} %staticpackages% [list [expr {[info exists ::punkboot::static_packages] ? $::punkboot::static_packages : ""}]] {*}{
@ -3923,7 +3954,9 @@ namespace eval repl {
catch {punk::lib::interp_sync_package_paths code}
code eval [list namespace eval ::punk::libunknown {}]
catch {
code eval [list set ::punk::libunknown::epoch $::punk::libunknown::epoch]
#reduced copy - 'added' short-circuit state is only sound in the donor
#interp (see punk::lib::libunknown_epoch_export)
code eval [list set ::punk::libunknown::epoch [punk::lib::libunknown_epoch_export]]
}
}
safebase {

803
src/bootsupport/modules/punkcheck-0.4.0.tm → src/bootsupport/modules/punkcheck-0.5.0.tm

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save