Personal outliner built with Rust.
4

Configure Feed

Select the types of activity you want to include in your feed.

archive add-auto-compaction: sync compaction requirements into block-graph spec, move change to archive/2026-07-15-add-auto-compaction

graham.systems (Jul 15, 2026, 2:21 PM -0700) cf43a434 231fe40f

+66
openspec/changes/add-auto-compaction/.openspec.yaml openspec/changes/archive/2026-07-15-add-auto-compaction/.openspec.yaml
openspec/changes/add-auto-compaction/design.md openspec/changes/archive/2026-07-15-add-auto-compaction/design.md
openspec/changes/add-auto-compaction/proposal.md openspec/changes/archive/2026-07-15-add-auto-compaction/proposal.md
openspec/changes/add-auto-compaction/specs/block-graph/spec.md openspec/changes/archive/2026-07-15-add-auto-compaction/specs/block-graph/spec.md
openspec/changes/add-auto-compaction/tasks.md openspec/changes/archive/2026-07-15-add-auto-compaction/tasks.md
+66
openspec/specs/block-graph/spec.md
··· 108 108 files are decoded 109 109 - **THEN** the golden-graph test fails, forcing either a fix or an explicit 110 110 format version bump with a migration 111 + 112 + ### Requirement: Update log is bounded by automatic compaction 113 + The system SHALL automatically fold the incremental update log into a fresh 114 + snapshot so that the log cannot grow without bound. Compaction SHALL trigger 115 + (a) when, after an edit has been durably persisted, the update log has reached 116 + a size threshold, and (b) at application quit when the update log exceeds a 117 + smaller quit-time threshold. Compaction MUST NOT be required for correctness: 118 + it only trades log-replay time at open for a snapshot write. 119 + 120 + #### Scenario: Size threshold crossed during a session 121 + - **WHEN** an acknowledged edit brings `updates.log` to or past the automatic 122 + compaction threshold 123 + - **THEN** the log is folded into `snapshot.loro` and emptied (or removed), and 124 + a subsequent open of the graph directory yields the identical graph state 125 + while replaying no pre-compaction updates 126 + 127 + #### Scenario: Quit-time compaction 128 + - **WHEN** the application quits normally with a non-trivial `updates.log` 129 + (at or past the quit-time threshold) 130 + - **THEN** on the next launch the graph loads from the snapshot with the 131 + pre-quit edits already folded in 132 + 133 + #### Scenario: Trivial sessions do not rewrite the snapshot 134 + - **WHEN** the application quits after a session whose `updates.log` is below 135 + the quit-time threshold 136 + - **THEN** the snapshot file is not rewritten and the small log is simply 137 + replayed on next open 138 + 139 + ### Requirement: Compaction preserves the acknowledged-edit guarantee 140 + Automatic compaction SHALL run only after the triggering edit is durable in the 141 + update log, and a compaction failure or interruption at any point MUST NOT lose 142 + an acknowledged edit, MUST NOT leave the graph directory unloadable, and MUST 143 + NOT surface as a persistence failure for the edit itself. Before the update log 144 + is removed, the newly written snapshot MUST be verified loadable, and the 145 + previous snapshot MUST be retained (as `snapshot.loro.prev`) as a manual 146 + recovery fallback. Opening a graph whose current snapshot fails to decode MUST 147 + fail with an error identifying the retained fallback rather than silently 148 + loading stale state. 149 + 150 + #### Scenario: Kill between snapshot replacement and log removal 151 + - **WHEN** the process is killed after compaction has atomically replaced 152 + `snapshot.loro` but before `updates.log` is removed 153 + - **THEN** the next open loads the graph to the identical state (re-importing 154 + updates already contained in the snapshot is harmless), and a later 155 + compaction removes the stale log 156 + 157 + #### Scenario: Compaction failure is not an edit failure 158 + - **WHEN** the snapshot write fails (e.g. disk error) after the triggering 159 + edit was appended and fsync'd 160 + - **THEN** the edit is still acknowledged as persisted, the previous snapshot 161 + and the update log remain intact and loadable, and compaction is retried at 162 + the next trigger 163 + 164 + #### Scenario: Unverifiable snapshot never replaces a good one 165 + - **WHEN** the newly exported snapshot fails verification (its bytes do not 166 + decode into a loadable document) 167 + - **THEN** compaction aborts before touching the current snapshot or the 168 + update log, both remain intact and loadable, and the failure is reported 169 + without affecting the acknowledged edit 170 + 171 + #### Scenario: Prior snapshot survives as a recovery fallback 172 + - **WHEN** a compaction completes successfully 173 + - **THEN** the pre-compaction snapshot remains on disk as 174 + `snapshot.loro.prev`, and a subsequent open that finds the current snapshot 175 + undecodable fails with an error naming that fallback instead of silently 176 + loading it