feat(store): draft states and discards are journaled, posted_seq is kept (P4)
Accounting reason: a draft that waits on its receipt, and one thrown
away, are facts about the books' working papers that must survive a
restart and replay identically, or a discarded draft would come back and
hold its period open. entry.draft_state_set and entry.draft_discarded are
drafts_v2's (an older version stops at the declaration instead of
misreading them); no figure moves. Schema 19 (taken at merge as the next
after the current last): entries.posted_seq, and a draft_states table
(drafts are not rows of entries, so the plan's entries.draft_state column
has nowhere to live). posted_seq is the journal line that posted each
entry (entry.posted, entry.reversed, the line that closed a period or
posted a schedule month), identical live and on replay.
8.0.6: a hand-written line of each new shape in
books_written_under_older_rules_still_open; fixture old-books/p4_before
written by 25b52580 (hand entries posted late, a reversal, a document, a
schedule month, a recurring draft, open drafts, a close) opens to the
same figures (serialized entries omit a building state, so no golden
moved); drafts_v2_is_its_own_feature; the fuzzer and GUI journal know the
types.
Tests: draft_states_replay_and_project, drafts_v2_waits_for_the_upgrade,
posted_seq_is_the_posting_lines_seq, an_older_version_stops_at_the_draft_events.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
1 parent
be8bcf340e
commit
28d46332e4
18 files changed
+1455
-24
No files matched your search
@@ -64,6 +64,7 @@ pub const KNOWN_FEATURES: &[&str] = &[
|
||||
DOCUMENT_STANDING,
|
||||
DOCUMENTS_V2,
|
||||
ATTACHMENT_TEXT,
|
||||
DRAFTS_V2,
|
||||
];
|
||||
|
||||
/// Feature: `period.closed` closes to members with `"rounding":
|
||||
@@ -208,6 +209,15 @@ pub const DOCUMENTS_V2: &str = "documents_v2";
|
||||
/// declaration instead (rules.md B-27, B-44).
|
||||
pub const ATTACHMENT_TEXT: &str = "attachment_text";
|
||||
|
||||
/// Feature: `drafts_v2` (plan P4, D-52): `entry.draft_state_set` (a draft
|
||||
/// building or awaiting its evidence) and `entry.draft_discarded` (a
|
||||
/// draft thrown away, with why). An older version doesn't know those
|
||||
/// event types: it would refuse them as unknown, or, replaying around
|
||||
/// them, keep a discarded draft that blocks a period's close and call a
|
||||
/// waiting draft one being built; it stops at the declaration instead
|
||||
/// (rules.md B-27, B-43).
|
||||
pub const DRAFTS_V2: &str = "drafts_v2";
|
||||
|
||||
/// The line type that declares the journal format.
|
||||
pub const FORMAT_SET: &str = "journal.format_set";
|
||||
|
||||
@@ -243,6 +253,8 @@ pub const EVENT_TYPES: &[&str] = &[
|
||||
"document.filed",
|
||||
"entry.approved",
|
||||
"entry.created",
|
||||
"entry.draft_discarded",
|
||||
"entry.draft_state_set",
|
||||
"entry.instrument_set",
|
||||
"entry.line_added",
|
||||
"entry.line_class_set",
|
||||
@@ -942,6 +954,41 @@ mod tests {
|
||||
assert_eq!(up.adds(), vec!["the journal feature documents_v2"]);
|
||||
}
|
||||
|
||||
/// The version before P4 (every feature through `attachment_text`)
|
||||
/// stops at a declaration of `drafts_v2` as newer, never keeping a
|
||||
/// discarded draft or calling a waiting one a draft being built; and
|
||||
/// books that declared every earlier feature still wait for it
|
||||
/// (B-27, D-39).
|
||||
#[test]
|
||||
fn drafts_v2_is_its_own_feature() {
|
||||
assert!(KNOWN_FEATURES.contains(&DRAFTS_V2));
|
||||
let before: Vec<&str> = KNOWN_FEATURES
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|f| *f != DRAFTS_V2)
|
||||
.collect();
|
||||
let l = line(
|
||||
FORMAT_SET,
|
||||
json!({"format": 2, "features": [ATTACHMENT_TEXT, DRAFTS_V2]}),
|
||||
);
|
||||
match declared_knowing(&l, &before) {
|
||||
Err(StoreError::NewerJournal { need, .. }) => {
|
||||
assert_eq!(need, "journal features drafts_v2")
|
||||
}
|
||||
other => panic!("expected NewerJournal, got {other:?}"),
|
||||
}
|
||||
let older = JournalFormat {
|
||||
format: 2,
|
||||
features: before.iter().map(|f| f.to_string()).collect(),
|
||||
};
|
||||
assert!(matches!(
|
||||
older.require_feature(DRAFTS_V2),
|
||||
Err(StoreError::FormatUpgradePending { .. })
|
||||
));
|
||||
let up = older.upgrade().expect("waits for it");
|
||||
assert_eq!(up.adds(), vec!["the journal feature drafts_v2"]);
|
||||
}
|
||||
|
||||
/// This version's format: the newest it reads, every feature it
|
||||
/// knows, each once, sorted (what new books and an upgrade declare).
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user