refactor(coding-agent): move the semantic-edge ledger onto the event-log substrate - #2028
Open
snimu wants to merge 3 commits into
Open
refactor(coding-agent): move the semantic-edge ledger onto the event-log substrate#2028snimu wants to merge 3 commits into
snimu wants to merge 3 commits into
Conversation
…log substrate The recorder's private append/replay/repair IO is deleted; EventLog owns it, the same move #1987 made for the RLM spawn ledger. One durability rule is unified in the substrate rather than dropped: an unterminated final line is an uncommitted append, skipped on read and truncated before the next append — never newline-completed and never surfaced to a consumer whose next append destroys it.
…atomic readSemanticEdgeLedger probed with statSync before reading through EventLog, which swallows ENOENT; a ledger deleted between the two returned [] instead of throwing. The missing-file decision now lives at the single open (replaySync missingFileThrows), so no check-then-read window exists.
The unterminated-tail contract was restated four times (module doc, replaySync doc, two test comments). It now lives once in the module doc; the method doc keeps only its own parse/missing-file semantics and the test comments reference the contract.
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.
Purpose
The ACP semantic-edge ledger (#1885) and the RLM spawn ledger implement the same append-only JSONL crash-safety independently. #1987 extracted that mechanics into
event-log.tsand moved the spawn ledger onto it; this PR completes the dedup by moving the semantic-edge ledger's private append/replay/repair IO onto the same substrate.EventLogis now the single owner of torn-tail handling, repair-on-append, and line-level replay for both ledgers.What changed
SemanticEdgeRecorderkeeps its semantics (degrade-to-disabled on the first ledger failure, write-before-action ordering, replay-on-resume, retry-identity parking) and deletes its IO:_loadExisting,parseLedgerContent,_pendingRepair(truncate/terminate memo), and the mkdir/appendFileSync plumbing are gone; construction replays throughEventLogand appends go throughEventLog.appendSyncreadSemanticEdgeLedgerkeeps its contract: reads never mutate or create the file, a missing ledger throws (statSyncprobe), interior corruption stays loud with the samecorrupt semantic-edge ledger line NmessageOne durability rule unified (union, not intersection)
The two IO layers disagreed on a parseable-but-unterminated final line: the edges ledger newline-completed it (keeping the record), the substrate truncates it (#1987's review verdict: completion turns a line a strict parser rejects into permanent fail-closed interior poison). Worse, the substrate's replay still surfaced such a line while its next append destroyed it — reading data the log then disowns. This PR unifies on one coherent rule in the substrate, for both ledgers: an unterminated final line is an uncommitted append — skipped on read (with the torn-final-line log) and truncated before the next append. This state is unreachable from either ledger's own single-write appends (record and newline are one write); it matters only for crash/interference windows, where "uncommitted" is the only safe reading.
Test surface
rlm-ledger.test.ts: passes unchangedsemantic-edges.test.ts: one test edited — the pin of the old newline-completion behavior now pins the unified truncation semantics (skipped on read, truncated on append); flagged for exactly this migration in the refactor(coding-agent): extract the append-only event-log substrate from the RLM spawn ledger #1987 review. Everything else (torn-tail tolerance, repair-once, read-never-mutates, read-never-creates, corruption messages, disable-on-failure) passes unchangedevent-log.test.ts: the JSON-parseable-tail pin gains the read-side assertion; both behavior pins verified fail-unfixed against the pre-change substrateValidation
event-log,rlm-ledger(unchanged),semantic-edges(52),agent-session-semantic-edges(23, incl. the fix(coding-agent): settle late compaction slices and forward daemon subagent lineage #2021 settle-race pin),agent-traces(45, incl. the feat(coding-agent): deliver the semantic-edge ledger through the agent-traces outbox #1984 consent-gated intent and outbox pins),suite/agent-session-compaction— 187 tests, 0 failuresnpm run check(biome, tsgo, installer render, browser smoke) passes via the pre-commit hookLOC
Src: semantic-edges.ts +16/−61, event-log.ts +17/−21 (net src −49). Tests +11/−4, changelog +1. Classification: deletion — duplicated IO mechanics removed; the only mechanism change is the read-side half of the unified unterminated-tail rule, which removes an inconsistency rather than adding a branch.
Linear: RES-1260 https://linear.app/primeintellect/issue/RES-1260
Note
Medium Risk
Changes crash-interference ledger replay for semantic edges (dropping unterminated final records) and centralizes durability in
EventLog, which affects session resume and downstream edge derivation in those edge cases.Overview
Moves ACP semantic-edge ledger I/O onto the shared
EventLogsubstrate, deletingSemanticEdgeRecorder's bespoke load/append/repair path (_loadExisting,parseLedgerContent,_pendingRepair, directappendFileSync). Construction and writes now go throughEventLog.replaySync/appendSync;readSemanticEdgeLedgerstill fails loud on a missing file viamissingFileThrows.Unifies torn-tail behavior with the RLM spawn ledger: an unterminated final line is treated as an uncommitted append—omitted during replay (even if JSON-parseable) and truncated before the next append, instead of the semantic-edge path newline-completing a valid partial record.
EventLog.replaySyncimplements that skip up front rather than only whenparsethrows.Tests pin the new read/append semantics for parseable unterminated tails in both
event-logandsemantic-edges.Reviewed by Cursor Bugbot for commit 9e6f959. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Move semantic-edge ledger onto
EventLogsubstrate and unify unterminated-tail handlingSemanticEdgeRecorderwith calls to the sharedEventLogfor both replay (replaySync) and writes (appendSync).EventLog.replaySyncnow skips any non-empty unterminated final line (including valid JSON) instead of parsing it, and gains an optionalmissingFileThrowsflag to control ENOENT behavior.SemanticEdgeRecorder._loadExistingand the manualreadFileSync/appendFileSyncpath;parseSemanticEdgeLinebecomes a per-line parser supplied as the replay callback.EventLog.replaySyncno longer swallows parser exceptions for a skipped unterminated tail, andreadSemanticEdgeLedgernow silently skips an unterminated final line rather than completing it — callers relying on newline-completion of partial tails will see those records dropped.Macroscope summarized 9e6f959.