Skip to content

Resume-After-Complete rewind persists the pre-append bodyHash, so it detects its own write (isa-utils.ts) #1503

Description

@MatiasBarboza

Summary

In LifeOS/install/hooks/lib/isa-utils.ts, syncToWorkJson() computes incomingBodyHash from the body it read. When the Resume-After-Complete rewind fires, it appends a D-auto-* Decisions row to the body and writes that to disk — but it still persists the pre-append hash to work.json.

The stored bodyHash therefore describes a body that no longer exists on disk. A later sync reads that mismatch as a fresh bodyChanged — a change the rewind itself caused.

Invariant violated

After any sync, the persisted bodyHash must describe the body actually on disk.

Where

// ~726 — hash of the body as read
const incomingBodyHash = content ? hashBody(content) : (existing.bodyHash || "");

// ~750/751 — the rewind mutates the body and writes the mutated version
updated = appendDecisionRow(updated, timestamp, newIteration);
writeFileSync(isaPath, updated);

// ~931 — but this persists the PRE-append hash
...(incomingBodyHash ? { bodyHash: incomingBodyHash, lastBodySize: content ? content.length : 0 } : {}),

Fix (2 lines)

// 726: const -> let
let incomingBodyHash = content ? hashBody(content) : (existing.bodyHash || "");

// right after writeFileSync(isaPath, updated):
incomingBodyHash = hashBody(updated);

Test

Regression test asserting the invariant (there were no tests under hooks/lib yet):

RED   (fix disabled): (fail) REGRESSION: after a rewind, the persisted bodyHash describes the body on disk
                      Expected: <sha256 of the body on disk>
                      Received: <sha256 of the pre-append body>
GREEN (fix applied):  3 pass, 0 fail

Scope, honestly

The broken invariant is proven by the test. What I could not reproduce is a full causal chain from it to a user-visible symptom: one of my ISAs bounced from complete back to learn on every close attempt, bumping iteration each time. A test replicating that exact sequence passes with and without the fix, so that symptom likely has an additional factor I have not isolated (multiple sessions / a restart in between are candidates).

What the evidence does support: the invariant is violated on every rewind, deterministically. In a single install I count 15 rewind events across 9 different slugs, so the path is well travelled — the damage is just usually invisible, because most ISAs re-sync their hash on the next body edit and close fine.

Happy to open a PR with the fix + test if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions