Thin-layout sync (part of the bootsupport run): the project-layout
make.tcl copies (vendor/punk/basic, vendor/punk/project-0.1, and the
punk::mix::templates modpod source tree) pick up the G-126 'tool'
subcommand from d76a7907 via the punkcheck-managed sync step. No hand
edits.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
Field failure (2026-07-27, second dev machine): 'make.tcl bake
punk9_beta' died in the vfslibs phase with
eventcollection ... key '$eventid' already exists in collection
because that machine's src/vfs/.punkcheck predates punkcheck 0.3.2.
The 0.3.2 note fixed the WRITER (a braced template wrote the literal
strings '$eventid' etc into event headers) but files written before it
keep the damage, and the modern installtrack constructor - which
reloads persisted events into a collection keyed by -id - collided on
the duplicate literals and aborted the whole bake.
- punkcheck 0.6.1: the constructor reload now self-heals instead of
aborting: an EVENT record with a missing or duplicate -id is
reconstructed under a synthesized unique id ('damaged-id-...') with
a stderr warning naming the file and the offending id. Synthesized
ids match no INSTALL record's -eventid reference, so healed events
age out through normal -keep_events pruning. Reload also tolerates
missing -source/-targets keys.
- oolib 0.1.4: the collection duplicate-key error no longer misnames
the object as 'col_processors' (stray copy-paste in the generic
class).
- regression test installtrack_damaged_event_history_selfheal seeds a
real two-event file, rewrites both -id values to the braced literal
{$eventid} (as the damaged files carry it - the record loader
evaluates records, so only a braced literal survives as the string),
and asserts the reload succeeds with both events kept under distinct
keys (one healed). Suite: 30/30.
- before/after proof against the released artifacts: punkcheck 0.6.0
(exact-pinned) reproduces the field abort on the crafted file;
0.6.1 heals it. Bootsupport promoted to punkcheck-0.6.1 + oolib-0.1.4
(superseded copies pruned), so make.tcl bakes get the fix.
Remedy already applied on the affected machine (file deletion); with
this fix, remaining old .punkcheck files anywhere self-heal with a
warning instead of failing builds.
Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
3 days ago
11 changed files with 1329 additions and 22 deletions
#(the record keys are -ts_begin/-ts_end - passing the raw record gave the constructor's
# -tsbegin/-tsend defaults, so reconstructed events took 'now' as begin and empty end,
# and any later flush/save_installer_record rewrote prior event history with those values)
set eid [punkcheck::dict_getwithdefault $e -id ""]
#self-heal damaged persisted history rather than abort the caller's operation:
#files written by punkcheck < 0.3.2 carry EVENT records whose -id (and
#-source/-targets/-config) hold the LITERAL strings of the old writer's braced
#template ('$eventid' etc - the 0.3.2 buildversion note fixed the writer, but
#files keep the damage) and any two such records collide on the duplicate key
#here. History is advisory - reconstruct the record under a synthesized unique
#id and warn naming the file, instead of failing e.g a whole bake's vfslibs
#phase. Synthesized ids match no INSTALL record's -eventid reference, so
#healed events age out through normal -keep_events pruning.
if {$eid eq "" || $eid in [my events keys]} {
set healed_id "damaged-id-[my events count]-[clock microseconds]"
set shown [string range $eid 0 60]
set why [expr {$eid eq "" ? "missing" : "duplicates an earlier event"}]
puts stderr "punkcheck: WARNING damaged event history in '$o_checkfile': event -id '$shown' $why - keeping record under synthesized id '$healed_id' (safe to ignore; delete the file to reset history)"
#0.1.4 - collection add duplicate-key error message no longer misnames the object as 'col_processors' (stray copy-paste in the generic collection class)
#(the record keys are -ts_begin/-ts_end - passing the raw record gave the constructor's
# -tsbegin/-tsend defaults, so reconstructed events took 'now' as begin and empty end,
# and any later flush/save_installer_record rewrote prior event history with those values)
set eid [punkcheck::dict_getwithdefault $e -id ""]
#self-heal damaged persisted history rather than abort the caller's operation:
#files written by punkcheck < 0.3.2 carry EVENT records whose -id (and
#-source/-targets/-config) hold the LITERAL strings of the old writer's braced
#template ('$eventid' etc - the 0.3.2 buildversion note fixed the writer, but
#files keep the damage) and any two such records collide on the duplicate key
#here. History is advisory - reconstruct the record under a synthesized unique
#id and warn naming the file, instead of failing e.g a whole bake's vfslibs
#phase. Synthesized ids match no INSTALL record's -eventid reference, so
#healed events age out through normal -keep_events pruning.
if {$eid eq "" || $eid in [my events keys]} {
set healed_id "damaged-id-[my events count]-[clock microseconds]"
set shown [string range $eid 0 60]
set why [expr {$eid eq "" ? "missing" : "duplicates an earlier event"}]
puts stderr "punkcheck: WARNING damaged event history in '$o_checkfile': event -id '$shown' $why - keeping record under synthesized id '$healed_id' (safe to ignore; delete the file to reset history)"
#0.6.1 - installtrack constructor self-heals damaged persisted event history instead of aborting the caller: EVENT records with a missing or duplicate -id (notably the literal '$eventid' records written by punkcheck < 0.3.2 - the 0.3.2 note fixed the writer but existing files keep the damage, and the modern keyed event reload collided on them, killing e.g a whole bake's vfslibs phase; first field hit 2026-07-27 on a machine whose src/vfs/.punkcheck predated 0.3.2) are reconstructed under synthesized unique ids ('damaged-id-...') with a stderr warning naming the file. Synthesized ids match no INSTALL record's -eventid reference, so healed events age out through normal -keep_events pruning. Event reload also tolerates missing -source/-targets keys. Regression test: src/tests/modules/punkcheck/testsuites/punkcheck/installtrack.test (installtrack_damaged_event_history_selfheal).
#0.6.0 - G-095 concurrent-writer safety: saves are write-temp-then-atomic-rename (bounded retry for Windows sharing violations) so readers never see torn content, and load_records_from_file retries transient open denials during a writer's replace window (parse errors stay strict). New punkcheck::lock namespace: advisory sibling lockfile <punkcheckfile>.lock (open WRONLY CREAT EXCL, holder pid/host/installer/timestamp contents, channel held open, in-process refcounted re-acquisition) serializes whole installer events - acquired at start_event, released at event end / installtrack destroy; flushes and chokepoint saves outside an event take a transient lock; contention retries with backoff to -timeout (env PUNKCHECK_LOCK_TIMEOUT) then errors naming the holder (errorcode PUNKCHECK LOCK TIMEOUT); stale locks break on provably-dead holder pid (twapi/kill -0, capability-gated) or age (-staleage / PUNKCHECK_LOCK_STALEAGE). Deferred flushes merge own records (INSTALLER by -name, touched FILEINFO by -targets) into freshly-loaded file state instead of wholesale overwrite; an mtime/size tripwire warns when a non-protocol writer touched the file between load and save. Duplicate-INSTALLER recovery degrades to a clean actionable error when stdin is non-interactive (new punkcheck::lib::stdin_is_interactive; twapi GetConsoleMode on the real stdin handle / unix -inputmode / test -t 0), and the interactive prompt gains the synced-targets deletion-wedge caveat. default_excludefiletail_core adds .punkcheck.* so lock/temp siblings are never treated as installable content.
#0.5.0 - G-094 single record lifecycle: installtrack/installevent OO layer is the sole implementation (targetset init/add-source/started/end) with a per-event persistence policy (start_event ?-persistence eager|deferred?; deferred = in-memory working recordset + $installer flush, the mode punkcheck::install now uses as a tree-walking OO consumer). Legacy proc pipeline (start_installer_event, installfile_begin/started_install/finished_install/skipped_install) retired to error-with-pointer shims. All saves flow through one chokepoint (installtrack save_working_recordset - G-095 attachment point). Fixes: start_event refreshes the working recordset (sequential events no longer clobber prior targetset writes; end/end_event now clears the active event so sequential events work), installtrack as_record no longer writes -name with a trailing space, event reconstruction maps -ts_begin/-ts_end (flush no longer rewrites prior event history timestamps), get_recordlist misspelled variable, targetset_dict called a nonexistent helper. punkcheck::install records now carry -metadata_us/-ts_start_transfer/-transfer_us/-cksum_us like OO-installed records, and all-targets mode now stores -targets_cksums. recordlist::extract_or_create_fileset_record no longer prints to stdout (isnew returned; targetset_init reports via debugchannel). Dead targetset class removed.
#0.4.0 - cross-root sources: installfile_add_source_and_fetch_metadata now records sources that share no common root with the punkcheck folder (e.g //zipfs:/ module-mounted sources installing to the filesystem) as independent absolute paths via the cksum helpers' empty-base mode, instead of erroring 'don't share a common root'. Enables punkcheck::install from module-carried (vfs-mounted) layout payloads - G-087 stage 3 bare-kit project generation.