You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

6.5 KiB

G-094 punkcheck single record lifecycle: OO installtrack as sole implementation with a persistence policy

Status: active Scope: src/modules/punkcheck-999999.0a1.0.tm (installtrack/installevent lifecycle unification, installfile_* retirement, punkcheck::install as OO consumer), src/modules/punk/mix/cli-999999.0a1.0.tm + src/make.tcl (consumers verified unchanged), src/tests/modules/punkcheck/testsuites/punkcheck/ (characterization + new equivalence/staleness coverage) Goal: The installtrack/installevent OO layer is the only implementation of the .punkcheck record lifecycle (targetset init / add-source / started / end), with an explicit per-event persistence policy - eager per-operation read-modify-write (current OO behaviour) and deferred event-scoped flush (current batch behaviour, QUERY's no-save being the in-object precedent) - punkcheck::install rebuilt as a tree-walking consumer of that layer in deferred mode, the zero-caller legacy pipeline (installfile_begin / installfile_finished_install / installfile_skipped_install) retired to error-with-pointer shims, and every .punkcheck save flowing through a single chokepoint. Acceptance: characterization suites install.test and installtrack.test pass (pins flipped only where this goal's contract deliberately changes behaviour, each flip recorded); a new test pins field equivalence between batch-installed and OO-installed records (including -targets_cksums, which the legacy proc pipeline never stored); a new regression test covers o_record_list coherence - two sequential events on one installtrack instance, with targetset writes in the first event, lose nothing at the second start_event; grep of src/modules, src/make.tcl and src/project_layouts finds no live caller of the retired trio outside shims and tests; a full make.tcl build completes with skip/copy decisions unchanged against pre-refactor .punkcheck state (no spurious recopies of unchanged sources).

Context

2026-07-19 module review found three parallel implementations of the record lifecycle, with drift:

  • Legacy proc pipeline (installfile_begin/started/finished/skipped + start_installer_event): frozen at an earlier design - INSTALL-only (no MODIFY/DELETE/VIRTUAL/QUERY), no FAILED status, no -note, and installfile_finished_install stores no post-install target cksums, so its records can never serve -overwrite synced-targets. Zero live callers of begin/finished/skipped anywhere (punk::mix::cli's calls are commented out next to their OO replacements); they are kept alive only by installfile_help and the characterization tests.
  • OO layer (installtrack/installevent): targetset_started, targetset_addsource and targetset_addsource_virtual delegate down to the legacy procs, while targetset_init and targetset_end are parallel rewrites of installfile_begin and finished/skipped with the extensions above - a half-inversion of the desired layering (procs should be the facade, the OO lifecycle the substrate).
  • Batch punkcheck::install: bypasses both - uses start_installer_event and the metadata fetcher but inlines its own begin and finalize so it can defer all persistence to one end-of-run save, independently re-growing -targets_cksums. Its in-memory recordset handling is the deferred persistence mode this goal makes first-class.

Latent single-process clobber: start_event saves the whole constructor-era o_record_list, which targetset operations never refresh (they re-load from file into locals) - a second start_event on one instance would discard every FILEINFO update the first event's targetsets wrote. Unexercised-path evidence: installtrack get_recordlist reads a misspelled variable ($o_recordlist) and targetset_dict calls a nonexistent punk::records_as_target_dict - both would error if ever called.

Performance shape that motivates the policy knob rather than "just save eagerly everywhere": eager OO costs three full parse+prettyprint rewrites of a MB-scale file per installed target (modules/.punkcheck is ~1.9MB), which is why the batch path defers; deferring must be a first-class mode of the one lifecycle, not an ad-hoc bypass with its own record code.

Approach

  • Persistence policy selected per event (eager | deferred); in deferred mode the installtrack's in-memory recordset is authoritative between flushes, so the o_record_list coherence fix is a precondition, not a side quest.
  • installfile_add_source_and_fetch_metadata stays as the shared pure engine - it is the one correctly-layered piece (record transform + filesystem metadata fetch, no saves), used by all three current paths.
  • start_installer_event's role is subsumed by installtrack construction + start_event; the surviving convenience surface for callers is punkcheck::install / install_tm_files / install_non_tm_files as thin OO consumers.
  • Single save chokepoint: all writes to a .punkcheck file (event start, eager targetset ops, deferred flush) route through one proc/method - this is the attachment point G-095 locks onto.

Alternatives considered

  • Making the legacy procs true facades that construct transient event objects while preserving their dict-passing contract - rejected: zero live callers make the reconstitution ceremony pointless; error-with-pointer shims are honest and cheaper.
  • Locking/concurrency first, refactor later - rejected: the safety measures would have to be implemented and kept in sync across three lifecycle implementations; sequencing the unification first means G-095 lands once.

Notes

  • Prerequisite for G-095 (concurrent-writer safety) - that goal's lock, atomic-rename and merge-on-flush measures assume this goal's single save chokepoint.
  • Version-skew during rollout: bootsupport punkcheck (0.4.0) and the dev module can both write the same tree's .punkcheck files (make.tcl runs bootsupport's copy; a dev shell runs the built one) - re-vendor bootsupport promptly after landing, and sequence this refactor before any on-disk protocol change from G-095.
  • G-087 (archived - goals/archive/G-087-thin-project-layouts.md) recorded a punkcheck MAX_PATH candidate (deep relative source chains exceed Windows path limits, unnormalized) - natural rider on this goal's load/save consolidation. Reconfirmed 2026-07-19 during the stage-4 verification: project generation into a deep directory fails reading the layout store manifests through a long ../ chain.
  • The characterization suites were written expressly to lock behaviour "before any refactoring" - this is that refactoring.
  • punkcheck::cli (status/paths) is a read-only consumer and stays untouched.