Browse Source

make.tcl tool build: stamp installed exe mtime so the bin freshness state can clear

tool_install_state judges bin/<tool>.exe current/stale by comparing its
mtime against the newest file anywhere in the tool tree. Two layers kept
that exe mtime frozen at the original link time: a zig cache-hit rebuild
does not rewrite the zig-out artifact, and Tcl's 'file copy -force'
preserves the source mtime (Windows CopyFile semantics). Once any
non-build-input file (e.g. PROVENANCE.md, edited after the G-128/G-135
doc passes) was newer than that link time, the tool was reported 'stale'
permanently - repeated 'make.tcl tool build' runs (field-observed
2026-08-01) reinstalled a timestamp-identical artifact and never cleared
it.

Fix: after the install copy, stamp the installed exe with the install
time ('file mtime $exe [clock seconds]'), making the state measure what
it means - installed since the last change in the tree. Also self-heals
mtime churn from branch switches/fresh clones on the next build.

punkshell 0.39.2 + CHANGELOG entry (patch: build-tooling bug fix).
Thin-layout make.tcl copies resynced via 'make.tcl libs -confirm 0'
(basic, project-0.1, modpod templates copy - byte-identical).

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 week ago
parent
commit
7ec7da4a5a
  1. 9
      CHANGELOG.md
  2. 2
      punkproject.toml
  3. 62
      src/make.tcl
  4. 62
      src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl
  5. 62
      src/project_layouts/vendor/punk/basic/src/make.tcl
  6. 62
      src/project_layouts/vendor/punk/project-0.1/src/make.tcl

9
CHANGELOG.md

@ -5,6 +5,15 @@ 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.39.2] - 2026-08-01
- make.tcl tool build: installed exe mtime is now stamped with the install time.
Previously `file copy` preserved the zig-out artifact's mtime and a zig
cache-hit rebuild left that at the original link time, so once any
non-build-input file in the tool tree (e.g. PROVENANCE.md) was newer, the
tool list/info bin state reported 'stale' permanently - no `tool build` run
could clear it.
## [0.39.1] - 2026-08-01
- make.tcl deprecated aliases removed (G-112 transition window closed): `project` and

2
punkproject.toml

@ -1,6 +1,6 @@
[project]
name = "punkshell"
version = "0.39.1"
version = "0.39.2"
license = "BSD-2-Clause"
url = "https://www.gitea1.intx.com.au/jn/punkshell"
#packager: declared identity for published artifacts (declarative, not proof -

62
src/make.tcl

