From 164687d26013b09af7bcb06996dc6b202fa08e71 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Tue, 28 Jul 2026 01:51:06 +1000 Subject: [PATCH] G-135 + G-136: asset provenance records and generated-project icon seeding Two proposed goals from the src/assets/logo work, split rather than combined: "the master-to-derived chain is verifiable anywhere" and "generated projects arrive with an icon" are separate outcomes with separate acceptance, and the second merely consumes the first. G-135 - asset provenance records. Nothing currently states that src/runtime/punkshell.ico came from punk-mark.svg, so one failure is undetectable: edit the SVG, forget to regenerate, and the committed .ico silently stops depicting the master. An optional directory-scoped assetorigin.toml records the derivation; a plain-Tcl checker classifies each asset as unrecorded / verified / source-absent / replaced / stale, advisory only. Absence of a record is the DEFAULT and stays silent - a record is an opt-in claim, and every field but path is optional, so "I know this file, I don't know where it came from" is expressible. The same goal retires the Windows tie in make-ico.ps1. The dependency is one line - System.Drawing, decoding PNG to raw BGRA - since pwsh and rsvg-convert are both cross-platform and Tcl core ships zlib. G-136 - generated projects seeded from src/assets/logo/project-default/ at generation time, as a sibling to bootsupport_inject with punkcheck install records, rather than storing artwork in layout trees. Layout storage would mean three copies with no master (project-0.1, basic, and the punkcheck-managed modpod-templates copy), reversing G-087's thin-layout achievement. Because G-135's record is directory-scoped with relative paths it copies verbatim and reads `verified` at the destination. Scope for G-135 separates the two tools rather than bundling them: the generator is replaced in place under src/assets/logo/ (it operates on the files beside it and is already documented there), while the checker is a lint in the shape of goals_lint and architecture_lint and belongs in scriptlib/developer/ - with the home explicitly revisited if it becomes a bake step, since scriptlib/ is user-only and make.tcl must not depend on it. That deferral follows G-057's existing "helper proc location decided in the work" pattern. Both goals record the workflow questions deliberately kept OUT of their acceptance: when the checker runs (bake step vs subcommand), and which layouts declare an icon slot. G-057 gains a Related pointer to G-135 per the dependency-direction convention - its per-kit sidecar is specified to carry "source .ico identity and provenance", which these records are the natural source for. goals_lint clean (86 active-index goals, 50 archived); the G-133 reference carries its archive marker in Notes per the Archive rules. Claude-Session: https://claude.ai/code/session_01YNjnq6oDzecknLg7AuWZgU Assisted-by: harness=claude; primary-model=claude-opus-5[1m]; api-location=anthropic.com --- GOALS.md | 8 ++ goals/G-057-kit-icon-embedding.md | 6 + goals/G-135-asset-provenance-records.md | 115 ++++++++++++++++++ goals/G-136-generated-project-icon-seeding.md | 88 ++++++++++++++ 4 files changed, 217 insertions(+) create mode 100644 goals/G-135-asset-provenance-records.md create mode 100644 goals/G-136-generated-project-icon-seeding.md diff --git a/GOALS.md b/GOALS.md index d9f168a8..dc108ec6 100644 --- a/GOALS.md +++ b/GOALS.md @@ -414,3 +414,11 @@ Detail: goals/G-132-tcl-windows-errno-catchall-report.md ### G-134 [proposed] Baked kits pinned archive-relative: advisory offset-style probe on assembled kit images Scope: src/make.tcl (post-assembly offset-style probe at the G-133 advisory-check seam, recapped reporting); src/modules/punkboot/utils-999999.0a1.0.tm (probe predicate beside the G-133 scan helpers, guarded require; bootsupport copies via established sync channels); src/tests/modules/punkboot/utils/ + src/tests/shell/testsuites/punkexe/ (characterization); src/AGENTS.md (what the pin does and does not guarantee) Detail: goals/G-134-baked-kits-pinned-archive-relative.md + +### G-135 [proposed] Asset provenance records: a verifiable master-to-derived icon chain with a platform-neutral generator +Scope: src/assets/logo/ (assetorigin.toml records, masters and generated icons); src/assets/logo/make-ico.ps1 (replaced in place by a Tcl generator, then removed); scriptlib/developer/ (provenance checker, sibling to goals_lint and architecture_lint); checker home revisited if it becomes a bake step (a punk:: module rather than scriptlib, which make.tcl must not depend on); src/runtime/punkshell.ico and src/vfs/_vfscommon.vfs/punkshell.ico (recorded as derived copies, not relocated) +Detail: goals/G-135-asset-provenance-records.md + +### G-136 [proposed] Generated projects are seeded with a default icon from a single punkshell-held master +Scope: src/assets/logo/project-default/ (the single held seed artwork); src/modules/punk/mix/commandset/project-999999.0a1.0.tm (generation-time asset injection beside bootsupport_inject); src/project_layouts/ (per-layout icon-slot declaration with no artwork stored in layout trees); src/modules/punkcheck-999999.0a1.0.tm (install records for injected assets) +Detail: goals/G-136-generated-project-icon-seeding.md diff --git a/goals/G-057-kit-icon-embedding.md b/goals/G-057-kit-icon-embedding.md index d7a18078..0b23a762 100644 --- a/goals/G-057-kit-icon-embedding.md +++ b/goals/G-057-kit-icon-embedding.md @@ -129,6 +129,12 @@ delete-then-write). Requirements sketch agreed 2026-07-10: - Related: G-127 - once one vfs definition bakes for several targets, "which kits get an icon" becomes a target question; that is what prompted the 2026-07-26 decision recorded in the Acceptance that the override is shared across a kit definition's targets. +- Related: G-135 - the source-tree provenance records this goal's sidecar can read. The + sidecar is emitted per kit at bake time and describes what got embedded; G-135's + assetorigin.toml is committed and describes a derivation. Populating the sidecar's + "source .ico identity and provenance" from those records closes the gap where this + goal's provenance otherwise stops at the .ico bytes, one tier short of the SVG master + they were generated from. - The sidecar is the natural reconciliation point for the three provenance surfaces this file already flags (G-025's kit stamp, G-117's embedded artifact record, the parked RT_VERSION follow-on) - one text record per kit, on every platform. diff --git a/goals/G-135-asset-provenance-records.md b/goals/G-135-asset-provenance-records.md new file mode 100644 index 00000000..33a181e7 --- /dev/null +++ b/goals/G-135-asset-provenance-records.md @@ -0,0 +1,115 @@ +# G-135 Asset provenance records: a verifiable master-to-derived icon chain with a platform-neutral generator + +Status: proposed +Scope: src/assets/logo/ (assetorigin.toml records, masters and generated icons); src/assets/logo/make-ico.ps1 (replaced in place by a Tcl generator, then removed); scriptlib/developer/ (provenance checker, sibling to goals_lint and architecture_lint); checker home revisited if it becomes a bake step (a punk:: module rather than scriptlib, which make.tcl must not depend on); src/runtime/punkshell.ico and src/vfs/_vfscommon.vfs/punkshell.ico (recorded as derived copies, not relocated) +Goal: Any committed icon in the tree can state what it was derived from and be checked against that claim on any platform, and regenerating one from its master needs no Windows-only tooling - while an icon carrying no record stays a fully supported and silent case. +Acceptance: `assetorigin.toml` is documented with a `schema` key and directory-scoped relative paths, and a reader tolerates an absent file, unknown keys and an unrecognised schema version without erroring; a checker written in plain Tcl - no rasterizer, no external binary, runnable under any punkshell runtime - classifies each recorded asset as exactly one of `unrecorded`, `verified`, `source-absent`, `replaced` or `stale`, demonstrably distinguishing `stale` (source changed since the artifact was generated) from `replaced` (artifact changed since recording) on constructed fixtures, and is advisory only, never failing a build, in the manner of the G-133 payload checks; the icon generator is reimplemented with no Windows-only component (`System.Drawing` in particular), producing byte-identical `.ico` output to the current `make-ico.ps1` for every master under `src/assets/logo/` before that script is removed; and records exist and verify for `src/assets/logo/`, `alternative/`, `web/` and `project-default/`. + +## Context + +Before 2026-07-27 the tree held several byte-identical `punk1.ico` files with +nothing above them. An `.ico` is a compiled artifact - a container of +pre-rasterized bitmaps - so there was no file anyone could edit, no recorded way +to produce a new one, and no way to verify that any copy was what someone +intended. + +`src/assets/logo/` (91fa500e) introduced a vector master and a generator, +establishing three tiers: the SVG master, the generated `.ico`, and the +build-consumed copies (`src/runtime/punkshell.ico`, `_vfscommon.vfs`, +`mkzipfix.vfs`, `punk8_statictwapi.vfs`). What is still missing is any record +that ties the tiers together. Nothing states that `src/runtime/punkshell.ico` +came from `punk-mark.svg`, so one specific failure is undetectable: someone +edits the SVG, forgets to regenerate, and the committed `.ico` silently stops +depicting the master. + +Design-time and bake-time are separate problems, and conflating them is what +makes the manual copy into build trees feel wrong when it is not. G-057's +acceptance requires that "a fresh checkout that fetches a runtime and bakes +produces iconed kits with no build toolchain installed" - a rasterizer IS a +build toolchain, so SVG-to-raster can never happen at bake time on any platform, +including Windows. The `.ico` must remain a committed artifact. What is missing +is therefore not automation but a recorded, checkable link. + +The generator's platform tie is narrower than it appears. `pwsh` and +`rsvg-convert` are both cross-platform; the entire Windows dependency is one +line - `Add-Type -AssemblyName System.Drawing`, used solely to decode a PNG into +raw BGRA pixels, which throws on non-Windows under .NET 6+. Tcl core ships +`zlib`, so PNG decode is inflate plus undoing the five row filters, and the ICO +container assembly downstream is pure `binary format`. + +## Approach + +- The record is OPTIONAL by construction. Absence means `unrecorded` - silent, + never a warning. A record is an opt-in claim, and nothing may read it to decide + whether a build proceeds. Most projects live in this state permanently. +- Directory-scoped `assetorigin.toml` carrying an `[[asset]]` array with + RELATIVE paths, so it survives being copied wholesale into a derived tree with + no rewriting (the property G-136 depends on). +- Every field except `path` is optional. An entry carrying only a path and a + hash means "I know this file, I do not know where it came from" - which serves + an owner who drops in their own `.ico` but still wants change detection. +- Unknown keys and unrecognised `schema` versions are skipped, not errors, so a + future writer cannot break an older reader. +- Five states, with policy left to the consumer rather than baked into the + format: + + | State | Condition | + |---|---| + | `unrecorded` | no record file | + | `verified` | artifact and source hashes both match | + | `source-absent` | artifact matches, source not present in the tree | + | `replaced` | artifact hash differs from the record | + | `stale` | artifact matches the record, source present but changed | + + `stale` is the point of the whole goal; it is also distinguishable from + `replaced`, which means the opposite thing and deserves the opposite response. +- A `role` field (`pe-resource` | `web` | `generic`) makes an existing hazard + machine-checkable rather than a documentation footnote: `web/favicon.ico` is + all-PNG, which is exactly what GDI+ and tklib's `ico` package cannot read, so a + future embedding step can refuse anything marked `role = "web"`. +- Generation and verification are separable and get different treatment. + Verification must be portable and dependency-free, because it is what runs + everywhere - including inside a bake and inside a derived project. Generation + needs a rasterizer and runs only when artwork changes, so it may depend on + `rsvg-convert` while still carrying no Windows-only component. + +## Alternatives considered + +- `PROVENANCE.md` prose, the `src/tools/punkzip` convention - rejected: human + prose is not machine-checkable, and the failure this goal targets is silent + drift, which needs hashes. +- `punkorigin.toml`, G-027's generated-project carrier - rejected: that answers + project lineage ("this project came from punkshell layout X"), a different + question from asset derivation. Keep them separate. +- Per-file sidecars instead of one record per directory - rejected: + `src/assets/logo/web/` alone would carry six, and a directory record with + relative paths is what makes verbatim copying work. +- Making the `.ico` a build output regenerated at bake time - rejected: G-057 + requires bakes to succeed with no build toolchain installed. + +## Notes + +- Related: G-057 - consumer. Its per-kit sidecar is specified to carry "source + .ico identity and provenance" in a documented plain-text form; these records + are the natural thing it reads to populate those fields, which closes the gap + where its provenance otherwise stops one tier short of the actual source. Its + Scope names `src/runtime/punkshell.ico` as the project default, which this goal + reframes as a recorded derived copy rather than the origin. +- Related: G-128 - sibling surface. Portable PE resource stamping shares this + goal's motivation that icon handling must not be Windows-bound. +- The advisory posture the Acceptance borrows is G-133's (achieved 2026-07-27, + archived at goals/archive/G-133-bake-payload-consistency-checks.md): its bake + payload checks recap as BUILD-WARNINGs and never fail the build. +- Generator and checker have different homes on purpose. The generator operates + on the files beside it, runs only when artwork changes, and is already + documented in `src/assets/logo/AGENTS.md`, so it is replaced in place. The + checker is a lint in the shape of `goals_lint.tcl` and `architecture_lint.tcl` + and belongs in `scriptlib/developer/` - unless it becomes a bake step, in which + case it must move to a `punk::` module, because `scriptlib/` is user-only + territory that `make.tcl` must not depend on. That decision is deliberately + deferred to the work. +- Open workflow question, deliberately kept out of the Acceptance: whether the + checker runs during `make.tcl bake` or only as an explicit verification + subcommand. `stale` is a design-time mistake rather than a build-time one, which + argues for a subcommand plus an opportunistic check only when a bake actually + consumes an icon. diff --git a/goals/G-136-generated-project-icon-seeding.md b/goals/G-136-generated-project-icon-seeding.md new file mode 100644 index 00000000..17b23cc4 --- /dev/null +++ b/goals/G-136-generated-project-icon-seeding.md @@ -0,0 +1,88 @@ +# G-136 Generated projects are seeded with a default icon from a single punkshell-held master + +Status: proposed +Scope: src/assets/logo/project-default/ (the single held seed artwork); src/modules/punk/mix/commandset/project-999999.0a1.0.tm (generation-time asset injection beside bootsupport_inject); src/project_layouts/ (per-layout icon-slot declaration with no artwork stored in layout trees); src/modules/punkcheck-999999.0a1.0.tm (install records for injected assets) +Goal: A newly generated project arrives with a working and inspectable icon - a readable vector master plus a ready-to-use `.ico` - injected from punkshell's single held copy at generation time, with no icon artwork stored in any layout payload tree, and regeneration never silently discards an owner's replacement. +Acceptance: `dev project.new` against a layout declaring an icon slot produces `src/assets/logo/` in the generated project containing `icon.svg`, `icon.ico` and an `assetorigin.toml` that the G-135 checker reports as `verified` in that tree (source and artifact both present, hashes matching), with the artwork sourced from `src/assets/logo/project-default/` at generation time rather than from any file under `src/project_layouts/` - verified by that tree containing no icon `.svg` or `.ico` payload; each injected file is recorded as a punkcheck install event in the same manner as `bootsupport_inject`; a layout declaring no icon slot generates a project with no `src/assets/logo/` and no warning; and re-running generation over a project whose `icon.svg` or `icon.ico` differs from the injected original leaves the owner's file intact and reports the skip by name. + +## Context + +A punkshell-generated project currently gets no icon at all. Its owner starts +from nothing, with no indication that an icon slot exists or what shape it takes. + +G-087 (achieved) established the pattern this goal follows: thin layouts carry +structure rather than stored payload, and `project.new` injects bootsupport +modules and libs from the generating shell at generation time +(`bootsupport_inject` in `project-999999.0a1.0.tm`, each copy recorded as a +punkcheck install event). + +Storing icon artwork in the layout trees instead would reintroduce exactly what +G-087 removed. There are two layouts (`vendor/punk/project-0.1` and +`vendor/punk/basic`) plus a synced copy under +`src/modules/punk/mix/#modpod-templates-*/` that is punkcheck-managed build +output and must never be hand-edited - so layout-stored artwork means three +copies with no master. + +`src/assets/logo/project-default/` (added 2026-07-28) holds the single copy: a +frozen monochrome placeholder, a blank screen wearing the crest, deliberately +unbranded so it reads "replace me" rather than as a product identity. + +## Approach + +- Inject at generation time from the single held copy, as a sibling to + `bootsupport_inject`, hung off the phase that already seeds `punkproject.toml`, + `CHANGELOG.md` and `punkorigin.toml`. Reuse the punkcheck install-record + approach of `_bootsupport_inject_copyfile` so seeded assets are tracked like + every other injected file. +- Path symmetry, which is worth having deliberately: authored at punkshell's + `src/assets/logo/project-default/`, landing at the generated project's + `src/assets/logo/`. The same relative shape at both ends makes the structure + self-explanatory to someone opening a fresh project. +- Ship BOTH the `.svg` master and the generated `.ico`. Producing an `.ico` needs + a rasterizer, and requiring one of a fresh project would defeat the purpose; + the pair also gives the owner a complete worked example of the master-to-derived + relationship, which no amount of documentation prose achieves. +- The `assetorigin.toml` copies verbatim. Because G-135's record is + directory-scoped with relative paths, it needs no rewriting at the destination + and reads `verified` there - source and artifact are both present in the + generated tree. +- Per-layout icon-slot declaration. A layout may decline, and declining is not a + failure or a warning. +- Never clobber a replacement. Re-running generation over a project whose icon + differs from the injected original leaves it alone and says which file it + skipped. + +## Alternatives considered + +- Store the artwork in each layout payload tree - rejected: three copies with no + master, one of which is punkcheck-managed build output that must not be + hand-edited, and it reverses G-087's thin-layout achievement. +- Ship only the `.svg` master - rejected: the generated project would have no + usable icon until its owner installed a rasterizer. +- Seed punkshell's own `punk-mark` artwork - rejected: every generated project + would ship punkshell's identity in its taskbar. A de-branded placeholder is + what invites replacement; a neutral grey square would not, which is why the + placeholder keeps the crest silhouette while dropping the colour and the + brackets. +- Sync `project-default/` from `punk-mark.svg` - rejected: it is a placeholder, + not a mirror of punkshell's identity. Tracking the real mark would churn every + generated project and re-date its provenance record on every logo tweak. + +## Notes + +- Related: G-135 - depends on. Consumes the `assetorigin.toml` format and the + checker, and relies specifically on the record being directory-scoped with + relative paths so it survives injection unchanged. +- Related: G-027 - consumer. Pull-based infrastructure updates for derived + projects would later refresh seeded assets, and both goals record their + installs through punkcheck. +- Related: G-086 - interaction. Whether the light layout declares an icon slot is + decided there; this goal's acceptance is written so a layout may decline + without that counting as a failure. +- Related: G-012 - sibling surface. The overwrite-on-regeneration behaviour is + the same question as its explicit layout-refresh semantics. +- The placeholder's geometry carries two constraints that are easy to undo by + accident, recorded in `src/assets/logo/AGENTS.md`: bezel and crest are ONE + union path (as two shapes the bezel's top edge cuts a visible line across the + mohawk), and the screen is filled rather than outlined (a solid mass survives + 16px where nested thin strokes merge).