diff --git a/CHANGELOG.md b/CHANGELOG.md index 67b57713..24bd1d3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ 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.12.27] - 2026-07-14 + +- make.tcl shell under a built punk executable: fixed missing prompts and the raw-mode 'invalid command name struct::set' error. Both stemmed from the kit's script-mode boot pre-loading the punk stack: (1) app-punkscript correctly forces ::tcl_interactive 0 for script runs, and since punk::repl was already loaded its load-time interactivity probe never re-ran - the shell branch now recomputes ::tcl_interactive via punk::repl::is_interactive against the real input channel before repl::start; (2) the accelerator-reload block (package forget + command destroy of sha1/md5/struct::*) relied on later package-require chains to restore the packages, which never re-fire when consumers (punk::lib, punk::du, flagfilter etc) were pre-loaded at kit boot - the block now records what was actually loaded and re-requires it immediately (verified: repl-thread interp state now identical under tclsh and punk91). src/AGENTS.md gains a "pre-loaded interp, not a virgin one" note covering the class: interp surgery and load-time-state assumptions in make.tcl must restore deliberately and be tested under both tclsh and a punk exe (includes the kit-vs-bootsupport module provenance-mixing caveat). + ## [0.12.26] - 2026-07-14 - make.tcl under a built punk executable: kit builds gain a self-build guard - the kit whose deployed executable is running the build is skipped with a clear warning (previously the pre-deploy process sweep would taskkill the build's own process mid-run with -k, since it matched processes by executable name only; without -k the polite kill's failure was the only thing saving the run). The sweep now also excludes the build's own pid unconditionally (covers a same-named copy driving the build from a non-deploy path). Informational/update subcommands were already safe (they exit before the kit machinery); building *other* kits from a punk exe remains supported. Also quietened the 'punk::libunknown::init already done - unnecessary call?' stderr diagnostic when a punk exe runs make.tcl: make.tcl now leaves an already-active libunknown in place instead of re-sourcing the bootsupport copy over it and tripping init's rename guard. src/AGENTS.md build guidance updated. diff --git a/punkproject.toml b/punkproject.toml index 23b05aa9..9c44b8fd 100644 --- a/punkproject.toml +++ b/punkproject.toml @@ -1,4 +1,4 @@ [project] name = "punkshell" -version = "0.12.26" +version = "0.12.27" license = "BSD-2-Clause" diff --git a/src/AGENTS.md b/src/AGENTS.md index 24a1e16b..cc41c4ee 100644 --- a/src/AGENTS.md +++ b/src/AGENTS.md @@ -70,6 +70,7 @@ Recovery after a wrong path guess: - make.tcl subcommands and flags are punk::args-declared (G-030): `tclsh src/make.tcl help ?subcommand?` or ` -help` shows tabled usage; invalid arguments produce punk::args usage errors. The dispatch degrades to plain scan/help when the bootsupport punk::args (or its rendering stack) is stale or unavailable; `PUNKBOOT_PLAIN=1` forces the degraded mode for troubleshooting. The `::punkboot::argdoc` definitions are constructed (string-built) and use the G-045 authoring mechanisms — block-form help values + `@normalize`, explicit `@form -synopsis` — see src/modules/AGENTS.md "punk::args definition authoring ergonomics" before editing them or authoring similar definitions. - Uncommitted `make.tcl`-generated outputs may be batched into one commit, regardless of how many `make.tcl` invocations produced them. This covers punkcheck-managed build outputs that are VCS-tracked: `src/bootsupport/`, `_vfscommon.vfs/modules` + `/lib`, and declared per-kit `*.vfs/lib_tcl/` subfolders. (Root `modules/`, `lib/`, `modules_tcl/`, `lib_tcl/` are gitignored and never committed.) Each module's old→new version rename is atomic; a regeneration reflects one build-output refresh, not independent hand-edits. This is a carve-out from generic "split aggressively" commit guidance for punkcheck-managed build outputs only — it does not cover user-curated VFS content (`_config/`, per-kit `*.vfs/` outside declared `lib_tcl` subfolders, `_aside/`, `mkzipfix.vfs`, `_vfscommon.vfs/doc`), which commits separately per its own concerns. - Use `punk make.tcl project` or `punk902z make.tcl project` inside Punk shell when building binaries through Punk. Driving make.tcl from a built punk executable is supported for informational/update subcommands and for kit builds of *other* kits — the kit whose deployed executable is running the build is skipped with a warning (it cannot be replaced while running, and the pre-deploy process sweep must not kill the build itself; the sweep also excludes the build's own pid in all cases). Rebuild that kit from tclsh or a different kit. +- **Punk-exe-hosted make.tcl runs in a pre-loaded interp, not a virgin one** — the kit's script-mode boot has already loaded much of the punk stack (punk, punk::lib, punk::repl, punk::console, punk::du, flagfilter, struct::set ...) and set process state (app-punkscript forces `::tcl_interactive 0` for script semantics; libunknown/packagepreference are active). Consequences make.tcl must (and now does) handle explicitly: `package require` of an already-provided package is a no-op, so anything make.tcl breaks in the interp (the accelerator-reload block forgets+destroys sha1/md5/struct::* — it re-requires what was loaded), and anything computed at package-load time (punk::repl's `::tcl_interactive` probe — the shell branch recomputes it before `repl::start`), must be restored deliberately rather than relying on later loads to re-fire. Also note the copies that run in this mode are the *kit's* pre-loaded modules, not the bootsupport snapshots make.tcl's paths would otherwise prefer — silent provenance mixing when versions diverge. When adding interp-surgery or load-time-state assumptions to make.tcl, test under both `tclsh src/make.tcl ...` and ` src/make.tcl ...`. - Binary images are platform-specific; build on each target platform rather than expecting a cross-platform flag. - Remove `_build/` artifacts only when a clean/resync is needed, then rerun the relevant `make.tcl` command. Avoid partial cleans that break boot modules. - Superseded module intermediates are pruned automatically so payload wrapping inherits clean trees: diff --git a/src/make.tcl b/src/make.tcl index 02ced58b..e67c6f18 100644 --- a/src/make.tcl +++ b/src/make.tcl @@ -2131,12 +2131,31 @@ if {$package_paths_modified} { #whether they provide functions such as Loaded, Implementations, SwitchTo set acceleratable [list sha1 md5] lappend acceleratable {*}[lsearch -all -inline [package names] struct::*] +set acceleratable_reload [list] foreach p $acceleratable { + if {[package provide $p] ne ""} { + #was actually loaded (not merely indexed) - must be re-required below or consumers + #that already required it are left calling a destroyed/forgotten command + lappend acceleratable_reload $p + } package forget $p if {[string match struct::* $p]} { catch {rename $p ""} } } +#Re-require whatever was loaded before the forget. The forget/rename exists so the reload can +#pick up accelerators (e.g tcllibc) from the now-restored full package paths - but the reload +#must actually happen here: when make.tcl runs under a built punk executable the consumers +#(punk::lib, punk::du, flagfilter etc) were already loaded at kit boot, so no later +#'package require' chain re-fires for these and e.g raw-mode repl paths would hit +#'invalid command name struct::set'. (Under plain tclsh the shell-branch loads happened to +#re-require them - relying on that was load-order luck, not design.) +foreach p $acceleratable_reload { + if {[catch {package require $p} _accelerr]} { + puts stderr "make.tcl: failed to reload package $p after accelerator-reload forget ($_accelerr) - its commands are unavailable for the rest of this run" + } +} +unset -nocomplain acceleratable_reload _accelerr # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- @@ -2393,6 +2412,13 @@ if {$::punkboot::command eq "shell"} { set ::argv [lrange $::argv 1 end] incr ::argc -1 + #When make.tcl runs under a built punk executable, app-punkscript set + #::tcl_interactive 0 (correct for script execution) and punk::repl was already loaded + #at kit boot - the package require above re-loads nothing, so the repl's load-time + #interactivity probe never re-ran and prompts would stay suppressed on a real console. + #Recompute against the actual input channel now that a shell has been explicitly asked for. + set ::tcl_interactive [punk::repl::is_interactive stdin] + repl::init set replresult [repl::start stdin -title make.tcl] #review diff --git a/src/project_layouts/custom/_project/punk.basic/src/make.tcl b/src/project_layouts/custom/_project/punk.basic/src/make.tcl index 02ced58b..e67c6f18 100644 --- a/src/project_layouts/custom/_project/punk.basic/src/make.tcl +++ b/src/project_layouts/custom/_project/punk.basic/src/make.tcl @@ -2131,12 +2131,31 @@ if {$package_paths_modified} { #whether they provide functions such as Loaded, Implementations, SwitchTo set acceleratable [list sha1 md5] lappend acceleratable {*}[lsearch -all -inline [package names] struct::*] +set acceleratable_reload [list] foreach p $acceleratable { + if {[package provide $p] ne ""} { + #was actually loaded (not merely indexed) - must be re-required below or consumers + #that already required it are left calling a destroyed/forgotten command + lappend acceleratable_reload $p + } package forget $p if {[string match struct::* $p]} { catch {rename $p ""} } } +#Re-require whatever was loaded before the forget. The forget/rename exists so the reload can +#pick up accelerators (e.g tcllibc) from the now-restored full package paths - but the reload +#must actually happen here: when make.tcl runs under a built punk executable the consumers +#(punk::lib, punk::du, flagfilter etc) were already loaded at kit boot, so no later +#'package require' chain re-fires for these and e.g raw-mode repl paths would hit +#'invalid command name struct::set'. (Under plain tclsh the shell-branch loads happened to +#re-require them - relying on that was load-order luck, not design.) +foreach p $acceleratable_reload { + if {[catch {package require $p} _accelerr]} { + puts stderr "make.tcl: failed to reload package $p after accelerator-reload forget ($_accelerr) - its commands are unavailable for the rest of this run" + } +} +unset -nocomplain acceleratable_reload _accelerr # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- @@ -2393,6 +2412,13 @@ if {$::punkboot::command eq "shell"} { set ::argv [lrange $::argv 1 end] incr ::argc -1 + #When make.tcl runs under a built punk executable, app-punkscript set + #::tcl_interactive 0 (correct for script execution) and punk::repl was already loaded + #at kit boot - the package require above re-loads nothing, so the repl's load-time + #interactivity probe never re-ran and prompts would stay suppressed on a real console. + #Recompute against the actual input channel now that a shell has been explicitly asked for. + set ::tcl_interactive [punk::repl::is_interactive stdin] + repl::init set replresult [repl::start stdin -title make.tcl] #review diff --git a/src/project_layouts/custom/_project/punk.project-0.1/src/make.tcl b/src/project_layouts/custom/_project/punk.project-0.1/src/make.tcl index 02ced58b..e67c6f18 100644 --- a/src/project_layouts/custom/_project/punk.project-0.1/src/make.tcl +++ b/src/project_layouts/custom/_project/punk.project-0.1/src/make.tcl @@ -2131,12 +2131,31 @@ if {$package_paths_modified} { #whether they provide functions such as Loaded, Implementations, SwitchTo set acceleratable [list sha1 md5] lappend acceleratable {*}[lsearch -all -inline [package names] struct::*] +set acceleratable_reload [list] foreach p $acceleratable { + if {[package provide $p] ne ""} { + #was actually loaded (not merely indexed) - must be re-required below or consumers + #that already required it are left calling a destroyed/forgotten command + lappend acceleratable_reload $p + } package forget $p if {[string match struct::* $p]} { catch {rename $p ""} } } +#Re-require whatever was loaded before the forget. The forget/rename exists so the reload can +#pick up accelerators (e.g tcllibc) from the now-restored full package paths - but the reload +#must actually happen here: when make.tcl runs under a built punk executable the consumers +#(punk::lib, punk::du, flagfilter etc) were already loaded at kit boot, so no later +#'package require' chain re-fires for these and e.g raw-mode repl paths would hit +#'invalid command name struct::set'. (Under plain tclsh the shell-branch loads happened to +#re-require them - relying on that was load-order luck, not design.) +foreach p $acceleratable_reload { + if {[catch {package require $p} _accelerr]} { + puts stderr "make.tcl: failed to reload package $p after accelerator-reload forget ($_accelerr) - its commands are unavailable for the rest of this run" + } +} +unset -nocomplain acceleratable_reload _accelerr # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- @@ -2393,6 +2412,13 @@ if {$::punkboot::command eq "shell"} { set ::argv [lrange $::argv 1 end] incr ::argc -1 + #When make.tcl runs under a built punk executable, app-punkscript set + #::tcl_interactive 0 (correct for script execution) and punk::repl was already loaded + #at kit boot - the package require above re-loads nothing, so the repl's load-time + #interactivity probe never re-ran and prompts would stay suppressed on a real console. + #Recompute against the actual input channel now that a shell has been explicitly asked for. + set ::tcl_interactive [punk::repl::is_interactive stdin] + repl::init set replresult [repl::start stdin -title make.tcl] #review diff --git a/src/project_layouts/custom/_project/punk.shell-0.1/src/make.tcl b/src/project_layouts/custom/_project/punk.shell-0.1/src/make.tcl index 02ced58b..e67c6f18 100644 --- a/src/project_layouts/custom/_project/punk.shell-0.1/src/make.tcl +++ b/src/project_layouts/custom/_project/punk.shell-0.1/src/make.tcl @@ -2131,12 +2131,31 @@ if {$package_paths_modified} { #whether they provide functions such as Loaded, Implementations, SwitchTo set acceleratable [list sha1 md5] lappend acceleratable {*}[lsearch -all -inline [package names] struct::*] +set acceleratable_reload [list] foreach p $acceleratable { + if {[package provide $p] ne ""} { + #was actually loaded (not merely indexed) - must be re-required below or consumers + #that already required it are left calling a destroyed/forgotten command + lappend acceleratable_reload $p + } package forget $p if {[string match struct::* $p]} { catch {rename $p ""} } } +#Re-require whatever was loaded before the forget. The forget/rename exists so the reload can +#pick up accelerators (e.g tcllibc) from the now-restored full package paths - but the reload +#must actually happen here: when make.tcl runs under a built punk executable the consumers +#(punk::lib, punk::du, flagfilter etc) were already loaded at kit boot, so no later +#'package require' chain re-fires for these and e.g raw-mode repl paths would hit +#'invalid command name struct::set'. (Under plain tclsh the shell-branch loads happened to +#re-require them - relying on that was load-order luck, not design.) +foreach p $acceleratable_reload { + if {[catch {package require $p} _accelerr]} { + puts stderr "make.tcl: failed to reload package $p after accelerator-reload forget ($_accelerr) - its commands are unavailable for the rest of this run" + } +} +unset -nocomplain acceleratable_reload _accelerr # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- @@ -2393,6 +2412,13 @@ if {$::punkboot::command eq "shell"} { set ::argv [lrange $::argv 1 end] incr ::argc -1 + #When make.tcl runs under a built punk executable, app-punkscript set + #::tcl_interactive 0 (correct for script execution) and punk::repl was already loaded + #at kit boot - the package require above re-loads nothing, so the repl's load-time + #interactivity probe never re-ran and prompts would stay suppressed on a real console. + #Recompute against the actual input channel now that a shell has been explicitly asked for. + set ::tcl_interactive [punk::repl::is_interactive stdin] + repl::init set replresult [repl::start stdin -title make.tcl] #review