# assetaz-journal Specification ## Purpose Define the authoritative AssetAZ fact journal and independent consumer reconstruction and causal freshness contracts. ## Requirements ### Requirement: Durable authoritative ordering The journal SHALL be append-only and serialize appends, assigning contiguous sequences from 1 independently of occurredAt. Fields 2..6 SHALL be encoded once, hashed, and copied into the final record. Only successful FileChannel.force(true) SHALL commit the candidate head, publish and return its sequence. Missing append codecs and oversized records MUST fail before any append. An uncertain write SHALL permanently fail closed in that JVM without retries, repair or truncation. #### Scenario: Failed force - **WHEN** persistence may have left an uncertain tail - **THEN** the candidate is not committed or published and all later appends fail. ### Requirement: Record integrity and bounded readers Each record SHALL be exactly one top-level PDE OBJECT containing checksum, previousChecksum, sequence, stable short eventType, eventVersion and OBJECT payload in that order. Both hashes SHALL be 32 raw bytes. SHA-256 SHALL cover the original persisted fields 2..6; the first previousChecksum SHALL be SHA-256(empty). Each independent reader SHALL reuse one fixed 1024-byte buffer. The entire record including header MUST be at most 1024 bytes; reading SHALL reject declared excess or truncation immediately after framing and before payload decoding. Startup SHALL validate the entire history, checksums, chain, contiguous sequences, PDE, known codecs and exact schemas before succeeding. #### Scenario: Corrupt history - **WHEN** a checksum, chain, sequence, schema, type/version or framing cannot be verified - **THEN** startup fails with the rejection layer identified; no record is skipped. ### Requirement: Event and codec contract Events SHALL represent facts, not commands. occurredAt SHALL be semantic time; note and correlationId are nullable. Correlation groups independently meaningful workflow events without atomicity or expected count; inseparable changes belong in one event. Codecs SHALL be registry components resolved by stable eventType and persistent version, never constructed in the journal. Domain class names MUST NOT encode persistent versions. Journal integrity, PDE validity and event schema validity SHALL remain separate layers. #### Scenario: Historical codec - **WHEN** a known historical persistent version is read - **THEN** its codec yields the current domain event without changing the domain type name. ### Requirement: Owner payload AssetOwnerRegistered version 1 SHALL contain exactly seven positional values: signed long epoch seconds, nano-of-second, nullable UTF-8 note, nullable UUID correlationId, required UUID assetOwnerId, required textual assetOwnerType, required UTF-8 name. UUIDs SHALL be exactly 16 bytes, most-significant then least-significant long, both big endian. Nanos MUST be 0..999999999; owner type MUST be NATURAL_PERSON or LEGAL_PERSON, never ordinal. Required values, Instant range and exact field count SHALL be validated by the codec. No additional owner metadata SHALL be added. #### Scenario: Extra payload value - **WHEN** an otherwise valid owner payload has an eighth value - **THEN** schema validation rejects it. ### Requirement: Format changes New journals SHALL implicitly start in format 1 with no header. JournalFormatVersionChanged SHALL be an ordinary event in the same envelope and hash chain, with a positive target version and common event fields. A change applies to subsequent records; returning to an earlier supported version SHALL be representable. Unsupported formats MUST fail closed; v1 implements only format 1. #### Scenario: Chained format event - **WHEN** a format-1 change to format 1 is appended - **THEN** it is checksummed and replayed normally; unsupported target formats are rejected before append. ### Requirement: Registration and independent replay Registration SHALL atomically capture registrationHead, validate 1 <= replayFromSequence <= registrationHead + 1, defensively copy a non-null filter without null members and reject duplicate consumer identity (==). Empty filters SHALL mean all; other filters SHALL match exact runtime class only. Registration SHALL return without awaiting replay. Each subscription SHALL own a virtual-thread worker and independent reader, replay through registrationHead, close the reader and drain subsequent FIFO publications. Degraded registrations remain registered. registrationHead MUST NOT be public API. #### Scenario: Late subscriber - **WHEN** another consumer registers while an existing one processes events - **THEN** only the new consumer replays; both receive their own ordered stream without gaps or duplicates. ### Requirement: Progress and barriers processedThrough SHALL mean all records through the cursor were successfully handled or examined as irrelevant, starting at replayFromSequence - 1. A barrier SHALL return whenever the safe cursor meets its minimum, including DEGRADED; otherwise wait while CATCHING_UP/ONLINE or throw DegradedException when DEGRADED. Progress and degradation SHALL wake waiters. The barrier MUST NOT imply a historical snapshot. #### Scenario: Filtered progress - **WHEN** a consumer last handled 1000 and examined irrelevant records through 1100 - **THEN** its cursor is 1100 and a barrier for 1100 succeeds. ### Requirement: States and atomic cutover Subscriptions SHALL begin CATCHING_UP, without requiring an initial state callback. After replay and pending events through an atomic cutover, the worker SHALL request ONLINE. Successful return establishes readiness to act; the callback should establish readiness without irreversible actions. Publication concurrent with cutover SHALL be processed before ONLINE or as the first normal event after ONLINE, with no gap. All callbacks for one consumer SHALL be sequential. #### Scenario: Append during readiness callback - **WHEN** a new event arrives at the chosen cutover - **THEN** it is queued and processed next after readiness succeeds. ### Requirement: Isolated degradation and atomic models Callback or ONLINE transition failure SHALL retain the last safe cursor, mark only that subscription DEGRADED, stop delivery, wake barriers, and log the exception plus consumer identity, sequence and state. Consumers SHALL atomically apply each event or leave their externally visible model unchanged. Replay SHALL rebuild state without repeating trades, transfers or notifications. No OFFLINE, unregister or recovery SHALL be implemented. #### Scenario: Consumer throws - **WHEN** one subscriber fails applying sequence N - **THEN** it retains its safe cursor, future barriers beyond that cursor fail, and other subscribers continue.