Browse Source

G-128: seam names the kit group icon PUNKSHELL (icon-derived, both arms); punkshell 0.30.2

RT_GROUP_ICON name policy (user decision 2026-07-29): kit_icon_process derives
icon_group from the icon FILE's uppercased rootname (RC convention) -
punkshell.ico -> PUNKSHELL for the default icon and per-kit overrides alike -
and passes it to whichever mechanism embeds. punkres arm uses set-icon -group
(punkres 0.2.0); kit_icon_embed_twapi gains the matching optional
group_override (name only - language adoption unchanged) so the arms stay in
parity. Replaces the retained stub-lineage names (punk91 "SFE", suite kits
"TCLSH"); already-deployed kits keep theirs until their next natural rebuild
(the policy is not in the punkcheck source set).

Sidecar gains icon_group (recorded whenever the icon file exists);
bin/AGENTS.md format doc + ARCHITECTURE.md updated, architecture_lint clean.

Live-verified on punkluck86 rebakes: punkres arm and twapi arm (TCLLIBPATH
route) both emit RT_GROUP_ICON "PUNKSHELL" lang 1033, extracted icons
byte-identical between arms; sidecar icon_group=PUNKSHELL; final deployed
state punkres-stamped and booting. Full punkexe suite 83 tests 0 failed.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 2 days ago
parent
commit
bd65183061
  1. 2
      ARCHITECTURE.md
  2. 11
      CHANGELOG.md
  3. 12
      bin/AGENTS.md
  4. 35
      goals/G-128-portable-pe-resource-stamping.md
  5. 2
      punkproject.toml
  6. 57
      src/make.tcl

2
ARCHITECTURE.md

