forked from heavy-duty/rig
manifest_value, manifest_has and manifest_foreign each read the file with a bare `while read`, which stops at EOF without ever handing over a populated partial line. An unterminated final line therefore read as ABSENT — and absent is exactly the input both convergence rules key off, so the file's last line was the one least able to survive the miss. Three failures, in descending order of how much they cost: - A file truncated mid-write ends AT bootstrapped_at, so the unreadable line is the birth stamp itself. Rule 1 saw no at-stamp and regenerated the pair as now() — overwriting the one field that can never be reconstructed. A fixture born 2020-01-01 came back stamped with the current clock. - A whole file whose last line is converged_at read as empty, so Rule 2's one-time repair re-fired on EVERY run: the render stopped being a function of (existing file, running version) and the clock reached the file after all. This is the crux property of the feature, broken by a missing byte. - manifest_foreign dropped an unterminated foreign line entirely, so the writer ate a later command's provenance — the exact preservation contract the function exists to keep. The idiom is the repo's own: lib/users-config.sh:49 reads `|| [ -n "$line" ]` for the same reason. Reading the line correctly also repairs the file, since the rewritten copy is newline-terminated — asserted as "the source plus the newline it was missing, and nothing else", because a plain ends-in-newline check stays green on an implementation that drops the final line. 7 tests, each observed RED against the unfixed reader. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| manifest.sh | ||
| runner-config.sh | ||
| sshd.sh | ||
| tenant-config.sh | ||
| users-config.sh | ||