Browse Source

punk-runtime list -remote identity columns (G-119 follow-on, user-directed)

The starred active row previously never changed across 'use' switches
(every -rN materializes to the same working name) and carried the
name-keyed dead end '(not listed on server)' - true of the name,
unexplanatory for a working copy whose bytes match a server artifact.

Rows the server listing does not name now carry an untitled identity
middle column: from=<artifact> for materialized working copies (the
beside-toml source; content verified against that artifact's server
sha1 so the Remote cell shows a real status - Same version, CONTENT
DIFFERS for the immutable-artifact anomaly, or '(artifact not listed
on server)'), sha1=<name> for tomlless byte-copies (default-first-
then-ordinal pick, the same rule as the freshness fallback so row
identity and freshness verdict always name the same artifact).
'(not listed on server)' remains only for genuinely unknown files;
the (server default) row annotation follows artifact identity onto
working copies of the default. Table header/rules widened to the
three-column layout, payload row formats now column-aligned across
ps1/bash; help text updated; bin/punk-runtime.cmd rewrapped
(checkfile clean).

Chosen shape (user-approved from mockup variants): untitled middle
column with self-keyed from=/sha1= tags - consistent with the plain
list metadata summary's from= vocabulary and self-describing when a
row is pasted without its header.

runtimecmd_freshness.test +3 row-shape tests (identity columns, the
identity-following default annotation, anomaly statuses) with ps1/bash
parity: 10/10 pass; scriptwrap + binscripts battery 35 pass /
1 pre-existing skip / 0 fail (roundtrip + checkfile pins green, WSL
row pins unaffected). bin/AGENTS.md + shell tests AGENTS updated.
punkshell 0.22.0.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 7 days ago
parent
commit
19c63ac60e
  1. 12
      CHANGELOG.md
  2. 15
      bin/AGENTS.md
  3. 171
      bin/punk-runtime.cmd
  4. 2
      punkproject.toml
  5. 85
      src/scriptapps/bin/punk-runtime.bash
  6. 86
      src/scriptapps/bin/punk-runtime.ps1
  7. 2
      src/tests/shell/AGENTS.md
  8. 79
      src/tests/shell/testsuites/binscripts/runtimecmd_freshness.test

12
CHANGELOG.md

@ -5,6 +5,18 @@ 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.22.0] - 2026-07-25
- punk-runtime `list -remote` identity columns (G-119 follow-on; both payloads + rewrapped
`bin/punk-runtime.cmd`): rows the server listing does not name carry an untitled middle
column - `from=<artifact>` for materialized working copies (content verified against the
source artifact's server sha1, so the Remote cell shows a real status: Same version /
CONTENT DIFFERS / artifact-not-listed) and `sha1=<name>` for byte-copies of server
artifacts. The starred active row now changes with every `use` switch instead of sitting
on the dead-end "(not listed on server)" (which now means genuinely unknown), and the
`(server default)` row annotation follows artifact identity. Table header/rules widened
to the three-column layout.
## [0.21.0] - 2026-07-25
- punk-runtime freshness verdict (G-119; both payloads + rewrapped `bin/punk-runtime.cmd`):

15
bin/AGENTS.md

