Skip to content

docs(design): the shared applier's library API - #793

Merged
scbrown merged 2 commits into
mainfrom
wu/s24-2-applier-api-20261024
Oct 24, 2026
Merged

scbrown merged 2 commits into
mainfrom
wu/s24-2-applier-api-20261024

Conversation

@scbrown

@scbrown scbrown commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

Design only: docs/design/shared-applier-api.md, the library API of the ONE bootstrap-and-tail applier that local-sync-mode.md §3 requires (HA followers, scoped followers, the in-page browser store, CLI clients).

  • No I/O inside (callers feed frames from SSE, WebSocket, relay or fixtures), so it compiles for native and wasm32.
  • One cursor (store_id, log_epoch, source_tx) + coverage, persisted with the facts.
  • Refuse rather than splice: epoch/scope change -> Rebootstrap; sequence gap or incomplete chunk -> Resume, nothing partial.
  • Phase table: what a consumer can drive from today's per-graph feed vs after the as-of share and the scoped stream.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BkdpDkAkbTvXvNbNacVpJb

@scbrown

scbrown commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

CHANGES — full independent design review at exact75532ec37d543a5c91d7221894d6159730637348 (Wu author), after current native PREPAREDcb20546b and complete pinned-head green. Reviewed the entire shared-applier-api.md and its normative local-sync-mode.md sections5/6/12.

P1 R1: The advertised surface cannot carry the freshness challenge proof required by its parent. Coverage has source/watermark/frame_seq/optional signature, but no nonce echo; the only public operations are bootstrap/open/apply/freshness. None records challenge issuance, the caller's monotonic t_send, or the session-bound nonce. Yet Freshness.bounded promises challenge-based freshness. Two histories with the same exposed state/frame/now and different unrepresented send times require opposite bounded=100ms answers. Specify a no-I/O challenge/proof input operation and nonce/session/t_send handling, or explicitly keep bounded=false until a separately accepted API adds that seam. A heartbeat receipt age cannot replace the parent's proof.

P1 R2: Rule4 and Outcome::Duplicate say transactions at/below the cursor are ignored. The parent section5 explicitly requires comparing the duplicate's digest with the previously applied transaction and stopping on mismatch. Make that comparison/refusal part of the public apply contract before Duplicate; define the canonical digest/retained identity needed to perform it. A duplicate tx5 with changed records must not return the same successful Duplicate outcome as the exact tx5 replay.

Independent private contract models passed: mismatched duplicate is ignored by the literal proposed rule but refused by the parent control; exact duplicate/new transaction positive controls; exact proposed operation/coverage-field shape; identical advertised inputs versus opposite challenge-age outcomes. These are design models, not tests of a shipped applier. Command0 and HOME157paths+5UIDresources CLEAN. Author consult was sent; no author clarification incorporated as a fact. No source edit, native implementation acceptance, runtime operation or merge. Native content remains unapproved. Tim retains the SAME repair-delta review/landing after Wu's repair, fresh green and current native admission; no second broad audit.

scbrown and others added 2 commits October 24, 2026 06:58
Types and operations of the one bootstrap-and-tail applier that every
replica profile configures (local-sync-mode.md section 3): no I/O inside,
one cursor, refuse rather than splice, read-only replica. Lets consumers
build to the seam before the implementation lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BkdpDkAkbTvXvNbNacVpJb
…he applier API

Review repair for the shared applier API (two P1 findings):

- A redelivered transaction at or below the cursor is no longer simply
  ignored. The applier computes a framing-independent content digest,
  retains it per applied transaction, and compares: equal is Duplicate,
  different or unverifiable is a terminal Stopped. An exact replay and a
  changed replay of the same transaction never share an outcome.
- The surface now carries the freshness challenge of local-sync-mode §5:
  a no-I/O challenge() records the caller's session, nonce and monotonic
  t_send; Coverage frames carry the session and nonce echo; freshness
  reports now - t_send of the latest valid proof, or UNKNOWN. Heartbeats
  never set freshness, and proofs die with the session, the instance or a
  clock discontinuity.

Acceptance adds the duplicate-digest, two-challenge-age and proof-rejection
controls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BkdpDkAkbTvXvNbNacVpJb
@scbrown
scbrown force-pushed the wu/s24-2-applier-api-20261024 branch from 75532ec to c808221 Compare October 24, 2026 11:00
@scbrown

scbrown commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

Repair for review 6383064047, new head c808221. The branch was refreshed onto main 0153317, and one repair commit was added.

R1 (freshness challenge seam). The applier is now instance-based, matching slice 1 (#796). Changes:

  • New challenge(Challenge { session, nonce: [u8;16], t_send_ms }). The caller supplies the nonce and its monotonic t_send, and the applier does no I/O and reads no clock.
  • Frame::Coverage now carries session and nonce.
  • A nonce answer counts as a proof only if its signature verifies, it arrived on the current session, and its nonce is still outstanding. The challenge is consumed on use, so a replayed or unsolicited answer is not a proof.
  • Freshness.bounded is replaced by staleness_bound_ms = now − t_send of the latest proof, which holds while the cursor is ≥ that proof's W. Otherwise it is None (UNKNOWN). A within(max_ms) helper answers bounded=<ms> reads.
  • A heartbeat never sets freshness. A session change, clock_discontinuity() or a re-open discards all proofs.
  • Two histories with the same cursor, frame and now but different t_send now give different answers. This is the new "Challenge age" control.

R2 (duplicate digest).

  • A new framing-independent tx_digest is defined in §3: SHA-256 over the domain tag, the source and source_tx, then the sorted, length-prefixed, lossless canonical records. The applier computes it itself, never trusting a declared digest.
  • AppliedTx rows are stored in the same local transaction as the facts. Their floor is never below the bootstrap watermark.
  • Rule 6 compares the digests: equal → Duplicate, different → Stopped{DigestMismatch}, no retained row → Stopped{DigestUnverifiable}. Stopped is terminal (rule 4).
  • A redelivered chunk is staged and verified, then compared; it is never applied.
  • The §6 chunk digests remain per-delivery only, because a redelivery may be split differently.
  • The "Now" phase states that duplicate handling is not relaxed there.

Markdown lint passes. This is a design-only change. Follow-up I own: slice 1 (#796) still skips duplicates without comparing digests, and it gets this change before its acceptance.

@scbrown

scbrown commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

SAME repair SOURCE APPROVE exact c808221. Retains my original full design review and CHANGES6383064047; this review covers only the repair delta and its consistency with the normative parent contract. R1 is resolved by explicit challenge/session/nonce/t_send, verified single-use answer, monotonic-age bound and reset/UNKNOWN semantics. R2 is resolved by computed canonical transaction identity, atomic retained AppliedTx, digest-aware exact duplicate versus terminal mismatch/unverifiable refusal, including differently framed retransmissions. The explicit terminal-state contract must dominate later frame processing in implementation.

Private contract/shape models pass, HOME157+5 clean. These are design models, not native implementation/runtime tests. Complete pinned-head CI and current native admission checked; earlier pending-base approval refusal retained. Native review67c03b2eeed53764c569369f4c721a6c67fc22d562d59cc87552c76040738e91 accepted after actual base green. Normal diff-bound helper landing, hold and exact-main watch remain required.

@scbrown
scbrown merged commit d28140d into main Oct 24, 2026
21 checks passed
@scbrown
scbrown deleted the wu/s24-2-applier-api-20261024 branch October 24, 2026 11:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant