mirror of
https://github.com/gastownhall/beads.git
synced 2026-09-14 20:17:24 +08:00
main
1 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4e7cc88180 |
feat(journalops): the mutation journal becomes the third facade namespace (#5552)
* feat(journalops): the mutation journal becomes the third facade namespace The durable mutation journal's read side is now a role. `journalops` is a leaf package beside `issueops` and `memoryops` holding Row, Page, TruncatedError/TruncatedCode and `Journal`, whose doc comment is the specification: since-exclusive, seq-ascending, limit 0 uncapped with the wire's cap named as the handler's, a head that comes with its rows and never decreases, a caught-up checkpoint, and a typed truncation naming a resumable window. NOTHING CHANGES BEHAVIOR. The four names in `internal/storage` become ALIASES of the leaf — `type EventsJournalCursor = journalops.Journal` and its three siblings — so every implementation, every errors.As site and every caller compiles unchanged. The alias direction is what avoids the import cycle: the leaf imports context and fmt and cannot name internal/storage, so the canon goes down and the alias goes up. Outside storage.go and the conformance tier, the only non-test edits in this change are COMMENTS. The role brings the apparatus a role owes: - backend/conformance/journal_contract.go, six cases over a fixture carrying the role, the activation switch, a prune, and a mutation kit that is exhaustive against the engine's closed op vocabulary. - Wirings on all three legs (dolt, embeddeddolt, uow), so #5499's entrypoint-scoped lock passes with every leg running every case. - journalops added to the conformance census's facadePackages. That is the #5459 demand side, and the role that needed it: `Journal` is handed out by NO accessor — a front door type-asserts for it — so only a source parse can see it. issueops.Importer is the precedent. EventsJournalAccessor and EventsJournalConfigurer stay in internal/storage. Retention and activation are operator surface, and a server documented to publish the journal and never retain it must not hold a delete. Net deletion on the bespoke side: TestEventsJournalPageReportsTheHead is subsumed by the contract and is gone. TestEventsJournalRestartAndRetentionBoundary stays, because it observes ReadEventsInTx — the accessor's second read body, deliberately not on the role — and a mutation of one is invisible to the other. Mutation-measured, per case, on the embedded leg: an inclusive checkpoint, a head derived from the page, an untyped truncation, a head taken from MAX(seq) of surviving rows, and a dropped engine-only op each redden exactly the arm named for them, and a fixture whose activation hook is a no-op reddens all six. Reading the head BEFORE the rows leaves all six green — that promise is declared unpinnable in the contract header rather than faked. Refs ga-fvya1, ga-2yaqp.4. * docs(journalops): fix three comment-precision findings from review All comments, plus one test-only compile-time pin block. 1. journal_facade_test.go told a misassembled causal story: it used the CURSOR de-aliasing as its example and then attributed the breakage to errors.As. Those are two different failures. Measured, both: de-aliasing the interface builds the WHOLE TREE clean and breaks nothing behavioral — what it costs is the claim that one role has one name — while the error's dangerous shape is not the bare defined type (which loses Error() and fails to build) but the COPY-PASTE TWIN, which compiles in silence and stops crossing errors.As for everything holding the leaf's spelling. Rewritten to say that straight. 2. The seq commit-order unpinnable declaration cited "the dolt package's own journal tests" for the server-mode half. Those are single-threaded. The actual pin is internal/storage/domain/db/journal_seq_test.go — TestEventsJournal_CommitOrderedGaplessSeq drives two OVERLAPPING transactions on independent connections, and TestEventsJournal_ConcurrentWritersGaplessNoDup a racing batch. Cited correctly, and the single-threaded ones named as NOT the pin. 3. EveryMutationKindLandsARow's runs-last ordering is load-bearing but held by convention: a reorder compiles, passes, and silently gives up the only coverage that a journal keeps recording after a full prune. It was stated in the dispatch table and the three leg wirings; it is now stated at the case itself, where somebody editing a leg file is actually looking. And the compile-time half of the alias pin, which fires before any test runs: a pointer conversion per name. The pointer form is not decoration — for the three structs a plain assignment would do, but for the INTERFACE it would be blind, since interface-to-interface assignment goes by method set and a de-aliased cursor still accepts a journalops.Journal. Watched fail: the de-aliased interface, which nothing else in the tree catches. --------- Co-authored-by: Review Sim <ci@beads.test> |