@ -1832,8 +1832,10 @@ set ::punkboot::bootsupport_optional [dict create {*}{
}]
set ::punkboot::bootsupport_recommended [dict create {*}{
} tcllibc [list -note {improves performance significantley}]
]
}]
#set ::punkboot::bootsupport_recommended [dict create {*}{
# } tcllibc [list -note {improves performance significantley}] {*}{
#}]
# ** *** *** *** *** *** *** *** *** *** *** ***
# create an interp in which we hijack package command
@ -2962,7 +2964,7 @@ proc ::punkboot::punkboot_availability_note {} {
append h $broken_out
append h "* $A(HIGHLIGHT)** *** *** ***$A(RST)" \n
append h " These packages are *probably* required for punk boot to function correctly and efficiently" \n
append h " Packages marked RECOMMENDED are *probably* required for punk boot to function efficiently" \n
append h " punk boot may still work if they are available elsewhere for the running interpreter" \n
append h " Review to see if bootsupport should be updated" \n
append h " Call 'make.tcl check' and examine the last table (which includes bootsupport + executable-provided packages)" \n
@ -3412,7 +3414,8 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
]
dict set SUBVALUES bakelist [list\
"@values -min 0 -max -1"\
"kitname -type string -optional 1 -multiple 1${_kitname_choicepart} -help\
"kitname -type string -optional 1 -multiple 1${_kitname_choicepart}\
-help\
{
Configured kit output(s) to report on - a kit name, or @<groupname>
for every kit in a configured group (filters the listing - doubles
@ -3449,18 +3452,19 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
}
punk::args::define {*}$defparts
}
punk::args::define\
"@id -id (script)::punkboot::shell"\
"@normalize"\
"@cmd -name [list "make.tcl shell"] -summary [list [dict get $SUMMARIES shell]] -help [list\
"
Run the punk shell (repl) using bootsupport libraries.
All arguments after the shell subcommand are passed through to the
repl untouched (they are not parsed or validated by make.tcl)."]"\
{@form -synopsis "make.tcl shell ?arg ...?"}\
{@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help\
"Arguments passed through to the punk repl."}
punk::args::define {
@id -id "(script)::punkboot::shell"
@cmd -name "make.tcl shell" -&
-summary "${[dict get $SUMMARIES shell]}" -&
-help -&
"Run the punk shell (repl) using bootsupport libraries.
All arguments after the shell subcommand are passed through to the
repl untouched (they are not parsed or validated by make.tcl)."
@form -synopsis "make.tcl shell ?arg ...?"
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help -&
"Arguments passed through to the punk repl."
}
punk::args::define\
"@id -id (script)::punkboot::buildsuite"\
"@normalize"\
@ -3470,15 +3474,20 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
arg -type any -optional 1 -multiple 1 -help\
"Action (list|info|build), suite name, and - for build - driver
arguments forwarded to the suite's suite.tcl untouched."}
punk::args::define\
"@id -id (script)::punkboot::tool"\
"@normalize"\
"@cmd -name [list "make.tcl tool"] -summary [list [dict get $SUMMARIES tool]] -help [list [dict get $HELPTEXTS tool]]"\
{@form -synopsis "make.tcl tool list|info|build|test ?<toolname> ...? ?-test 0|1?"}\
{@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help\
punk::args::define {
@id -id "(script)::punkboot::tool"
@cmd -name "make.tcl tool" -&
-summary "${[dict get $SUMMARIES tool]}" -&
-help -&
{${[dict get $HELPTEXTS tool]}}
@form -synopsis "make.tcl tool list|info|build|test ?<toolname> ...? ?-test 0|1?"
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help -&
"Action (list|info|build|test), tool name(s), and - for build -
the -test 0|1 gate flag. Parsed by the handler."}
the -test 0|1 gate flag. Parsed by the handler."
}
punk::args::define\
"@id -id (script)::punkboot::help"\
"@normalize"\
@ -5249,6 +5258,11 @@ if {$::punkboot::command eq "tool"} {
puts stderr "tool build $name: could not install $exe ($cperr) - is a copy currently running?"
exit 1
}
#stamp install time: file copy preserves the source mtime, and a zig
#cache-hit rebuild leaves the zig-out artifact at its original link
#time - without this, tool_install_state compares that old link time
#against the tree and a doc-only edit keeps the tool 'stale' forever.
file mtime $exe [clock seconds]
puts stdout "tool build $name: installed $exe ([file size $exe] bytes)"
}
exit 0

62
src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/vendor/punk/project-0.1/src/make.tcl vendored

@ -1832,8 +1832,10 @@ set ::punkboot::bootsupport_optional [dict create {*}{
}]
set ::punkboot::bootsupport_recommended [dict create {*}{
} tcllibc [list -note {improves performance significantley}]
]
}]
#set ::punkboot::bootsupport_recommended [dict create {*}{
# } tcllibc [list -note {improves performance significantley}] {*}{
#}]
# ** *** *** *** *** *** *** *** *** *** *** ***
# create an interp in which we hijack package command
@ -2962,7 +2964,7 @@ proc ::punkboot::punkboot_availability_note {} {
append h $broken_out
append h "* $A(HIGHLIGHT)** *** *** ***$A(RST)" \n
append h " These packages are *probably* required for punk boot to function correctly and efficiently" \n
append h " Packages marked RECOMMENDED are *probably* required for punk boot to function efficiently" \n
append h " punk boot may still work if they are available elsewhere for the running interpreter" \n
append h " Review to see if bootsupport should be updated" \n
append h " Call 'make.tcl check' and examine the last table (which includes bootsupport + executable-provided packages)" \n
@ -3412,7 +3414,8 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
]
dict set SUBVALUES bakelist [list\
"@values -min 0 -max -1"\
"kitname -type string -optional 1 -multiple 1${_kitname_choicepart} -help\
"kitname -type string -optional 1 -multiple 1${_kitname_choicepart}\
-help\
{
Configured kit output(s) to report on - a kit name, or @<groupname>
for every kit in a configured group (filters the listing - doubles
@ -3449,18 +3452,19 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
}
punk::args::define {*}$defparts
}
punk::args::define\
"@id -id (script)::punkboot::shell"\
"@normalize"\
"@cmd -name [list "make.tcl shell"] -summary [list [dict get $SUMMARIES shell]] -help [list\
"
Run the punk shell (repl) using bootsupport libraries.
All arguments after the shell subcommand are passed through to the
repl untouched (they are not parsed or validated by make.tcl)."]"\
{@form -synopsis "make.tcl shell ?arg ...?"}\
{@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help\
"Arguments passed through to the punk repl."}
punk::args::define {
@id -id "(script)::punkboot::shell"
@cmd -name "make.tcl shell" -&
-summary "${[dict get $SUMMARIES shell]}" -&
-help -&
"Run the punk shell (repl) using bootsupport libraries.
All arguments after the shell subcommand are passed through to the
repl untouched (they are not parsed or validated by make.tcl)."
@form -synopsis "make.tcl shell ?arg ...?"
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help -&
"Arguments passed through to the punk repl."
}
punk::args::define\
"@id -id (script)::punkboot::buildsuite"\
"@normalize"\
@ -3470,15 +3474,20 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
arg -type any -optional 1 -multiple 1 -help\
"Action (list|info|build), suite name, and - for build - driver
arguments forwarded to the suite's suite.tcl untouched."}
punk::args::define\
"@id -id (script)::punkboot::tool"\
"@normalize"\
"@cmd -name [list "make.tcl tool"] -summary [list [dict get $SUMMARIES tool]] -help [list [dict get $HELPTEXTS tool]]"\
{@form -synopsis "make.tcl tool list|info|build|test ?<toolname> ...? ?-test 0|1?"}\
{@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help\
punk::args::define {
@id -id "(script)::punkboot::tool"
@cmd -name "make.tcl tool" -&
-summary "${[dict get $SUMMARIES tool]}" -&
-help -&
{${[dict get $HELPTEXTS tool]}}
@form -synopsis "make.tcl tool list|info|build|test ?<toolname> ...? ?-test 0|1?"
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help -&
"Action (list|info|build|test), tool name(s), and - for build -
the -test 0|1 gate flag. Parsed by the handler."}
the -test 0|1 gate flag. Parsed by the handler."
}
punk::args::define\
"@id -id (script)::punkboot::help"\
"@normalize"\
@ -5249,6 +5258,11 @@ if {$::punkboot::command eq "tool"} {
puts stderr "tool build $name: could not install $exe ($cperr) - is a copy currently running?"
exit 1
}
#stamp install time: file copy preserves the source mtime, and a zig
#cache-hit rebuild leaves the zig-out artifact at its original link
#time - without this, tool_install_state compares that old link time
#against the tree and a doc-only edit keeps the tool 'stale' forever.
file mtime $exe [clock seconds]
puts stdout "tool build $name: installed $exe ([file size $exe] bytes)"
}
exit 0

62
src/project_layouts/vendor/punk/basic/src/make.tcl vendored

@ -1832,8 +1832,10 @@ set ::punkboot::bootsupport_optional [dict create {*}{
}]
set ::punkboot::bootsupport_recommended [dict create {*}{
} tcllibc [list -note {improves performance significantley}]
]
}]
#set ::punkboot::bootsupport_recommended [dict create {*}{
# } tcllibc [list -note {improves performance significantley}] {*}{
#}]
# ** *** *** *** *** *** *** *** *** *** *** ***
# create an interp in which we hijack package command
@ -2962,7 +2964,7 @@ proc ::punkboot::punkboot_availability_note {} {
append h $broken_out
append h "* $A(HIGHLIGHT)** *** *** ***$A(RST)" \n
append h " These packages are *probably* required for punk boot to function correctly and efficiently" \n
append h " Packages marked RECOMMENDED are *probably* required for punk boot to function efficiently" \n
append h " punk boot may still work if they are available elsewhere for the running interpreter" \n
append h " Review to see if bootsupport should be updated" \n
append h " Call 'make.tcl check' and examine the last table (which includes bootsupport + executable-provided packages)" \n
@ -3412,7 +3414,8 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
]
dict set SUBVALUES bakelist [list\
"@values -min 0 -max -1"\
"kitname -type string -optional 1 -multiple 1${_kitname_choicepart} -help\
"kitname -type string -optional 1 -multiple 1${_kitname_choicepart}\
-help\
{
Configured kit output(s) to report on - a kit name, or @<groupname>
for every kit in a configured group (filters the listing - doubles
@ -3449,18 +3452,19 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
}
punk::args::define {*}$defparts
}
punk::args::define\
"@id -id (script)::punkboot::shell"\
"@normalize"\
"@cmd -name [list "make.tcl shell"] -summary [list [dict get $SUMMARIES shell]] -help [list\
"
Run the punk shell (repl) using bootsupport libraries.
All arguments after the shell subcommand are passed through to the
repl untouched (they are not parsed or validated by make.tcl)."]"\
{@form -synopsis "make.tcl shell ?arg ...?"}\
{@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help\
"Arguments passed through to the punk repl."}
punk::args::define {
@id -id "(script)::punkboot::shell"
@cmd -name "make.tcl shell" -&
-summary "${[dict get $SUMMARIES shell]}" -&
-help -&
"Run the punk shell (repl) using bootsupport libraries.
All arguments after the shell subcommand are passed through to the
repl untouched (they are not parsed or validated by make.tcl)."
@form -synopsis "make.tcl shell ?arg ...?"
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help -&
"Arguments passed through to the punk repl."
}
punk::args::define\
"@id -id (script)::punkboot::buildsuite"\
"@normalize"\
@ -3470,15 +3474,20 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
arg -type any -optional 1 -multiple 1 -help\
"Action (list|info|build), suite name, and - for build - driver
arguments forwarded to the suite's suite.tcl untouched."}
punk::args::define\
"@id -id (script)::punkboot::tool"\
"@normalize"\
"@cmd -name [list "make.tcl tool"] -summary [list [dict get $SUMMARIES tool]] -help [list [dict get $HELPTEXTS tool]]"\
{@form -synopsis "make.tcl tool list|info|build|test ?<toolname> ...? ?-test 0|1?"}\
{@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help\
punk::args::define {
@id -id "(script)::punkboot::tool"
@cmd -name "make.tcl tool" -&
-summary "${[dict get $SUMMARIES tool]}" -&
-help -&
{${[dict get $HELPTEXTS tool]}}
@form -synopsis "make.tcl tool list|info|build|test ?<toolname> ...? ?-test 0|1?"
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help -&
"Action (list|info|build|test), tool name(s), and - for build -
the -test 0|1 gate flag. Parsed by the handler."}
the -test 0|1 gate flag. Parsed by the handler."
}
punk::args::define\
"@id -id (script)::punkboot::help"\
"@normalize"\
@ -5249,6 +5258,11 @@ if {$::punkboot::command eq "tool"} {
puts stderr "tool build $name: could not install $exe ($cperr) - is a copy currently running?"
exit 1
}
#stamp install time: file copy preserves the source mtime, and a zig
#cache-hit rebuild leaves the zig-out artifact at its original link
#time - without this, tool_install_state compares that old link time
#against the tree and a doc-only edit keeps the tool 'stale' forever.
file mtime $exe [clock seconds]
puts stdout "tool build $name: installed $exe ([file size $exe] bytes)"
}
exit 0

62
src/project_layouts/vendor/punk/project-0.1/src/make.tcl vendored

@ -1832,8 +1832,10 @@ set ::punkboot::bootsupport_optional [dict create {*}{
}]
set ::punkboot::bootsupport_recommended [dict create {*}{
} tcllibc [list -note {improves performance significantley}]
]
}]
#set ::punkboot::bootsupport_recommended [dict create {*}{
# } tcllibc [list -note {improves performance significantley}] {*}{
#}]
# ** *** *** *** *** *** *** *** *** *** *** ***
# create an interp in which we hijack package command
@ -2962,7 +2964,7 @@ proc ::punkboot::punkboot_availability_note {} {
append h $broken_out
append h "* $A(HIGHLIGHT)** *** *** ***$A(RST)" \n
append h " These packages are *probably* required for punk boot to function correctly and efficiently" \n
append h " Packages marked RECOMMENDED are *probably* required for punk boot to function efficiently" \n
append h " punk boot may still work if they are available elsewhere for the running interpreter" \n
append h " Review to see if bootsupport should be updated" \n
append h " Call 'make.tcl check' and examine the last table (which includes bootsupport + executable-provided packages)" \n
@ -3412,7 +3414,8 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
]
dict set SUBVALUES bakelist [list\
"@values -min 0 -max -1"\
"kitname -type string -optional 1 -multiple 1${_kitname_choicepart} -help\
"kitname -type string -optional 1 -multiple 1${_kitname_choicepart}\
-help\
{
Configured kit output(s) to report on - a kit name, or @<groupname>
for every kit in a configured group (filters the listing - doubles
@ -3449,18 +3452,19 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
}
punk::args::define {*}$defparts
}
punk::args::define\
"@id -id (script)::punkboot::shell"\
"@normalize"\
"@cmd -name [list "make.tcl shell"] -summary [list [dict get $SUMMARIES shell]] -help [list\
"
Run the punk shell (repl) using bootsupport libraries.
All arguments after the shell subcommand are passed through to the
repl untouched (they are not parsed or validated by make.tcl)."]"\
{@form -synopsis "make.tcl shell ?arg ...?"}\
{@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help\
"Arguments passed through to the punk repl."}
punk::args::define {
@id -id "(script)::punkboot::shell"
@cmd -name "make.tcl shell" -&
-summary "${[dict get $SUMMARIES shell]}" -&
-help -&
"Run the punk shell (repl) using bootsupport libraries.
All arguments after the shell subcommand are passed through to the
repl untouched (they are not parsed or validated by make.tcl)."
@form -synopsis "make.tcl shell ?arg ...?"
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help -&
"Arguments passed through to the punk repl."
}
punk::args::define\
"@id -id (script)::punkboot::buildsuite"\
"@normalize"\
@ -3470,15 +3474,20 @@ if {[info exists ::env(PUNKBOOT_PLAIN)] && [string is true -strict $::env(PUNKBO
arg -type any -optional 1 -multiple 1 -help\
"Action (list|info|build), suite name, and - for build - driver
arguments forwarded to the suite's suite.tcl untouched."}
punk::args::define\
"@id -id (script)::punkboot::tool"\
"@normalize"\
"@cmd -name [list "make.tcl tool"] -summary [list [dict get $SUMMARIES tool]] -help [list [dict get $HELPTEXTS tool]]"\
{@form -synopsis "make.tcl tool list|info|build|test ?<toolname> ...? ?-test 0|1?"}\
{@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help\
punk::args::define {
@id -id "(script)::punkboot::tool"
@cmd -name "make.tcl tool" -&
-summary "${[dict get $SUMMARIES tool]}" -&
-help -&
{${[dict get $HELPTEXTS tool]}}
@form -synopsis "make.tcl tool list|info|build|test ?<toolname> ...? ?-test 0|1?"
@values -min 0 -max -1
arg -type any -optional 1 -multiple 1 -help -&
"Action (list|info|build|test), tool name(s), and - for build -
the -test 0|1 gate flag. Parsed by the handler."}
the -test 0|1 gate flag. Parsed by the handler."
}
punk::args::define\
"@id -id (script)::punkboot::help"\
"@normalize"\
@ -5249,6 +5258,11 @@ if {$::punkboot::command eq "tool"} {
puts stderr "tool build $name: could not install $exe ($cperr) - is a copy currently running?"
exit 1
}
#stamp install time: file copy preserves the source mtime, and a zig
#cache-hit rebuild leaves the zig-out artifact at its original link
#time - without this, tool_install_state compares that old link time
#against the tree and a doc-only edit keeps the tool 'stale' forever.
file mtime $exe [clock seconds]
puts stdout "tool build $name: installed $exe ([file size $exe] bytes)"
}
exit 0

Loading…
Cancel
Save