@ -154,7 +154,20 @@ first ordinal/C-collation match wins in both payloads). A NO-NAME `fetch` (the
active diverges from the resolved default - silent when current, and BEHIND carries
an inline `use <default>` hint since the artifact was just fetched. Verdict lines
are byte-identical across the payloads; only the guidance lines carry each payload's
program-name convention. Pinned by
program-name convention. Identity middle column (2026-07-25 follow-on, user-decided
shape): `list -remote` rows the server listing does not name carry an UNTITLED
middle column - `from=<artifact>` for a materialized working copy (its beside-toml
source artifact; content is then verified against that artifact's server sha1, so
the Remote cell shows a real status: `Same version`, `CONTENT DIFFERS` for the
immutable-artifact anomaly, or `(artifact not listed on server)`), and
`sha1=<name>` for a tomlless byte-copy of a server artifact (default-first-then-
ordinal pick - the same rule as the freshness fallback, so row identity and verdict
always name the same artifact). `(not listed on server)` remains only for genuinely
unidentified files, and the `(server default)` row annotation follows artifact
identity (a working copy of the default carries it). Rationale: the starred active
row is the salient element - it must change with every `use` switch and teach the
working-copy mechanics, and column position carries meaning (identity phrasing
inside the Remote column would read as a server fact). Pinned by
`src/tests/shell/testsuites/binscripts/runtimecmd_freshness.test` against a fixture
punkbin server (`PUNKBIN_URL` override + `src/tests/testsupport/httpfixture.tcl`).

171
bin/punk-runtime.cmd

@ -1543,6 +1543,11 @@ show_help() {
echo " visible at a glance. When both are known, a freshness verdict"
echo " states current/ahead/behind/incomparable explicitly (revision"
echo " comparison, sha1 identity fallback; update guidance on behind)."
echo " Rows the server listing does not name carry an identity column:"
echo " from=<artifact> for a materialized working copy (content verified"
echo " against that artifact), sha1=<name> for a byte-copy of a server"
echo " artifact - only genuinely unknown files stay '(not listed on"
echo " server)'."
echo " use <name> ?-platform <p>?"
echo " Select the runtime that 'run' launches (records active.toml in"
echo " the platform folder). An immutable -r<N> ARTIFACT name is"
@ -1874,7 +1879,7 @@ case "$action" in
if [[ -f "$defaultslocal" ]]; then
platform_default=$(awk -v p="$archtail" '$1==p {print $2; exit}' "$defaultslocal")
fi
echo "-----------------------------------------------------------------------"
echo "----------------------------------------------------------------------------------------"
echo "Runtimes for $archtail"
echo "Local $archdir"
echo "Remote ${url_kitbase}/${archtail}"
@ -1901,24 +1906,78 @@ case "$action" in
#annotation above already says so)
freshness_verdict "$activename" "$platform_default" "$sha1local" list
fi
echo "-----------------------------------------------------------------------"
echo " Local Remote"
echo "-----------------------------------------------------------------------"
echo "----------------------------------------------------------------------------------------"
printf ' %-35s %-33s %s\n' "Local" "" "Remote"
echo "----------------------------------------------------------------------------------------"
for f in $(list_candidates); do
local_sha1=$(lcase "$(sha1_of "$archdir/$f")")
stored_sha1=$(grep -E "^[0-9a-fA-F]{40} \*${f}\$" "$sha1local" | head -n 1 | cut -d' ' -f1)
if [[ -z "$stored_sha1" ]]; then
rhs="(not listed on server)"
elif [[ "$local_sha1" == "$(lcase "$stored_sha1")" ]]; then
rhs="Same version"
mid=""
ident=""
if [[ -n "$stored_sha1" ]]; then
if [[ "$local_sha1" == "$(lcase "$stored_sha1")" ]]; then
rhs="Same version"
else
rhs="UPDATE AVAILABLE"
fi
else
rhs="UPDATE AVAILABLE"
#G-119 refinement: identity middle column for rows the server
#listing does not name. A materialized working copy is identified
#by its beside-toml source artifact (from=) with its CONTENT
#verified against that artifact's server sha1; a tomlless stray by
#sha1 VALUE-match (sha1=). The Remote column then carries a real
#server relationship for such rows instead of the dead-end
#'(not listed on server)', which now means genuinely unknown.
rowtoml="$archdir/$(rootname_of "$f").toml"
artname=""
if [[ -f "$rowtoml" ]]; then
artname=$(sed -n 's/^[[:space:]]*name[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$rowtoml" | head -n 1)
[[ "$artname" == "$f" ]] && artname=""
fi
if [[ -n "$artname" ]]; then
mid="from=$artname"
ident="$artname"
artsha=$(lcase "$(tr -d '\r' < "$sha1local" | grep -E "^[0-9a-fA-F]{40} \*${artname}\$" | head -n 1 | cut -d' ' -f1)")
if [[ -z "$artsha" ]]; then
rhs="(artifact not listed on server)"
elif [[ "$local_sha1" == "$artsha" ]]; then
rhs="Same version"
else
#-r<N> artifacts are immutable - differing content means a
#modified local copy or a mismatched sidecar, not an update
rhs="CONTENT DIFFERS"
fi
else
#sha1 value-match: the default's row first, else the first
#C-collation match with support files excluded - the same pick
#rule as freshness_verdict, so the row identity and the
#freshness line always name the same artifact
vname=""
if [[ -n "$platform_default" ]]; then
dsha=$(lcase "$(tr -d '\r' < "$sha1local" | grep -E "^[0-9a-fA-F]{40} \*${platform_default}\$" | head -n 1 | cut -d' ' -f1)")
[[ -n "$dsha" && "$local_sha1" == "$dsha" ]] && vname="$platform_default"
fi
if [[ -z "$vname" ]]; then
vname=$(tr -d '\r' < "$sha1local" | awk -v h="$local_sha1" '{n=$2; sub(/^\*/,"",n); if (tolower($1)==h) print n}' | grep -Ev '\.(txt|toml|tm|tmp|log)$' | LC_ALL=C sort | head -n 1)
fi
if [[ -n "$vname" ]]; then
mid="sha1=$vname"
ident="$vname"
rhs="Same version"
else
rhs="(not listed on server)"
fi
fi
fi
mark=" "
[[ "$f" == "$activename" ]] && mark="* "
annot=""
[[ -n "$platform_default" && "$f" == "$platform_default" ]] && annot=" (server default)"
printf '%s%-35s %s%s\n' "$mark" "$f" "$rhs" "$annot"
#the (server default) annotation follows artifact identity: a working
#copy of (or byte-copy of) the default artifact is the default
if [[ -n "$platform_default" ]] && [[ "$f" == "$platform_default" || "$ident" == "$platform_default" ]]; then
annot=" (server default)"
fi
printf '%s%-35s %-33s %s%s\n' "$mark" "$f" "$mid" "$rhs" "$annot"
done
#remote-only entries, sorted (ps1-payload parity - its dict enumeration
#is explicitly sorted). tr strips CRLF \r: msys grep strips it in text
@ -1934,10 +1993,10 @@ case "$action" in
if [[ -n "$rname" && ! -f "$archdir/$rname" ]]; then
annot=""
[[ -n "$platform_default" && "$rname" == "$platform_default" ]] && annot=" (server default)"
printf ' %-35s %s%s\n' "-" "$rname" "$annot"
printf ' %-35s %-33s %s%s\n' "-" "" "$rname" "$annot"
fi
done
echo "-----------------------------------------------------------------------"
echo "----------------------------------------------------------------------------------------"
echo "* = active (local selection)"
else
if [[ -d "$archdir" ]]; then
@ -2667,6 +2726,11 @@ function Show-PunkRuntimeHelp {
write-host " visible at a glance. When both are known, a freshness verdict"
write-host " states current/ahead/behind/incomparable explicitly (revision"
write-host " comparison, sha1 identity fallback; update guidance on behind)."
write-host " Rows the server listing does not name carry an identity column:"
write-host " from=<artifact> for a materialized working copy (content verified"
write-host " against that artifact), sha1=<name> for a byte-copy of a server"
write-host " artifact - only genuinely unknown files stay '(not listed on"
write-host " server)'."
write-host " use <name> ?-platform <p>?"
write-host " Select the runtime that 'run' launches (records active.toml in"
write-host " the platform folder). An immutable -r<N> ARTIFACT name is"
@ -3603,7 +3667,7 @@ function psmain {
}
}
}
Write-host "-----------------------------------------------------------------------"
Write-host "----------------------------------------------------------------------------------------"
Write-host "Runtimes for $arch"
Write-host "Local $archfolder"
Write-host "Remote $archurl"
@ -3633,9 +3697,9 @@ function psmain {
#annotation above already says so)
Write-PunkFreshnessVerdict -archfolder $archfolder -activename $activename -defaultname $platform_default -sha1local $sha1local -arch $arch -mode 'list'
}
Write-host "-----------------------------------------------------------------------"
Write-host " Local Remote"
Write-host "-----------------------------------------------------------------------"
Write-host "----------------------------------------------------------------------------------------"
Write-host (" {0,-35} {1,-33} {2}" -f "Local", "", "Remote")
Write-host "----------------------------------------------------------------------------------------"
# 12345678910234567892023456789302345
$G = "`e[32m" #Green
$Y = "`e[33m" #Yellow
@ -3649,6 +3713,8 @@ function psmain {
[array]::Sort($localkeys, [System.StringComparer]::Ordinal)
foreach ($key in $localkeys) {
$local_sha1 = $($localdict[$key])
$mid = ""
$ident = ""
if ($remotedict.ContainsKey($key)) {
if ($local_sha1 -eq $remotedict[$key]) {
$rhs = "Same version"
@ -3659,7 +3725,63 @@ function psmain {
}
} else {
$C = $R
$rhs = "(not listed on server)"
#G-119 refinement: identity middle column for rows the server
#listing does not name. A materialized working copy is
#identified by its beside-toml source artifact (from=) with
#its CONTENT verified against that artifact's server sha1; a
#tomlless stray by sha1 VALUE-match (sha1=). The Remote
#column then carries a real server relationship for such
#rows instead of the dead-end '(not listed on server)',
#which now means genuinely unknown.
$rowtoml = Join-Path -Path $archfolder -ChildPath ((Get-PunkRuntimeRootName $key) + ".toml")
$artname = ""
if (Test-Path -Path $rowtoml -PathType Leaf) {
foreach ($tline in (Get-Content -Path $rowtoml)) {
$m = [regex]::Match($tline, '^\s*name\s*=\s*"(.*)"\s*$')
if ($m.Success) { $artname = $m.Groups[1].Value; break }
}
if ($artname -eq $key) { $artname = "" }
}
if ($artname -ne "") {
$mid = "from=$artname"
$ident = $artname
if (-not $remotedict.ContainsKey($artname)) {
$rhs = "(artifact not listed on server)"
} elseif ($local_sha1 -eq $remotedict[$artname]) {
$rhs = "Same version"
} else {
#-r<N> artifacts are immutable - differing content
#means a modified local copy or a mismatched
#sidecar, not an update
$rhs = "CONTENT DIFFERS"
}
} else {
#sha1 value-match: the default's row first, else the
#first ordinal match with support files excluded - the
#same pick rule as Write-PunkFreshnessVerdict, so the
#row identity and the freshness line always name the
#same artifact
$vname = ""
if ($platform_default -ne "" -and $remotedict.ContainsKey($platform_default) -and $remotedict[$platform_default] -eq $local_sha1) {
$vname = $platform_default
}
if ($vname -eq "") {
$rnames = @($remotedict.Keys)
[array]::Sort($rnames, [System.StringComparer]::Ordinal)
foreach ($rn in $rnames) {
$rext = [System.IO.Path]::GetExtension($rn)
if ($(".txt",".toml",".tm",".tmp",".log") -contains $rext) { continue }
if ($remotedict[$rn] -eq $local_sha1) { $vname = $rn; break }
}
}
if ($vname -ne "") {
$mid = "sha1=$vname"
$ident = $vname
$rhs = "Same version"
} else {
$rhs = "(not listed on server)"
}
}
}
#ansi problems from cmd.exe not in windows terminal - review
$C = ""
@ -3667,12 +3789,15 @@ function psmain {
$mark = " "
if ($key -eq $activename) { $mark = "* " }
$annot = ""
if ($platform_default -ne "" -and $key -eq $platform_default) { $annot = " (server default)" }
#the (server default) annotation follows artifact identity: a
#working copy of (or byte-copy of) the default artifact is the
#default
if ($platform_default -ne "" -and ($key -eq $platform_default -or $ident -eq $platform_default)) { $annot = " (server default)" }
$lhs = "$key".PadRight(35, ' ')
write-host -nonewline "${mark}${C}${lhs}${RST}"
$midp = "$mid".PadRight(33, ' ')
write-host -nonewline "${mark}${C}${lhs}${RST} ${midp} "
write-host "$rhs$annot"
}
$lhs_missing = "-".PadRight(35, ' ')
$remotekeys = @($remotedict.Keys)
[array]::Sort($remotekeys, [System.StringComparer]::Ordinal)
foreach ($key in $remotekeys) {
@ -3684,11 +3809,11 @@ function psmain {
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"
write-host -nonewline (" {0,-35} {1,-33} " -f "-", "")
write-host "$key$annot"
}
}
Write-host "-----------------------------------------------------------------------"
Write-host "----------------------------------------------------------------------------------------"
Write-host "* = active (local selection)"
} else {

2
punkproject.toml

@ -1,4 +1,4 @@
[project]
name = "punkshell"
version = "0.21.0"
version = "0.22.0"
license = "BSD-2-Clause"

85
src/scriptapps/bin/punk-runtime.bash

@ -263,6 +263,11 @@ show_help() {
echo " visible at a glance. When both are known, a freshness verdict"
echo " states current/ahead/behind/incomparable explicitly (revision"
echo " comparison, sha1 identity fallback; update guidance on behind)."
echo " Rows the server listing does not name carry an identity column:"
echo " from=<artifact> for a materialized working copy (content verified"
echo " against that artifact), sha1=<name> for a byte-copy of a server"
echo " artifact - only genuinely unknown files stay '(not listed on"
echo " server)'."
echo " use <name> ?-platform <p>?"
echo " Select the runtime that 'run' launches (records active.toml in"
echo " the platform folder). An immutable -r<N> ARTIFACT name is"
@ -594,7 +599,7 @@ case "$action" in
if [[ -f "$defaultslocal" ]]; then
platform_default=$(awk -v p="$archtail" '$1==p {print $2; exit}' "$defaultslocal")
fi
echo "-----------------------------------------------------------------------"
echo "----------------------------------------------------------------------------------------"
echo "Runtimes for $archtail"
echo "Local $archdir"
echo "Remote ${url_kitbase}/${archtail}"
@ -621,24 +626,78 @@ case "$action" in
#annotation above already says so)
freshness_verdict "$activename" "$platform_default" "$sha1local" list
fi
echo "-----------------------------------------------------------------------"
echo " Local Remote"
echo "-----------------------------------------------------------------------"
echo "----------------------------------------------------------------------------------------"
printf ' %-35s %-33s %s\n' "Local" "" "Remote"
echo "----------------------------------------------------------------------------------------"
for f in $(list_candidates); do
local_sha1=$(lcase "$(sha1_of "$archdir/$f")")
stored_sha1=$(grep -E "^[0-9a-fA-F]{40} \*${f}\$" "$sha1local" | head -n 1 | cut -d' ' -f1)
if [[ -z "$stored_sha1" ]]; then
rhs="(not listed on server)"
elif [[ "$local_sha1" == "$(lcase "$stored_sha1")" ]]; then
rhs="Same version"
mid=""
ident=""
if [[ -n "$stored_sha1" ]]; then
if [[ "$local_sha1" == "$(lcase "$stored_sha1")" ]]; then
rhs="Same version"
else
rhs="UPDATE AVAILABLE"
fi
else
rhs="UPDATE AVAILABLE"
#G-119 refinement: identity middle column for rows the server
#listing does not name. A materialized working copy is identified
#by its beside-toml source artifact (from=) with its CONTENT
#verified against that artifact's server sha1; a tomlless stray by
#sha1 VALUE-match (sha1=). The Remote column then carries a real
#server relationship for such rows instead of the dead-end
#'(not listed on server)', which now means genuinely unknown.
rowtoml="$archdir/$(rootname_of "$f").toml"
artname=""
if [[ -f "$rowtoml" ]]; then
artname=$(sed -n 's/^[[:space:]]*name[[:space:]]*=[[:space:]]*"\(.*\)".*/\1/p' "$rowtoml" | head -n 1)
[[ "$artname" == "$f" ]] && artname=""
fi
if [[ -n "$artname" ]]; then
mid="from=$artname"
ident="$artname"
artsha=$(lcase "$(tr -d '\r' < "$sha1local" | grep -E "^[0-9a-fA-F]{40} \*${artname}\$" | head -n 1 | cut -d' ' -f1)")
if [[ -z "$artsha" ]]; then
rhs="(artifact not listed on server)"
elif [[ "$local_sha1" == "$artsha" ]]; then
rhs="Same version"
else
#-r<N> artifacts are immutable - differing content means a
#modified local copy or a mismatched sidecar, not an update
rhs="CONTENT DIFFERS"
fi
else
#sha1 value-match: the default's row first, else the first
#C-collation match with support files excluded - the same pick
#rule as freshness_verdict, so the row identity and the
#freshness line always name the same artifact
vname=""
if [[ -n "$platform_default" ]]; then
dsha=$(lcase "$(tr -d '\r' < "$sha1local" | grep -E "^[0-9a-fA-F]{40} \*${platform_default}\$" | head -n 1 | cut -d' ' -f1)")
[[ -n "$dsha" && "$local_sha1" == "$dsha" ]] && vname="$platform_default"
fi
if [[ -z "$vname" ]]; then
vname=$(tr -d '\r' < "$sha1local" | awk -v h="$local_sha1" '{n=$2; sub(/^\*/,"",n); if (tolower($1)==h) print n}' | grep -Ev '\.(txt|toml|tm|tmp|log)$' | LC_ALL=C sort | head -n 1)
fi
if [[ -n "$vname" ]]; then
mid="sha1=$vname"
ident="$vname"
rhs="Same version"
else
rhs="(not listed on server)"
fi
fi
fi
mark=" "
[[ "$f" == "$activename" ]] && mark="* "
annot=""
[[ -n "$platform_default" && "$f" == "$platform_default" ]] && annot=" (server default)"
printf '%s%-35s %s%s\n' "$mark" "$f" "$rhs" "$annot"
#the (server default) annotation follows artifact identity: a working
#copy of (or byte-copy of) the default artifact is the default
if [[ -n "$platform_default" ]] && [[ "$f" == "$platform_default" || "$ident" == "$platform_default" ]]; then
annot=" (server default)"
fi
printf '%s%-35s %-33s %s%s\n' "$mark" "$f" "$mid" "$rhs" "$annot"
done
#remote-only entries, sorted (ps1-payload parity - its dict enumeration
#is explicitly sorted). tr strips CRLF \r: msys grep strips it in text
@ -654,10 +713,10 @@ case "$action" in
if [[ -n "$rname" && ! -f "$archdir/$rname" ]]; then
annot=""
[[ -n "$platform_default" && "$rname" == "$platform_default" ]] && annot=" (server default)"
printf ' %-35s %s%s\n' "-" "$rname" "$annot"
printf ' %-35s %-33s %s%s\n' "-" "" "$rname" "$annot"
fi
done
echo "-----------------------------------------------------------------------"
echo "----------------------------------------------------------------------------------------"
echo "* = active (local selection)"
else
if [[ -d "$archdir" ]]; then

86
src/scriptapps/bin/punk-runtime.ps1

@ -124,6 +124,11 @@ function Show-PunkRuntimeHelp {
write-host " visible at a glance. When both are known, a freshness verdict"
write-host " states current/ahead/behind/incomparable explicitly (revision"
write-host " comparison, sha1 identity fallback; update guidance on behind)."
write-host " Rows the server listing does not name carry an identity column:"
write-host " from=<artifact> for a materialized working copy (content verified"
write-host " against that artifact), sha1=<name> for a byte-copy of a server"
write-host " artifact - only genuinely unknown files stay '(not listed on"
write-host " server)'."
write-host " use <name> ?-platform <p>?"
write-host " Select the runtime that 'run' launches (records active.toml in"
write-host " the platform folder). An immutable -r<N> ARTIFACT name is"
@ -1060,7 +1065,7 @@ function psmain {
}
}
}
Write-host "-----------------------------------------------------------------------"
Write-host "----------------------------------------------------------------------------------------"
Write-host "Runtimes for $arch"
Write-host "Local $archfolder"
Write-host "Remote $archurl"
@ -1090,9 +1095,9 @@ function psmain {
#annotation above already says so)
Write-PunkFreshnessVerdict -archfolder $archfolder -activename $activename -defaultname $platform_default -sha1local $sha1local -arch $arch -mode 'list'
}
Write-host "-----------------------------------------------------------------------"
Write-host " Local Remote"
Write-host "-----------------------------------------------------------------------"
Write-host "----------------------------------------------------------------------------------------"
Write-host (" {0,-35} {1,-33} {2}" -f "Local", "", "Remote")
Write-host "----------------------------------------------------------------------------------------"
# 12345678910234567892023456789302345
$G = "`e[32m" #Green
$Y = "`e[33m" #Yellow
@ -1106,6 +1111,8 @@ function psmain {
[array]::Sort($localkeys, [System.StringComparer]::Ordinal)
foreach ($key in $localkeys) {
$local_sha1 = $($localdict[$key])
$mid = ""
$ident = ""
if ($remotedict.ContainsKey($key)) {
if ($local_sha1 -eq $remotedict[$key]) {
$rhs = "Same version"
@ -1116,7 +1123,63 @@ function psmain {
}
} else {
$C = $R
$rhs = "(not listed on server)"
#G-119 refinement: identity middle column for rows the server
#listing does not name. A materialized working copy is
#identified by its beside-toml source artifact (from=) with
#its CONTENT verified against that artifact's server sha1; a
#tomlless stray by sha1 VALUE-match (sha1=). The Remote
#column then carries a real server relationship for such
#rows instead of the dead-end '(not listed on server)',
#which now means genuinely unknown.
$rowtoml = Join-Path -Path $archfolder -ChildPath ((Get-PunkRuntimeRootName $key) + ".toml")
$artname = ""
if (Test-Path -Path $rowtoml -PathType Leaf) {
foreach ($tline in (Get-Content -Path $rowtoml)) {
$m = [regex]::Match($tline, '^\s*name\s*=\s*"(.*)"\s*$')
if ($m.Success) { $artname = $m.Groups[1].Value; break }
}
if ($artname -eq $key) { $artname = "" }
}
if ($artname -ne "") {
$mid = "from=$artname"
$ident = $artname
if (-not $remotedict.ContainsKey($artname)) {
$rhs = "(artifact not listed on server)"
} elseif ($local_sha1 -eq $remotedict[$artname]) {
$rhs = "Same version"
} else {
#-r<N> artifacts are immutable - differing content
#means a modified local copy or a mismatched
#sidecar, not an update
$rhs = "CONTENT DIFFERS"
}
} else {
#sha1 value-match: the default's row first, else the
#first ordinal match with support files excluded - the
#same pick rule as Write-PunkFreshnessVerdict, so the
#row identity and the freshness line always name the
#same artifact
$vname = ""
if ($platform_default -ne "" -and $remotedict.ContainsKey($platform_default) -and $remotedict[$platform_default] -eq $local_sha1) {
$vname = $platform_default
}
if ($vname -eq "") {
$rnames = @($remotedict.Keys)
[array]::Sort($rnames, [System.StringComparer]::Ordinal)
foreach ($rn in $rnames) {
$rext = [System.IO.Path]::GetExtension($rn)
if ($(".txt",".toml",".tm",".tmp",".log") -contains $rext) { continue }
if ($remotedict[$rn] -eq $local_sha1) { $vname = $rn; break }
}
}
if ($vname -ne "") {
$mid = "sha1=$vname"
$ident = $vname
$rhs = "Same version"
} else {
$rhs = "(not listed on server)"
}
}
}
#ansi problems from cmd.exe not in windows terminal - review
$C = ""
@ -1124,12 +1187,15 @@ function psmain {
$mark = " "
if ($key -eq $activename) { $mark = "* " }
$annot = ""
if ($platform_default -ne "" -and $key -eq $platform_default) { $annot = " (server default)" }
#the (server default) annotation follows artifact identity: a
#working copy of (or byte-copy of) the default artifact is the
#default
if ($platform_default -ne "" -and ($key -eq $platform_default -or $ident -eq $platform_default)) { $annot = " (server default)" }
$lhs = "$key".PadRight(35, ' ')
write-host -nonewline "${mark}${C}${lhs}${RST}"
$midp = "$mid".PadRight(33, ' ')
write-host -nonewline "${mark}${C}${lhs}${RST} ${midp} "
write-host "$rhs$annot"
}
$lhs_missing = "-".PadRight(35, ' ')
$remotekeys = @($remotedict.Keys)
[array]::Sort($remotekeys, [System.StringComparer]::Ordinal)
foreach ($key in $remotekeys) {
@ -1141,11 +1207,11 @@ function psmain {
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"
write-host -nonewline (" {0,-35} {1,-33} " -f "-", "")
write-host "$key$annot"
}
}
Write-host "-----------------------------------------------------------------------"
Write-host "----------------------------------------------------------------------------------------"
Write-host "* = active (local selection)"
} else {

2
src/tests/shell/AGENTS.md

@ -22,7 +22,7 @@ Tests for shell-level behavior, command-line execution, and stdin/stdout interac
- punkexe tests run against the built binary: after changing punk::repl or app-punkshell source, rebuild via `make.tcl packages`, `make.tcl vfscommonupdate -confirm 0` (unattended runs must pass the flag - the REPLACE confirmation aborts fast on non-interactive stdin instead of reading a piped `y`), then `make.tcl project` before trusting results; `make.tcl project` alone does not refresh `_vfscommon.vfs`.
- `testsuites/punkexe/tclshcmd.test` covers the `tclsh` subcommand (punk_main.tcl dispatch): script-file sourcing (argv0/argv/info-script state, honest exit codes incl. explicit exit), piped no-arg stdin evaluation, stock argument forms (leading `-` arg = no script, all args stay in `::argv`; `-encoding name file` sources with the named encoding, incomplete `-encoding` forms fall through to argv; verified byte-level via a utf-8/iso8859-1 fixture), the `lib:` refusal (plain-tclsh semantics - pointer to the `script` subcommand, exit 1), and - on kits built on a TCLSH_PIPEREPL-patched runtime (G-096/G-103, see `src/buildsuites/suite_tcl90/patches/README.md`) - the piperepl launch-state contract: ::tclsh(istty) published, piped script-arg launch state all-zero, unconsumed piped input NOT evaluated unless the script sets `::tclsh(evalinput) 1`, and the script's right to consume stdin itself; plus the degraded-mode notice on unpatched runtimes. Patched-kit resolution: `env(PUNK_PIPEREPL_TEST_EXE)` if it probes as patched, else the punkexe if patched, else `bin/punk9_beta.exe`/`bin/punk9bi_beta.exe`. HANG RULE: no PIPED test or fixture may set `::tclsh(dorepl)` - that path reopens stdin from the console (CONIN$) and blocks under the piped punk_run harness. Console-reopen coverage exists but is env-gated (G-118 item 11; constraint `punkconsole` = windows + env `PUNK_TEST_CONSOLE=1`, normal runs skip): two tests drive the dorepl path end-to-end in the child's OWN hidden console per the G-106 recipe - an outer `Start-Process -WindowStyle Hidden` powershell wrapper owns a fresh console and runs `<kit> tclsh` there with std handles redirected to files, then `testsupport/consoleinject.ps1` attaches to that console and types the verification lines + `exit 0` as key events. They pin: `::tclsh(reopened)` flips 0 -> 1 at reopen while `istty` stays 0 (immutable launch fact), `::tclsh(inputbuffer)` appears empty (the repl loop consumed all piped input), the reopen notice is silent by default and appears on STDERR only under `TCLSH_PIPEREPL_DEBUG=1`, and the injected exit completes the process (exit code 0). Deadlines plus wrapper-tree kill keep them hang-proof.
- `testsuites/binscripts/runtimebash_wsl.test` (G-059) exercises the punk-runtime.cmd unix payload (`src/scriptapps/bin/punk-runtime.bash`) through WSL: active/use/run resolution, env override, stale-active guidance, single-candidate fallback, and `list -remote` local-vs-server comparison (offline via a crafted cached sha1sums.txt + `PUNKBIN_URL` pointed at an unreachable address). Gated by the `wsllinux` capability constraint from `src/tests/testsupport/wslprobe.tcl`; all execution happens in a WSL-native staging dir, and a final test asserts the Windows checkout's git status is unchanged by the run.
- `testsuites/binscripts/runtimecmd_freshness.test` (G-119) exercises the punk-runtime freshness verdict (active-vs-server-default: current/ahead/behind/incomparable) in BOTH payloads against a fixture punkbin server: a child-process http file server (`testsupport/httpfixture.tcl`) serves a crafted layout (defaults.txt, sha1sums, artifacts + metadata tomls) via the `PUNKBIN_URL` override, with all execution in a tempdir staging area on a fixture platform folder (`testplat-x86_64` - the real `bin/runtime` store is untouched). The powershell payload runs as the staged committed `bin/punk-runtime.cmd` via cmd.exe (the wrap-pinned path); the bash payload is driven DIRECTLY under a probed msys/git bash (uname MINGW*/MSYS*; WSL bash is rejected - under WSL2 it cannot reach the 127.0.0.1 fixture server, and on windows the polyglot routes unix shells to the powershell payload anyway). Asserts the G-119 acceptance scenarios (ahead/behind+guidance/current-annotation/pre-family no-basis and hash-identity/no-name-fetch note) with ps1-vs-bash verdict-line parity. Gated by capability constraints (windows, committed cmd, bash source, certutil, msys bash, fixture server started); skips cleanly elsewhere.
- `testsuites/binscripts/runtimecmd_freshness.test` (G-119) exercises the punk-runtime freshness verdict (active-vs-server-default: current/ahead/behind/incomparable) in BOTH payloads against a fixture punkbin server: a child-process http file server (`testsupport/httpfixture.tcl`) serves a crafted layout (defaults.txt, sha1sums, artifacts + metadata tomls) via the `PUNKBIN_URL` override, with all execution in a tempdir staging area on a fixture platform folder (`testplat-x86_64` - the real `bin/runtime` store is untouched). The powershell payload runs as the staged committed `bin/punk-runtime.cmd` via cmd.exe (the wrap-pinned path); the bash payload is driven DIRECTLY under a probed msys/git bash (uname MINGW*/MSYS*; WSL bash is rejected - under WSL2 it cannot reach the 127.0.0.1 fixture server, and on windows the polyglot routes unix shells to the powershell payload anyway). Asserts the G-119 acceptance scenarios (ahead/behind+guidance/current-annotation/pre-family no-basis and hash-identity/no-name-fetch note) with ps1-vs-bash verdict-line parity, plus the identity middle column row shapes (2026-07-25 follow-on): `from=<artifact>` working-copy rows with content-verified Remote statuses (Same version / CONTENT DIFFERS / artifact-not-listed), `sha1=<name>` byte-copy strays, and the identity-following `(server default)` row annotation. Gated by capability constraints (windows, committed cmd, bash source, certutil, msys bash, fixture server started); skips cleanly elsewhere.
- `testsuites/punkexe/staticruntime.test` (G-058) targets a kit built on a runtime with statically-linked packages: resolved from `env(PUNK_STATICKIT_TEST_EXE)`, else `<projectroot>/bin/punk91.exe`, gated by the capability-probed `statickitavailable` constraint (a probe run must show a captured `::punkboot::static_packages` baseline including Thread - not mere file existence). It asserts repl boot without the codethread Thread failure, Thread resolution in the code interp / fabricated interps / snapshot-seeded threads, and twapi resolution per the static-vs-bundled policy. The verification runtime (tclsfe-x64.exe) is pinned in the punkbin artifact repo so the constraint is satisfiable off the original dev machine. Kit script-exec paths must be passed with forward slashes.
- GOAL tests (constraint `punkgoals`, enabled by env `PUNK_TEST_GOALS=1`) cover the piped-stdin-then-interactive-shell drop-in (`shellpipe_eof_then_interactive_shell`, `shellpipe_script_and_eof_then_interactive_shell`); they are excluded from normal runs because they require an openable console in the test environment and briefly attach a live shell to it - they pass in console-attached environments against a current build.
- The eof-policy discriminator is `env(PUNK_PIPE_EOF)` (`exit`|`interactive`|unset=console heuristic) implemented in app-punkshell; termination-guard tests spawn children with `exit`, goal tests spawn with it unset.

79
src/tests/shell/testsuites/binscripts/runtimecmd_freshness.test

@ -459,6 +459,85 @@ namespace eval ::testspace {
}\
-result [list 0 1 0 1]
#added 2026-07-25 (agent, G-119) - follow-on refinement (user-directed shape):
#identity middle column (from=/sha1=) so the salient starred row changes with
#every 'use' switch and the Remote column carries a real server relationship
test runtimecmd_freshness_identity_columns {rows the server listing does not name carry identity: from=<artifact> on a materialized working copy (starred) and sha1=<name> on a byte-copy stray, both with a real Remote status - the dead-end '(not listed on server)' no longer appears for identified rows - both payloads}\
-constraints $C\
-setup {
set result ""
variable artifacts
reset_stage
stage_materialized fakert-alpha.exe fakert-alpha-r2.exe 2
stage_file oldkit.exe [dict get $artifacts fakert-alpha-r2.exe]
set_default fakert-alpha-r1.exe
}\
-body {
foreach r [list [run_cmdroute list -remote -platform testplat-x86_64] \
[run_bashroute list -remote -platform testplat-x86_64]] {
set out [dict get $r output]
lappend result [dict get $r exit]
lappend result [regexp {\* fakert-alpha\.exe\s+from=fakert-alpha-r2\.exe\s+Same version} $out]
lappend result [regexp {oldkit\.exe\s+sha1=fakert-alpha-r2\.exe\s+Same version} $out]
lappend result [string match "*(not listed on server)*" $out]
}
set result
}\
-cleanup {
}\
-result [list 0 1 1 0 0 1 1 0]
test runtimecmd_freshness_identity_default_annotation {the (server default) row annotation follows artifact identity: a working copy materialized from the default artifact carries it on the starred row - both payloads}\
-constraints $C\
-setup {
set result ""
reset_stage
stage_materialized fakert-alpha.exe fakert-alpha-r1.exe 1
set_default fakert-alpha-r1.exe
}\
-body {
foreach r [list [run_cmdroute list -remote -platform testplat-x86_64] \
[run_bashroute list -remote -platform testplat-x86_64]] {
lappend result [dict get $r exit]
lappend result [regexp {\* fakert-alpha\.exe\s+from=fakert-alpha-r1\.exe\s+Same version\s+\(server default\)} [dict get $r output]]
}
set result
}\
-cleanup {
}\
-result [list 0 1 0 1]
test runtimecmd_freshness_identity_anomaly_statuses {identified working copies surface anomalies: content differing from the immutable source artifact reports CONTENT DIFFERS, and a source artifact absent from the server listing is stated - both payloads}\
-constraints $C\
-setup {
set result ""
variable artifacts
reset_stage
#working copy claiming r1 provenance but carrying r2 bytes (modified local
#copy / mismatched sidecar - artifacts are immutable, so never an "update")
stage_file fakert-alpha.exe [dict get $artifacts fakert-alpha-r2.exe]
stage_file fakert-alpha.toml [artifact_toml fakert-alpha-r1.exe 1]
#working copy whose recorded source artifact is not on the server
stage_file fakert-beta.exe [dict get $artifacts fakert-beta-r1.exe]
stage_file fakert-beta.toml [artifact_toml fakert-beta-r9.exe 9]
set_active fakert-alpha.exe
set_default fakert-alpha-r1.exe
}\
-body {
foreach r [list [run_cmdroute list -remote -platform testplat-x86_64] \
[run_bashroute list -remote -platform testplat-x86_64]] {
set out [dict get $r output]
lappend result [dict get $r exit]
lappend result [regexp {fakert-alpha\.exe\s+from=fakert-alpha-r1\.exe\s+CONTENT DIFFERS} $out]
lappend result [regexp {fakert-beta\.exe\s+from=fakert-beta-r9\.exe\s+\(artifact not listed on server\)} $out]
}
set result
}\
-cleanup {
}\
-result [list 0 1 1 0 1 1]
#whole-file teardown: stop the fixture server, restore shielded env state
if {$fixtureok} {
server_stop

Loading…
Cancel
Save