@ -87,7 +87,7 @@ Two layers with a deliberate dependency direction (the class never depends on th
- **Provenance gates.** Build/promotion commands warn on uncommitted `src/` changes (column-0 `PROVENANCE-WARNING:` token, `-dirty-abort` for strict mode); `vendorupdate` warns for dirty source-project checkouts.
- **Boot-precondition gate (G-125).** A bake refuses a kit whose merged `.vfs` supplies no tcl library rather than deploying an artifact that cannot initialise: the kit lands in `FAILED KITS` and nothing is written, so the previously deployed `bin/<kit>` survives. Structural and non-executing (so cross-target kits are covered), reading the merged tree rather than the extraction outcome. The predicate is `punkboot::utils::vfs_boot_library_report`, called through the same guarded require as the provenance check so a stale bootsupport snapshot degrades it to a notice; `make.tcl check` reports ACTIVE/UNAVAILABLE. Sources: `src/AGENTS.md`, `src/modules/punkboot/utils-999999.0a1.0.tm`.
- **Payload/target consistency checks (G-133), both advisory.** At the same post-merge seam, a bake classifies each binary library in the merged tree by header (`punkboot::utils::binary_arch_classify` - PE/ELF/Mach-O, honest unknowns) against the kit's target platform; wrong-arch libraries outside platform-discriminated subdirs (canonical `<os>-<cpu>` names, vendor spellings like `win-x64`) earn recapped `BUILD-WARNING`s. After assembly, a kit declaring smoke-require packages (`mapvfs.config` 5th entry element) has each one plain-`package require`d inside the freshly built artifact via its tclsh subcommand - the only check that sees resolution-order defects (wrong-arch version shadowing); cross-target kits skip with a stated reason. Same guarded-require degradation; `make.tcl check` reports both. Neither guarantees statics, pure-tcl packages with binary deps, or version preference beyond the declared smoke set. Sources: `src/AGENTS.md`, `src/runtime/AGENTS.md`, `src/modules/punkboot/utils-999999.0a1.0.tm`.
- **Kit icon step (G-057 + G-128).** Every kit a bake builds gets a `<kitname>.resources.toml` sidecar (deployed beside `bin/<kit>`) recording the build-time icon choice - default `src/runtime/punkshell.ico`, overridable by a root `punkshell.ico` in the kit's own custom `.vfs` folder (pre-merge) - with sha256 identity and provenance lifted from the icon's G-135 assetorigin record. win32-target kits get the icon embedded as PE `RT_ICON`/`RT_GROUP_ICON` behind a single seam (`::punkboot::kit_icon_process`): the vendored punkres portable stamper (`src/tools/punkres`, built to `bin/punkres(.exe)` by the make.tcl tool step) is selected when present and works from any build host; otherwise the twapi arm (tcl-sfe mechanism) serves windows hosts with nothing built. Both arms share one semantic (delete all icon/group entries, write ids 1..N under the first pre-existing group's name+lang) and stamp a per-kit copy of the payload-free raw runtime prefix BEFORE payload attach; punkres can also stamp a FINISHED kit post-hoc, preserving an appended zip payload (archive-relative moved verbatim; file-relative refused by default, shifted with a consent flag). Non-PE targets skip as not applicable; a host where neither mechanism serves skips with a combined notice naming the punkres build remedy; the sidecar is written in every case. Sources: `src/AGENTS.md`, `bin/AGENTS.md`, `src/vfs/AGENTS.md`, `src/tools/AGENTS.md`.
- **Kit icon step (G-057 + G-128).** Every kit a bake builds gets a `<kitname>.resources.toml` sidecar (deployed beside `bin/<kit>`) recording the build-time icon choice - default `src/runtime/punkshell.ico`, overridable by a root `punkshell.ico` in the kit's own custom `.vfs` folder (pre-merge) - with sha256 identity and provenance lifted from the icon's G-135 assetorigin record. win32-target kits get the icon embedded as PE `RT_ICON`/`RT_GROUP_ICON` behind a single seam (`::punkboot::kit_icon_process`): the vendored punkres portable stamper (`src/tools/punkres`, built to `bin/punkres(.exe)` by the make.tcl tool step) is selected when present and works from any build host; otherwise the twapi arm (tcl-sfe mechanism) serves windows hosts with nothing built. Both arms share one semantic (delete all icon/group entries, write ids 1..N; the group is NAMED from the icon file's uppercased rootname - `PUNKSHELL` - with language adopted from the replaced group) and stamp a per-kit copy of the payload-free raw runtime prefix BEFORE payload attach; punkres can also stamp a FINISHED kit post-hoc, preserving an appended zip payload (archive-relative moved verbatim; file-relative refused by default, shifted with a consent flag). Non-PE targets skip as not applicable; a host where neither mechanism serves skips with a combined notice naming the punkres build remedy; the sidecar is written in every case. Sources: `src/AGENTS.md`, `bin/AGENTS.md`, `src/vfs/AGENTS.md`, `src/tools/AGENTS.md`.
- **Buildsuites and the kit family.** `src/buildsuites/suite_tcl90/` builds Tcl/Tk/tcllib from source with a pinned zig toolchain and produces the runtime kit family (plain / punk / bi) plus artifact metadata (G-096-G-117 era: see archived goals G-096, G-098, G-102, G-103, G-107); artifacts publish to the punkbin repo that `bin/punk-runtime.cmd` fetches from. `src/buildsuites/suite_tcl86/` is the 8.6 sibling (G-099 + G-100, both achieved): a Tcl 8.6 windows runtime (static + dynamic shells, `tcl86t.dll`, on-disk lib tree - no zipfs, so no self-contained kit) with thread 2.8 + tclvfs + Tk 8.6 + tklib + tcllib(+tcllibc critcl accelerators) companions, gated against the 8.6 core/thread/tclvfs testsuites with tcllib/tklib on a record tier and an opt-in `test-tk`; punkshell's own runtests is censused on that runtime at parity with a same-day native-8.6 baseline. Suite child shells scrub `TCL<major>_<minor>_TM_PATH` as well as TCLLIBPATH/TCL_LIBRARY/TK_LIBRARY - without it a census measures the machine's module trees. The 8.6 kit container strategy remains in-flux - see "In-flux areas".
- **Project generation.** `dev project.new` composes thin layouts from `src/project_layouts/` (overlay chain with `.anti` deletion markers and `name@base` derivation - G-087, achieved) and injects bootsupport modules from the generating shell at generation time.
- **Workflow overview.** `tclsh src/make.tcl workflow` prints the embedded ASCII data-flow overview of the build/release pipeline, with its own update contract in `src/AGENTS.md`. This section deliberately summarises rather than copies it.

11
CHANGELOG.md

@ -5,6 +5,17 @@ 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.30.2] - 2026-07-29
- Baked kits' embedded RT_GROUP_ICON is now NAMED from the icon file's
uppercased rootname - `PUNKSHELL` for `punkshell.ico` (default and per-kit
overrides alike) - instead of retaining the stub lineage's name ("SFE",
"TCLSH"). Applied identically by both icon mechanisms (punkres 0.2.0
`set-icon -group`; the twapi arm gained the matching override), language
still adopted from the replaced group. The `<kit>.resources.toml` sidecar
records the name as `icon_group`. Already-deployed kits keep their old
group name until their next rebuild.
## [0.30.1] - 2026-07-29
- Portable PE resource stamping (G-128, first increment): new vendored zig tool

12
bin/AGENTS.md

@ -168,8 +168,12 @@ never modified). Two mechanisms share one semantic and yield equivalent
resource content (G-128 parity evidence): `bin/punkres(.exe)` - the vendored
portable stamper, built via `make.tcl tool build punkres`, host-independent -
is selected when present; otherwise the twapi arm serves windows hosts with
nothing built. Named "resources", not "icon", so the parked RT_VERSION
stamping extends the same file.
nothing built. The embedded RT_GROUP_ICON is NAMED from the icon file's
uppercased rootname (`punkshell.ico` -> `PUNKSHELL`, RC convention; applies
to per-kit overrides identically since they are also named `punkshell.ico`),
with the resource language adopted from the group being replaced. Named
"resources", not "icon", so the parked RT_VERSION stamping extends the same
file.
- Icon choice: a root-level `punkshell.ico` in the kit's own custom `.vfs`
folder (consulted pre-merge, so the `_vfscommon.vfs` copy never reads as an
@ -183,7 +187,9 @@ stamping extends the same file.
path of the chosen icon); `icon_hash` (`sha256:`-prefixed);
`icon_provenance_source` / `icon_provenance_source_hash` (lifted from the
chosen icon's `.assetorigin.toml` when one exists - the record then reaches
back to the SVG-derived master); `embedding` = `punkres` | `twapi` | `none`;
back to the SVG-derived master); `icon_group` (the derived RT_GROUP_ICON
resource name, present whenever the icon file exists - recorded even when
embedding did not run); `embedding` = `punkres` | `twapi` | `none`;
`embedding_status` = `embedded` | `not-applicable` | `unavailable` |
`failed`; `embedding_reason` (empty when embedded).
- Skip semantics: a target with no PE resources (linux, freebsd, macosx

35
goals/G-128-portable-pe-resource-stamping.md

@ -173,17 +173,20 @@ the parked RT_VERSION stamping wants, since the project version is known late.
stamping-support review). No acceptance wording change; revisit if other
payload shapes (cookit/cookfs, or a G-101-chosen container) become stamping
targets - a dedicated goal is warranted only then.
- Group naming (2026-07-29, user-directed): punkres 0.2.0 adds
`set-icon -group <id|name>` overriding the adopted RT_GROUP_ICON name
(stored as given, case-insensitive lookups; language + codepage adoption
unchanged; re-stamps without the flag adopt the renamed group, so renaming
is convergent). Motivation: stamped kits carry their stub lineage's group
name (punk91 "SFE", suite-runtime kits "TCLSH") where a punkshell-derived
name reads better. SEAM DEFAULT NOT CHANGED yet - flipping the bake to pass
a derived name (e.g. the icon file's root name, giving "punkshell" for both
the default icon and per-kit overrides) needs the twapi arm to gain the
same override for parity, and changes every baked kit's resource listing -
proposal outstanding with the user.
- Group naming (2026-07-29, user-directed; DEFAULT DECIDED same day): punkres
0.2.0 adds `set-icon -group <id|name>` overriding the adopted RT_GROUP_ICON
name (stored as given, case-insensitive lookups; language + codepage
adoption unchanged; re-stamps without the flag adopt the renamed group, so
renaming is convergent). Motivation: stamped kits carried their stub
lineage's group name (punk91 "SFE", suite-runtime kits "TCLSH"). The SEAM
now derives the name from the icon FILE's uppercased rootname (user
decision: uppercase per the RC convention) - punkshell.ico -> PUNKSHELL for
the default icon and per-kit overrides alike - and passes it to whichever
arm embeds: kit_icon_embed_twapi gained the same optional group_override
for parity. The sidecar records it as icon_group (present whenever the icon
file exists). Kits already deployed keep their old group name until their
next natural rebuild (the naming policy is not in the punkcheck source set,
so it does not itself dirty kits).
- Related: G-105 - recorded at activation (2026-07-29 overlap survey): the end-to-end
non-windows-host acceptance clause needs a host-native punkres build; cross-target
zig builds and target naming live in G-105 rather than here (the same division
@ -288,3 +291,13 @@ the parked RT_VERSION stamping wants, since the project version is known late.
(user decision - see Notes); G-101 carries the container-review rider.
BSD-2-Clause licence confirmed by user. Seam group-name default: proposal
outstanding (Notes).
- 2026-07-29 increment 1b (user-directed): seam group-name default LANDED -
kit_icon_process derives icon_group = uppercased icon-file rootname
(PUNKSHELL) and passes it to both arms; kit_icon_embed_twapi gained the
matching group_override parameter (parity); sidecar gains icon_group
(bin/AGENTS.md + ARCHITECTURE.md updated). Live-verified on punkluck86
rebakes: punkres arm and twapi arm (TCLLIBPATH route) both produce
RT_GROUP_ICON "PUNKSHELL" lang 1033 with byte-identical extracted icons
(codepage divergence as per the Notes finding); sidecar icon_group +
embedding recorded; final deployed state punkres-stamped and booting.
punkshell 0.30.2.

2
punkproject.toml

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

57
src/make.tcl

@ -6312,13 +6312,15 @@ proc ::punkboot::assemble_zipcat_image {raw_runtime wrapvfs outfile zipfile} {
#consulted PRE-merge so the _vfscommon copy never reads as an override), and
#win32-target kits get that icon embedded as PE RT_ICON/RT_GROUP_ICON resources
#when this host can write them. Two mechanisms share one semantic (delete ALL
#icon/group entries, write the .ico images as ids 1..N under the first
#pre-existing group's name+lang - idempotent by construction, parity recorded
#in the G-128 detail file): the vendored punkres portable stamper (G-128,
#src/tools/punkres, built by 'make.tcl tool build punkres') is selected when
#bin/punkres(.exe) exists and works from ANY build host; otherwise the twapi
#arm (tcl-sfe sfe-0.2.tm ReplaceIconInStub lineage; Ashok P. Nadkarni) serves
#windows hosts with nothing built. Ordering is stub-first either way: a per-kit
#icon/group entries, write the .ico images as ids 1..N; the RT_GROUP_ICON is
#NAMED from the icon file - uppercased rootname, so punkshell.ico gives
#PUNKSHELL (user decision 2026-07-29, RC uppercase convention) - with language
#adopted from the first replaced group; idempotent by construction, parity
#recorded in the G-128 detail file): the vendored punkres portable stamper
#(G-128, src/tools/punkres, built by 'make.tcl tool build punkres') is
#selected when bin/punkres(.exe) exists and works from ANY build host;
#otherwise the twapi arm (tcl-sfe sfe-0.2.tm ReplaceIconInStub lineage; Ashok
#P. Nadkarni) serves windows hosts with nothing built. Ordering is stub-first either way: a per-kit
#copy of the payload-free raw runtime prefix is stamped BEFORE any payload is
#appended (punkres could also stamp post-hoc - the bake keeps stub-first so
#both arms stay interchangeable). kit_icon_process is the single internal
@ -6538,12 +6540,14 @@ proc ::punkboot::kiticon::punkres_available {repo_root} {
}
#G-128 punkres arm: exec the vendored portable stamper on the payload-free stub
#copy. punkres itself deletes all icon/group entries, writes the images as ids
#1..N under the first pre-existing group's name+lang, and re-reads its own
#1..N under the first pre-existing group's name+lang (a non-empty
#group_override renames the group via 'set-icon -group' - the seam's naming
#policy; language and codepage adoption unchanged), and re-reads its own
#output (resources AND any overlay) before reporting success - equivalent
#resource content to the twapi arm from the same input (parity evidence in the
#G-128 detail file). Returns the number of images embedded; raises an error
#carrying punkres's output on any nonzero exit.
proc ::punkboot::kit_icon_embed_punkres {punkres_exe exepath icopath} {
proc ::punkboot::kit_icon_embed_punkres {punkres_exe exepath icopath {group_override {}}} {
set f [open $icopath rb]
set icondata [read $f]
close $f
@ -6554,7 +6558,12 @@ proc ::punkboot::kit_icon_embed_punkres {punkres_exe exepath icopath} {
if {$ico_count == 0} {
error "no images in $icopath"
}
if {[catch {exec [file nativename $punkres_exe] set-icon [file nativename $exepath] [file nativename $icopath] 2>@1} output]} {
set cmd [list exec [file nativename $punkres_exe] set-icon]
if {$group_override ne ""} {
lappend cmd -group $group_override
}
lappend cmd [file nativename $exepath] [file nativename $icopath] 2>@1
if {[catch $cmd output]} {
error "punkres set-icon failed: $output"
}
return $ico_count
@ -6563,10 +6572,13 @@ proc ::punkboot::kit_icon_embed_punkres {punkres_exe exepath icopath} {
#the images of icopath. Follows sfe-0.2.tm ReplaceIconInStub generalized: all
#existing icon/group entries (any name, any lang) are deleted and the new set
#written as ids 1..N under the first pre-existing group's name+lang (fallback
#name 1, lang 1033), in one resource-update transaction. Raises an error on
#failure (after -discard); returns the number of images embedded. The file at
#exepath must carry NO appended payload - a resource update rewrites the image.
proc ::punkboot::kit_icon_embed_twapi {exepath icopath} {
#name 1, lang 1033), in one resource-update transaction. A non-empty
#group_override replaces the group NAME only (language adoption unchanged) -
#the same override punkres's 'set-icon -group' applies, so the seam's naming
#policy keeps the two arms in parity (G-128). Raises an error on failure
#(after -discard); returns the number of images embedded. The file at exepath
#must carry NO appended payload - a resource update rewrites the image.
proc ::punkboot::kit_icon_embed_twapi {exepath icopath {group_override {}}} {
set f [open $icopath rb]
set icondata [read $f]
close $f
@ -6601,6 +6613,9 @@ proc ::punkboot::kit_icon_embed_twapi {exepath icopath} {
if {[llength $old_groups]} {
lassign [lindex $old_groups 0] group_name group_lang
}
if {$group_override ne ""} {
set group_name $group_override
}
set update_h [twapi::begin_resource_update $exepath]
try {
foreach entry $old_icons {
@ -6649,6 +6664,15 @@ proc ::punkboot::kit_icon_process {targetkit rt_target rtname kit_type sourcefol
set icon_path $sourcefolder/runtime/punkshell.ico
}
set have_icon [file isfile $icon_path]
#RT_GROUP_ICON name policy (G-128, user decision 2026-07-29): derived from
#the icon FILE's rootname, uppercased per the RC convention - punkshell.ico
#gives PUNKSHELL for the default icon and for per-kit overrides alike
#(overrides are by convention also named punkshell.ico). Applied by
#whichever mechanism embeds; language stays adopted from the replaced group.
set icon_group ""
if {$have_icon} {
set icon_group [string toupper [file rootname [file tail $icon_path]]]
}
set embedding none
set status ""
set reason ""
@ -6687,10 +6711,10 @@ proc ::punkboot::kit_icon_process {targetkit rt_target rtname kit_type sourcefol
catch {exec chmod +w $stamped}
if {$punkres_ok} {
set embedding punkres
set embed_cmd [list ::punkboot::kit_icon_embed_punkres $punkres_info $stamped $icon_path]
set embed_cmd [list ::punkboot::kit_icon_embed_punkres $punkres_info $stamped $icon_path $icon_group]
} else {
set embedding twapi
set embed_cmd [list ::punkboot::kit_icon_embed_twapi $stamped $icon_path]
set embed_cmd [list ::punkboot::kit_icon_embed_twapi $stamped $icon_path $icon_group]
}
if {[catch $embed_cmd embed_result]} {
::punkboot::print_build_warnings [list "kit icon embedding FAILED for $targetkit ($icon_origin icon $icon_path via $embedding): $embed_result - kit keeps its runtime's own icon"]
@ -6727,6 +6751,7 @@ proc ::punkboot::kit_icon_process {targetkit rt_target rtname kit_type sourcefol
if {[dict exists $ao source_hash]} {
lappend lines "icon_provenance_source_hash = \"[$esc [dict get $ao source_hash]]\""
}
lappend lines "icon_group = \"[$esc $icon_group]\""
}
lappend lines "embedding = \"$embedding\""
lappend lines "embedding_status = \"$status\""

Loading…
Cancel
Save