Register paths created *after* the snapshot for removal on rollback. Some artifacts get their final name only once the mutation runs — the PageIndex ``{doc_id}`` blob under ``.openkb/files`` is named by indexing, which happens after :func:`snapshot_paths`. Rather than eagerl
(self, paths: list[Path])
| 177 | self.write_journal("committed") |
| 178 | |
| 179 | def track_new(self, paths: list[Path]) -> None: |
| 180 | """Register paths created *after* the snapshot for removal on rollback. |
| 181 | |
| 182 | Some artifacts get their final name only once the mutation runs — the |
| 183 | PageIndex ``{doc_id}`` blob under ``.openkb/files`` is named by indexing, |
| 184 | which happens after :func:`snapshot_paths`. Rather than eagerly |
| 185 | snapshotting the whole append-only blob store up front (an ``os.link`` |
| 186 | per existing blob on *every* add — O(total blobs), not O(this doc)), |
| 187 | the caller invokes this once the new artifacts exist. Each is recorded |
| 188 | with no backup, so both :meth:`rollback` and a crash-recovery replay |
| 189 | delete exactly the new paths and nothing else. The active journal is |
| 190 | rewritten so a crash after the artifacts land but before commit still |
| 191 | cleans them up. Paths already tracked are ignored; missing paths are a |
| 192 | no-op (nothing was created). |
| 193 | """ |
| 194 | changed = False |
| 195 | for path in paths: |
| 196 | target = path.resolve() |
| 197 | if target not in self.entries: |
| 198 | self.entries[target] = None |
| 199 | changed = True |
| 200 | if changed: |
| 201 | self.write_journal("active") |
| 202 | |
| 203 | def rollback(self) -> None: |
| 204 | # Restore children before parents so directory deletes cannot remove |