feat(implement): stage piece-wise partials off the bootable file, publish atomically - #67
Merged
Merged
Conversation
…lish atomically Measured live (greenhouse fixture series, run 12): mode=start/append wrote each partial DIRECTLY to the live plugin source the app registers and loads at boot. An append left a class with an unclosed brace; that half-written file was loaded at boot, so the whole app — the agent that had to repair it included — died inside a process that would no longer boot. A partial must never be a bootable file. The staging discipline: - STAGING_SUFFIX (`.milpa-part`): the partial lives at `<scaffold>.php.milpa-part`, a sibling whose path does NOT end in `.php`, so every autoloader/glob keyed on the `*.php` extension is blind to it and can never load it as a source. - mode=start writes the first section to STAGING (truncating stale staging), NOT the live file; mode=append appends verbatim to STAGING. The live scaffold stays byte-identical to what `make` left through the whole authoring — a mid-authoring boot loads valid PHP. - mode=append/finish now require the STAGING file to exist (the scaffold existing is no longer sufficient); its absence teaches mode=start first. - mode=finish reads the assembly from STAGING and judges it through the SAME landing gate a single-shot passes. On GREEN it publishes atomically and deletes staging; on RED the live file stays the untouched scaffold and staging is KEPT, so the caller can append a fix and finish again. The bootable file never carries a red assembly. The landing gate is one code path for both doors and now lands through publishAtomically (temp file + rename): rename(2) is atomic on POSIX, so a crash never leaves the live file half-written — single-shot included. The temp lives in the target's own directory (a cross-device rename would fail) and inherits the scaffold's file mode, so the durability is invisible past the bytes. The 0.22 equivalence still holds: start+append+finish over a split of C lands the live file byte-identical to single-shot with C, same verify verdict. Falsifiers (tests/Operations/ImplementHandlerTest.php): D-11 core (live scaffold byte-identical through start+append; the deliberately-unclosed partial only at staging), finish publishes atomically on green and deletes staging, finish discards on red keeping staging, append/finish refused without staging even when the scaffold exists, single-shot leaves no staging sibling, publish preserves the file mode. testAppendIsVerbatimByteConcatenation strengthened to assert the bytes on staging AND the live scaffold untouched (on 0.22 the same append wrote straight into the live file).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The root fix for the killer of run 12 (D-11): implement's piece-wise authoring (0.22) wrote each partial DIRECTLY to the live scaffolded plugin file — the app REGISTERS and loads it at boot, so a half-written class (measured live: an unclosed brace on Tareas.php) killed the WHOLE app, agent included, with the repair trapped inside the process that would not boot.
mode=start/appendnow write to a sibling<scaffold>.php.milpa-part(suffix after .php, invisible to any*.phpautoloader) — the live scaffold stays byte-identical and valid throughout authoring.mode=finishreads the assembled staging, runs the SAME landing gate (extracted to oneland()path), and only on GREEN publishes atomically via temp+rename (POSIX-atomic, mode-preserving), then deletes staging. On RED the live file stays the untouched scaffold and staging is kept so the caller can fix and finish again. Even mid-finish a boot loads either the valid scaffold or the complete candidate — never a broken partial.Evidence
Falsifiers red-first, and the D-11 invariant proven by mutation (write-live-directly → the three named tests go red); tests 414 → 420, 0 regressions; the 0.22 equivalence adapted to the STRONGER staging invariant (concat asserted on staging AND live-scaffold byte-identical); coverage 92.35% ≥ 90; all gates green including cs-fixer. Adversarially verified: scaffold never partial through start+4 broken appends; atomic publish via temp+rename (grep-proven zero direct live writes); red discards; split mid-4-byte-UTF8 reassembles byte-identical; 60-check independent harness green.