diff --git a/GOALS.md b/GOALS.md index e37b0944..650656b9 100644 --- a/GOALS.md +++ b/GOALS.md @@ -402,3 +402,7 @@ Detail: goals/G-166-cmdinfo-unavailable-surfacing.md Scope: src/modules/punk/args-999999.0a1.0.tm (form selection/candidacy availability filter + tailored raises, option-scan unavailable-name recognition, synopsis/table renderers - annotated form lines, unavailable option rows, automated other-shapes note, formstatus 'available' field, -form display-vs-parse split); src/modules/punk/ns-999999.0a1.0.tm (cmdhelp -return text form-section heading annotation; form narrowing inherits the availability filter); src/modules/punk/args/moduledoc/tclcore-999999.0a1.0.tm (adoption on curated real 8.6-vs-9 deltas); src/tests/modules/punk/args/testsuites/args/ (new suite + formviability/tclcoreparity extensions) Detail: goals/G-167-punkargs-version-delta-availability.md +### G-171 [proposed] Routine builds never write tracked paths - explicit commands own VCS-visible changes +Scope: src/make.tcl (bake/bakehouse/vfslibs - VFSPAYLOAD + PACKAGES phases, kit assembly, new explicit materialize/tidy commands); src/vfs/ (payload declarations, _targets staging generalization, README.md + AGENTS.md precedence spec); src/_bake (assembly staging); bin/packages (lib-tier consumption); .gitignore + .fossil-settings/ignore-glob (staging ignore coverage); src/tests/shell/testsuites/punkexe/ (kill-probe + precedence characterization) +Detail: goals/G-171-bake-writes-land-untracked.md + diff --git a/goals/G-171-bake-writes-land-untracked.md b/goals/G-171-bake-writes-land-untracked.md new file mode 100644 index 00000000..92837deb --- /dev/null +++ b/goals/G-171-bake-writes-land-untracked.md @@ -0,0 +1,124 @@ +# G-171 Routine builds never write tracked paths - explicit commands own VCS-visible changes + +Status: proposed +Scope: src/make.tcl (bake/bakehouse/vfslibs - VFSPAYLOAD + PACKAGES phases, kit assembly, new explicit materialize/tidy commands); src/vfs/ (payload declarations, _targets staging generalization, README.md + AGENTS.md precedence spec); src/_bake (assembly staging); bin/packages (lib-tier consumption); .gitignore + .fossil-settings/ignore-glob (staging ignore coverage); src/tests/shell/testsuites/punkexe/ (kill-probe + precedence characterization) +Goal: routine build operations (bake/bakehouse/vfslibs and every phase they run) write only to VCS-ignored locations - declared payloads materialize into ignored staging and merge-time layering enacts drop-in-wins precedence identically on every machine - so an interrupted, buggy, or stale-tool bake cannot modify, delete, or shadow tracked files and git/fossil status stays truthful during and after any build; deliberate writes into tracked .vfs folders (materializing a fetched/built package as drop-in content, retiring superseded committed files) remain available only as explicit separately-invoked commands whose output is an ordinary reviewable VCS change, with help and docs steering to the declaration-based route by default. +Acceptance: from a clean tree, a full bake/bakehouse run - including runs killed at arbitrary mid-run points (kill-probe characterization) - leaves git status and fossil changes reporting nothing outside ignore-covered paths; the VFSPAYLOAD/PACKAGES phases self-check their write destinations and abort before writing any tracked path; a hand-dropped file shadowing a declared payload entry survives assembly with drop-in-wins precedence on a records-free fresh clone (characterized, with the unified regime including _targets documented in src/vfs/README.md); the explicit materialize/tidy command(s) exist and are documented as the sole sanctioned tracked-tree write path, and no bake/bakehouse phase invokes them; a bake -hold run stops after assembly leaving the resolved per-kit images inspectable in staging, and a subsequent plain bake completes to kits identical to a single uninterrupted bake - staged assembly is reused only under digest-confirmed input currency, and stale or hand-modified held state is redone, never shipped. + +## Context + +The kit merge already runs entirely in ignored staging (src/_bake). The only step +writing tracked paths is payload materialization: G-115's declarations (achieved +2026-07-31 - see goals/archive/G-115-declarative-vfs-composition.md) automate filling +the same .vfs folders that were previously hand-assembled, so machine writes land in +tracked space as an evolutionary artifact, not an architectural necessity. + +Three structural problems follow. (1) Drop-in-wins is enacted in place via punkcheck +records that are machine-local and VCS-ignored, so the authored-vs-installed +distinction does not travel between machines - replace=true exists to wipe rather +than reason, which is exactly the operation that strands trees when a bake dies. +(2) Two precedence regimes coexist: in-folder materialization preserves drop-ins, +while _targets staging (G-127, achieved - see +goals/archive/G-127-crosstarget-vfs-bake.md) merges OVER the per-kit .vfs. +(3) Any bake failure mid-materialization leaves tracked files modified/deleted - +uninterpretable for a non-developer, and the reason three separate mid-bake-status +traps are recorded in project memory. + +Incident evidence 2026-08-06: on a consumer-shaped machine (reset --hard + pull + +fetch + bake), a stale untracked bin/punkzip.exe (pre-2.4.0) received the v2.4.0 +-b flag, wrote a misdirected 90MB archive to src/-b, the kit-container step failed, +and the died bake stranded half-replaced tracked trees across the declared kits - +a wall of git modifications/deletions with no user-facing recovery story. Under +this goal that whole class strands only ignored staging: recovery = rerun. + +## Approach + +1. Generalize the G-127 staging pattern: ALL declared payload entries (not just + %platform% forms) materialize into ignored staging; the tracked .vfs folder is + never a materialization target of any routine phase. +2. Unify precedence at merge time: staging merged first, authored .vfs merged over + it (drop-in wins), supersedes/replace expressed as merge-time masks; record the + deliberate flip of the current _targets-over-.vfs order; document in + src/vfs/README.md; characterize. +3. Explicit commands own tracked-tree writes: a tidy command retiring superseded + committed files (report-then-act), and a materialize command that deliberately + lands a fetched/built package into a .vfs folder as drop-in content for the + developer/builder persona (user direction 2026-08-06) - both producing ordinary + reviewable VCS changes, with help text steering to declarations by default. +4. Defense in depth: the phases enumerate write destinations and abort before + touching any tracked path. +5. Kill-probe characterization: clean tree in, clean tree out, at arbitrary kill + points. +6. Optional interim hardening (independent value if migration is staged): + temp-sibling + atomic-swap for the current in-folder materialization, removing + the stranded-mid-replace mode before the full move lands. +7. Hold point: 'bake -hold' stops after assembly (post payload-staging + merge + + per-target selection, pre container/deploy) with the resolved per-kit images + in staging for inspection; a subsequent plain bake completes. Deliberate holds + and interrupted bakes unify: any completing run re-verifies staged assembly + against per-kit input digests (the G-162 machinery, write-side) and reuses + only what is confirmed current. Held staging is machine-owned - inspection is + sanctioned, hand-edits are not, and a digest mismatch means redo, not ship. + bakelist surfaces held state alongside its freshness verdicts. The + project-version gate applies to both invocations. +8. DOX revision: src/vfs/AGENTS.md + README.md - the authored .vfs remains the + drop-in surface; the resolved view is src/_bake (optionally a resolved-view + report); revise the "operative assembly area" language accordingly. +9. Coordinate with G-162: digest inputs become authored .vfs + staging (or + declarations + tier); the digest infrastructure serves both bakelist's + read-side verdicts and hold/resume's reuse decision (Approach 7); record the + decision in both files. + +## Alternatives considered + +- Generalized per-kit ignore lines (punkdeclare pattern for every kit) - workable + interim, rejected as endpoint: machine writes still land inside tracked folders, + ignore bookkeeping grows per kit, authored-vs-machine mixing persists, and + supersedes still mutates tracked files from inside bake. +- Hardening only (step 6 atomic swap) - rejected as endpoint: shrinks the window + but any bake bug can still mutate tracked files; the class survives. +- Requiring committed payload removal first - rejected as sequencing: that is + G-004's own arc (artifact coverage per package); this goal works with committed + payloads still present and decouples their eventual removal from bake behaviour. + +## Notes + +- Related: G-004 - parent hygiene arc; write-side complement: bake stops being the + maintainer of committed payload copies, so G-004's per-package removals decouple + from bake behaviour. No dependency on G-004's completion. +- Related: G-157 - sibling commit-policy goal; shares the working-modes-stay- + first-class posture (uncommitted drop-ins and derived-project permissiveness + remain untouched). +- Related: G-162 - input-freshness digests consume payload trees; the staging move + changes the digest input set, and the per-kit digest machinery is shared with + the hold/resume seam (Approach 7, 9). +- Related: G-065 - declarative vendoring is the source-side sibling surface; its + sync command is the same explicit-command pattern, and its audit's + undeclared-drop-in reporting complements an authored-only .vfs. +- Related: G-141 - consumption-driven libfetch selection reads the same + *.vfs.toml declarations; unaffected by the write-location move. +- Related: G-147 - fetch-side sibling from the same 2026-08-06 evidence + (content-keyed tier tree currency). +- Related: G-006 - consent posture untouched: this goal moves write locations + only; every network-consent gate stays as-is. +- Related (archived): G-115 (achieved 2026-07-31 - see + goals/archive/G-115-declarative-vfs-composition.md) - the declarative surface + consumed here; this goal deliberately revises its "folder remains the operative + assembly area" stance: the authored folder stays the drop-in surface, + machine-written content leaves it. +- Related (archived): G-127 (achieved - see + goals/archive/G-127-crosstarget-vfs-bake.md) - the ignored-staging + merge + mechanism generalized here, and the origin of the second precedence regime this + goal unifies. +- Related (archived): G-155 (achieved 2026-08-02, archived) - the _bake/_mint + stage-workdir vocabulary this staging extends. +- Tool-level fixes from the 2026-08-06 incident (punk::zip accelerator version + handshake before emitting v2.4.0-only flags; punkzip strict rejection of unknown + dash-flags) harden the trigger and are deliberately outside this contract - this + goal removes the blast radius, not the trigger. +- Overlap survey 2026-08-06 (goals_xref paths over src/vfs, src/make.tcl, + src/_bake, bin/packages): related in substance - G-004, G-141, G-065, G-162, + plus policy/evidence siblings G-157, G-147, G-006 added conceptually; surveyed + and judged not related in substance: G-089, G-114, G-131 (boot/_config surface), + G-019 (payload content trimming), G-020 (Scope names a lib_tcl9 path only), + G-116 (tcltls payload constituent - its removal stays G-004-era work).