From a76e73db3122050c5064d1e70fd2888aed5effe5 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Wed, 22 Jul 2026 15:41:14 +1000 Subject: [PATCH] punk-runtime: toml-aware list -remote for the published family artifacts Publication side-effects handled ahead of the punkbin push (b5c258f there - first family -r1 artifacts + metadata + defaults flip to tclsh9.0.5-punk-r1.exe): - remote-only rows skip support files (the six metadata tomls now ride in the server's sha1sums beside the runtimes; same extension set as the local candidate filter). - the '(= server default)' active marker also matches via the active's beside-toml artifact identity: the default names an immutable -r artifact while the active is typically its materialized WORKING name, so exact-name matching alone would never fire post-family. Live-verified against the pushed server: no-name fetch resolves tclsh9.0.5-punk-r1.exe + metadata; list -remote shows all three -r1 artifacts Same version, the default row marked, active tclsh9.0.5-punk.exe annotated '(= server default)' through its toml identity, and no toml leakage. G-067 note records publication done (r1 immutable - next republish takes -Dfamilyrev=2). Rewrapped; roundtrip pin PASS; layout copy refreshed. Project 0.18.8. Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com --- CHANGELOG.md | 4 ++ bin/punk-runtime.cmd | 38 ++++++++++++++++++- goals/G-067-module-artifact-channel.md | 18 +++++---- punkproject.toml | 2 +- .../punk/project-0.1/bin/punk-runtime.cmd | 38 ++++++++++++++++++- src/scriptapps/bin/punk-runtime.bash | 19 +++++++++- src/scriptapps/bin/punk-runtime.ps1 | 19 +++++++++- 7 files changed, 123 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3bd68b2..c49d57c9 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.18.8] - 2026-07-22 + +- `bin/punk-runtime.cmd` `list -remote`: remote-only rows skip support files (artifact metadata tomls etc now ride in the server's sha1sums beside runtimes - same extension set the local candidate filter excludes); the `(= server default)` active marker also recognizes a materialized working copy via its beside-toml artifact identity (the default names an immutable `-r` artifact while the active is typically its working name - exact-name matching alone would never fire post-family). Verified live against the first published family artifacts. + ## [0.18.7] - 2026-07-22 - `bin/punk-runtime.cmd`: deterministic listing order everywhere. Investigation of an ordering discrepancy between launch paths found two edition divergences: the `.ps1` twin runs under the INVOKING powershell (pwsh 7) while the `.cmd` routes via wrap-pinned Windows PowerShell 5, and both Hashtable enumeration order AND culture-sensitive `Sort-Object` collation (NLS vs ICU) differ between those editions. All name orderings now use ordinal comparison (ps1) / `LC_ALL=C` (bash), which agree byte-for-byte across pwsh, powershell and bash. Also corrected `bin/AGENTS.md`: the `.ps1` twin is SELF-MATERIALIZED by the polyglot's batch layer (created when missing, `fc`-compared and re-copied when stale) - the previous "refresh both on re-wrap" manual-copy guidance was stale; twin verified regenerating byte-identical after deletion. diff --git a/bin/punk-runtime.cmd b/bin/punk-runtime.cmd index 229b4c6f..d61612b4 100755 --- a/bin/punk-runtime.cmd +++ b/bin/punk-runtime.cmd @@ -1747,8 +1747,19 @@ case "$action" in echo "server default for $archtail: $platform_default" fi if [[ -n "$activename" ]]; then + #the default names an immutable -r ARTIFACT while the active is + #typically its materialized WORKING name - the beside-toml's 'name' + #field records which artifact the working copy came from, so match + #on either identity + active_artifact="" + activetoml="$archdir/$(rootname_of "$activename").toml" + if [[ -f "$activetoml" ]]; then + active_artifact=$(sed -n 's/^[[:space:]]*name[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$activetoml" | head -n 1) + fi matchnote="" - [[ -n "$platform_default" && "$activename" == "$platform_default" ]] && matchnote=" (= server default)" + if [[ -n "$platform_default" ]] && [[ "$activename" == "$platform_default" || "$active_artifact" == "$platform_default" ]]; then + matchnote=" (= server default)" + fi echo "active (local): $activename$matchnote" fi echo "-----------------------------------------------------------------------" @@ -1775,6 +1786,12 @@ case "$action" in #mode but bash read does not - without this a locally-present runtime #also shows as a remote-only row (\r-suffixed name fails the -f test) tr -d '\r' < "$sha1local" | sed -n 's/^[0-9a-fA-F]\{40\} \*\(.*\)$/\1/p' | LC_ALL=C sort | while IFS= read -r rname; do + #skip support files riding in the server's sha1sums beside the + #runtimes (artifact metadata tomls etc) - same extension set the + #local candidate filter excludes + case "$rname" in + *.txt|*.toml|*.tm|*.tmp|*.log) continue;; + esac if [[ -n "$rname" && ! -f "$archdir/$rname" ]]; then annot="" [[ -n "$platform_default" && "$rname" == "$platform_default" ]] && annot=" (server default)" @@ -3063,8 +3080,20 @@ function psmain { Write-host "server default for ${arch}: $platform_default" } if ($activename -ne "") { + #the default names an immutable -r ARTIFACT while the active + #is typically its materialized WORKING name - the beside-toml's + #'name' field records which artifact the working copy came from, + #so match on either identity + $active_artifact = "" + $activetoml = Join-Path -Path $archfolder -ChildPath ((Get-PunkRuntimeRootName $activename) + ".toml") + if (Test-Path -Path $activetoml -PathType Leaf) { + foreach ($tline in (Get-Content -Path $activetoml)) { + $m = [regex]::Match($tline, '^\s*name\s*=\s*"(.*)"\s*$') + if ($m.Success) { $active_artifact = $m.Groups[1].Value; break } + } + } $matchnote = "" - if ($platform_default -ne "" -and $activename -eq $platform_default) { + if ($platform_default -ne "" -and ($activename -eq $platform_default -or $active_artifact -eq $platform_default)) { $matchnote = " (= server default)" } Write-host "active (local): $activename$matchnote" @@ -3113,6 +3142,11 @@ function psmain { [array]::Sort($remotekeys, [System.StringComparer]::Ordinal) foreach ($key in $remotekeys) { if (-not ($localdict.ContainsKey($key))) { + #skip support files riding in the server's sha1sums beside + #the runtimes (artifact metadata tomls etc) - same extension + #set the local candidate filter excludes + $rext = [System.IO.Path]::GetExtension($key) + if ($(".txt",".toml",".tm",".tmp",".log") -contains $rext) { continue } $annot = "" if ($platform_default -ne "" -and $key -eq $platform_default) { $annot = " (server default)" } write-host -nonewline " $lhs_missing" diff --git a/goals/G-067-module-artifact-channel.md b/goals/G-067-module-artifact-channel.md index 2ce659f3..a499ac73 100644 --- a/goals/G-067-module-artifact-channel.md +++ b/goals/G-067-module-artifact-channel.md @@ -44,14 +44,16 @@ and pull (G-065) instead of re-vendoring by hand. (punkshell-only, must be documented). Generation + multi-name discovery detail: G-066 Notes. - 2026-07-22 (from archived G-103 - see goals/archive/G-103-runtime-kit-family.md): - the family runtime -rN artifacts + toml metadata + sha1sums now EXIST - (suite kit-family-artifacts emission, punkbin-layout shaped) and their punkbin - publication is a pending USER-GATED step - the archived goal's deferral - condition ("publication follows this goal's artifacts") is satisfied. When it - happens: copy the emission into punkbin win32-x86_64/, run build_sha1sums.tcl, - flip defaults.txt's win32-x86_64 line in the same change-set (the recorded - release process). Those runtime artifacts are punkbin's FIRST metadata-carrying - class; this goal's library class follows the same per-platform + toml pattern. + the family runtime -rN artifacts + toml metadata are PUBLISHED (2026-07-22, + user-directed; punkbin b5c258f pushed): tclsh9.0.5-r1/-punk-r1/-punk-bi-r1 + .exe+.toml in win32-x86_64/, sha1sums regenerated, defaults.txt flipped to + tclsh9.0.5-punk-r1.exe in the same change-set (the recorded release process, + exercised for real). r1 is now immutable - republishing those (patchlevel, + variant) pairs needs -Dfamilyrev=2. Live-verified: no-name fetch resolves the + new default with metadata; list -remote marks the default row and recognizes + the materialized active via its toml artifact identity. These runtime + artifacts are punkbin's FIRST metadata-carrying class; this goal's library + class follows the same per-platform + toml pattern. - 2026-07-22 (user framing): the zig BUILDSUITES are a producing source for this channel - libraries the suites build (thread/tk/tcltls-class) get packaged as per-platform .tm/pkg artifacts when they are not going directly into a kit, and diff --git a/punkproject.toml b/punkproject.toml index 493a81dc..481144fb 100644 --- a/punkproject.toml +++ b/punkproject.toml @@ -1,4 +1,4 @@ [project] name = "punkshell" -version = "0.18.7" +version = "0.18.8" license = "BSD-2-Clause" diff --git a/src/project_layouts/vendor/punk/project-0.1/bin/punk-runtime.cmd b/src/project_layouts/vendor/punk/project-0.1/bin/punk-runtime.cmd index 229b4c6f..d61612b4 100644 --- a/src/project_layouts/vendor/punk/project-0.1/bin/punk-runtime.cmd +++ b/src/project_layouts/vendor/punk/project-0.1/bin/punk-runtime.cmd @@ -1747,8 +1747,19 @@ case "$action" in echo "server default for $archtail: $platform_default" fi if [[ -n "$activename" ]]; then + #the default names an immutable -r ARTIFACT while the active is + #typically its materialized WORKING name - the beside-toml's 'name' + #field records which artifact the working copy came from, so match + #on either identity + active_artifact="" + activetoml="$archdir/$(rootname_of "$activename").toml" + if [[ -f "$activetoml" ]]; then + active_artifact=$(sed -n 's/^[[:space:]]*name[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$activetoml" | head -n 1) + fi matchnote="" - [[ -n "$platform_default" && "$activename" == "$platform_default" ]] && matchnote=" (= server default)" + if [[ -n "$platform_default" ]] && [[ "$activename" == "$platform_default" || "$active_artifact" == "$platform_default" ]]; then + matchnote=" (= server default)" + fi echo "active (local): $activename$matchnote" fi echo "-----------------------------------------------------------------------" @@ -1775,6 +1786,12 @@ case "$action" in #mode but bash read does not - without this a locally-present runtime #also shows as a remote-only row (\r-suffixed name fails the -f test) tr -d '\r' < "$sha1local" | sed -n 's/^[0-9a-fA-F]\{40\} \*\(.*\)$/\1/p' | LC_ALL=C sort | while IFS= read -r rname; do + #skip support files riding in the server's sha1sums beside the + #runtimes (artifact metadata tomls etc) - same extension set the + #local candidate filter excludes + case "$rname" in + *.txt|*.toml|*.tm|*.tmp|*.log) continue;; + esac if [[ -n "$rname" && ! -f "$archdir/$rname" ]]; then annot="" [[ -n "$platform_default" && "$rname" == "$platform_default" ]] && annot=" (server default)" @@ -3063,8 +3080,20 @@ function psmain { Write-host "server default for ${arch}: $platform_default" } if ($activename -ne "") { + #the default names an immutable -r ARTIFACT while the active + #is typically its materialized WORKING name - the beside-toml's + #'name' field records which artifact the working copy came from, + #so match on either identity + $active_artifact = "" + $activetoml = Join-Path -Path $archfolder -ChildPath ((Get-PunkRuntimeRootName $activename) + ".toml") + if (Test-Path -Path $activetoml -PathType Leaf) { + foreach ($tline in (Get-Content -Path $activetoml)) { + $m = [regex]::Match($tline, '^\s*name\s*=\s*"(.*)"\s*$') + if ($m.Success) { $active_artifact = $m.Groups[1].Value; break } + } + } $matchnote = "" - if ($platform_default -ne "" -and $activename -eq $platform_default) { + if ($platform_default -ne "" -and ($activename -eq $platform_default -or $active_artifact -eq $platform_default)) { $matchnote = " (= server default)" } Write-host "active (local): $activename$matchnote" @@ -3113,6 +3142,11 @@ function psmain { [array]::Sort($remotekeys, [System.StringComparer]::Ordinal) foreach ($key in $remotekeys) { if (-not ($localdict.ContainsKey($key))) { + #skip support files riding in the server's sha1sums beside + #the runtimes (artifact metadata tomls etc) - same extension + #set the local candidate filter excludes + $rext = [System.IO.Path]::GetExtension($key) + if ($(".txt",".toml",".tm",".tmp",".log") -contains $rext) { continue } $annot = "" if ($platform_default -ne "" -and $key -eq $platform_default) { $annot = " (server default)" } write-host -nonewline " $lhs_missing" diff --git a/src/scriptapps/bin/punk-runtime.bash b/src/scriptapps/bin/punk-runtime.bash index 2fcf54bd..83015060 100644 --- a/src/scriptapps/bin/punk-runtime.bash +++ b/src/scriptapps/bin/punk-runtime.bash @@ -467,8 +467,19 @@ case "$action" in echo "server default for $archtail: $platform_default" fi if [[ -n "$activename" ]]; then + #the default names an immutable -r ARTIFACT while the active is + #typically its materialized WORKING name - the beside-toml's 'name' + #field records which artifact the working copy came from, so match + #on either identity + active_artifact="" + activetoml="$archdir/$(rootname_of "$activename").toml" + if [[ -f "$activetoml" ]]; then + active_artifact=$(sed -n 's/^[[:space:]]*name[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$activetoml" | head -n 1) + fi matchnote="" - [[ -n "$platform_default" && "$activename" == "$platform_default" ]] && matchnote=" (= server default)" + if [[ -n "$platform_default" ]] && [[ "$activename" == "$platform_default" || "$active_artifact" == "$platform_default" ]]; then + matchnote=" (= server default)" + fi echo "active (local): $activename$matchnote" fi echo "-----------------------------------------------------------------------" @@ -495,6 +506,12 @@ case "$action" in #mode but bash read does not - without this a locally-present runtime #also shows as a remote-only row (\r-suffixed name fails the -f test) tr -d '\r' < "$sha1local" | sed -n 's/^[0-9a-fA-F]\{40\} \*\(.*\)$/\1/p' | LC_ALL=C sort | while IFS= read -r rname; do + #skip support files riding in the server's sha1sums beside the + #runtimes (artifact metadata tomls etc) - same extension set the + #local candidate filter excludes + case "$rname" in + *.txt|*.toml|*.tm|*.tmp|*.log) continue;; + esac if [[ -n "$rname" && ! -f "$archdir/$rname" ]]; then annot="" [[ -n "$platform_default" && "$rname" == "$platform_default" ]] && annot=" (server default)" diff --git a/src/scriptapps/bin/punk-runtime.ps1 b/src/scriptapps/bin/punk-runtime.ps1 index 57a583ab..090ffffa 100644 --- a/src/scriptapps/bin/punk-runtime.ps1 +++ b/src/scriptapps/bin/punk-runtime.ps1 @@ -766,8 +766,20 @@ function psmain { Write-host "server default for ${arch}: $platform_default" } if ($activename -ne "") { + #the default names an immutable -r ARTIFACT while the active + #is typically its materialized WORKING name - the beside-toml's + #'name' field records which artifact the working copy came from, + #so match on either identity + $active_artifact = "" + $activetoml = Join-Path -Path $archfolder -ChildPath ((Get-PunkRuntimeRootName $activename) + ".toml") + if (Test-Path -Path $activetoml -PathType Leaf) { + foreach ($tline in (Get-Content -Path $activetoml)) { + $m = [regex]::Match($tline, '^\s*name\s*=\s*"(.*)"\s*$') + if ($m.Success) { $active_artifact = $m.Groups[1].Value; break } + } + } $matchnote = "" - if ($platform_default -ne "" -and $activename -eq $platform_default) { + if ($platform_default -ne "" -and ($activename -eq $platform_default -or $active_artifact -eq $platform_default)) { $matchnote = " (= server default)" } Write-host "active (local): $activename$matchnote" @@ -816,6 +828,11 @@ function psmain { [array]::Sort($remotekeys, [System.StringComparer]::Ordinal) foreach ($key in $remotekeys) { if (-not ($localdict.ContainsKey($key))) { + #skip support files riding in the server's sha1sums beside + #the runtimes (artifact metadata tomls etc) - same extension + #set the local candidate filter excludes + $rext = [System.IO.Path]::GetExtension($key) + if ($(".txt",".toml",".tm",".tmp",".log") -contains $rext) { continue } $annot = "" if ($platform_default -ne "" -and $key -eq $platform_default) { $annot = " (server default)" } write-host -nonewline " $lhs_missing"