Commit Graph
124 Commits
Author SHA1 Message Date
pingqiuandClaude Opus 4.7 38cb25702f T4 kick-off: L1 V2 replication entity survey (pre-sketch review)
First artifact for T4 (Gate G5 Replicated Write Path) under the §8C.8
top-down port discipline added post-T3 retrospective. This is L1 only
— raw V2 entity enumeration with scope / lifecycle / concurrency /
cross-session / authority / protocol / invariants attributes. No V3
bridge verdicts proposed yet; L2 follows only after L1 review closes.

9 entities identified across replication surface:
- WALShipper (per-replica fan-out)
- ShipperGroup (per-volume aggregator)
- ReplicaReceiver (per-volume replica-side listener)
- ReplicaBarrier FSM (per-barrier ephemeral)
- DistGroupCommit closure (per-write-op, mode-aware)
- RebuildSession (volatile, non-crash-durable)
- RebuildServer (per-primary listener)
- RebuildTransportServer / Client (per-session base lane)

9 L1-level observations flagged as L2 hazards: epoch fencing
pervasiveness, contiguous-LSN cross-cutting invariant, two-lane
rebuild bitmap integration, mode-dependent durability, volatility
of rebuild session (vs BUG-005 Provider cache lesson), explicit
reconnect protocol, three-phase barrier, ioMu.RLock nesting,
shipper-group double watermark.

5 open questions raised for sw / architect / PM review before L1
sign: scope completeness (sync_all_reconnect, split-brain arbiter?),
scope accuracy (ReplicaReceiver per-volume vs per-assignment),
RebuildSession volatility confirmation, DistGroupCommit V3 residence
opinion, protocol-frame wire-compat stance.

Status: DRAFT — open for sw review; L2 + L3 work blocked on L1 sign
per §8C.8 discipline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:30:57 -07:00
pingqiuandClaude Opus 4.7 88dcd49d67 sw-block/design: T3 mini-plan + audit + sketch docs (pre-close docs)
Predecessor docs for the T3 batch, retained here for audit trail.
The closure report (`v3-phase-15-t3-closure-report.md`), contract
bridge catalogue, and BUG-005/006 artifacts already landed in
commits `4127e5136` + `6e196885e`; this commit fills the docs
those closure artifacts reference back to.

Landed:
  v3-phase-15-t3-port-plan-sketch.md    T3 umbrella sketch (rev-2.1, three-signed)
  v3-phase-15-t3-port-audit.md          T3.0 port audit + Addendum A (QA-signed)
  v3-phase-15-t3a-mini-plan.md          T3a scope + sign-off (CLOSED 0e1595c)
  v3-phase-15-t3b-mini-plan.md          T3b scope + sign-off (CLOSED 72d0d40)
  v3-phase-15-t3c-mini-plan.md          T3c scope + sign-off (CLOSED 829c6a9)

Total 1,346 lines of doc; no code impact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:19:03 -07:00
pingqiuandClaude Opus 4.7 6e196885e4 T3 closure: reconcile §8C.3 trigger narrative + C5 pin strength
Two document-truthfulness mismatches flagged by architect review:

§B Governance transition (closure report): previously claimed "no
§8C.3 triggers fired during T3"; §H Phase 3's own BUG-005 description
matches trigger #1 (unknown-unknown architectural bug, V2/V3 shape-
level mismatch). Corrected to say trigger #1 fired once (BUG-005)
and was handled per §8C.3, with log entry, architect+PM notification,
catalogue §2.3 drift-event row, and porting-discipline citation.

C5-NVME-SESSION-STATE-CLEANUP-ON-CLOSE (contract bridge catalogue
§2.2.14): previously stated "PASSES today" / "pinned explicitly".
Closure §H Phase 4 correctly narrows landed tests to "smoke +
goroutine-leak guard" with Target.ctrls/AER/KATO-stored-ms
introspection not exercised. Catalogue row now matches that
strength: "pin strength today: smoke + goroutine-leak guard only;
full state-release introspection NOT exercised; queued as
follow-up".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:14:08 -07:00
pingqiuandClaude Opus 4.7 4127e5136c T3 closure: finalize sign-ready state + BUG-006/007 + catalogue retrofill
Closure report (v3-phase-15-t3-closure-report.md):
- §E rewritten as FINAL A–F m01 verification table with per-impl
  status + G4 pass criterion = smartwal (production default) full
  A–F green; walstore non-default fallback with Matrix D failure
  tracked via BUG-007
- §E sign table: QA re-sign 2026-04-22 with evidence basis
  (seaweed_block@313dd52 + BUG-005 fix 42b045a); prior RETRACTED
  row superseded
- §D INV-DURABLE-001: conditional "Path B pending" wording
  removed; scoped to smartwal; canonical row name stands
- §B non-claims: stale _TBD_ perf wording replaced with
  first-light scope statement; new non-claim "G4 pass =
  smartwal only; walstore deferred via BUG-007" added
- §G.3 finalized: FINAL resolution with smartwal A–F PASS;
  walstore deferred
- §H Phase 2 narrative updated to match final matrix outcome
  (Matrix E smartwal-only; walstore E skipped pending BUG-007)
- §H Phase 4: T3-DEF-6 test wording downgraded from
  "pins cleanup contract" to "smoke + goroutine-leak guard"
  per PM feedback (no test-only introspection of Target.ctrls/
  AER/KATO internals; follow-up deferred)
- §H Phase 5: BUG-007 filed and scoped; non-blocking basis
  spelled out

Contract Bridge Catalogue (v2-v3-contract-bridge-catalogue.md):
- §2.2.14 C1-NVME-SESSION-KATO reclassified PRESERVE-partial
  → VIOLATED with BUG-006 anchor + m01 Matrix D evidence
- §2.2.14 C5-NVME-SESSION-STATE-CLEANUP-ON-CLOSE added
  (T3-DEF-6 retrofit, pinned by QA L1 addendum)
- §2.3 drift-event audit table expanded with BUG-006, BUG-007,
  T3-DEF-5, T3-DEF-6

BUG-006 (006_nvme_kato_timer_not_enforced.md):
- Unified contract ID to catalogue name
  C1-NVME-SESSION-KATO-STORED-NOT-ENFORCED (was drifting as
  C3-NVME-KATO-ENFORCEMENT, PM Low catch)
- §7 reframed as "existing row reclassified VIOLATED"
  rather than "add new row"

BUG-007 (007_walstore_umount_remount_data_loss.md): filed as
pre-existing walstore-specific durability bug surfaced by
Matrix D re-verify; explicitly non-blocking for T3 since
smartwal is production default.

BUG-005 (005_backend_close_cross_session.md): committed for
HEAD-reproducibility (referenced by closure §H Phase 3).

Inventory (bugs/inventory/nvme-test-coverage-deferred.md):
T3-DEF-5/6/7 struck through with per-row resolution pointers;
zero open T3-scope inventory rows remaining.

Evidence artifacts committed in seaweed_block@313dd52
(scripts/iterate-m01-nvme.sh Matrix F robustness +
t3_qa_session_cleanup_addendum_test.go).

Awaiting architect + PM three-sign.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 22:08:30 -07:00
pingqiuandClaude Opus 4.7 953fdb7564 doc: P14 S8 final bounded close — evidence matrix + P15 handoff
Adds the six S8 closure deliverables consolidating S4-S7 evidence,
classifying V2 scenarios, and mapping residual product gaps onto
canonical P15 tracks (per v3-phase-15-product-plan.md §4).

New docs:
- v3-phase-14-s8-assignment.md — S8 execution contract.
- v3-phase-14-s8-final-bounded-close.md — bounded P14 target,
  accepted topology, reject conditions.
- v3-phase-14-s8-evidence-matrix.md — 16 claims × {L0, L1, L2, L3,
  Status, Residual}. 15 PROVEN, 1 PARTIAL (Claim 15 fence
  quantitative bound, P14 internal follow-up). Rounds 2-3 architect
  corrections: Claim 10 / 12 L2 narrowed; Claim 6 refresh gap closed
  by the new L1 test (see companion commit in seaweed_block).
- v3-phase-14-s8-v2-scenario-classification.md — every V2 scenario
  mapped to RUNNABLE-P14 / BLOCKED-FRONTEND / BLOCKED-OPS /
  BLOCKED-HA / BLOCKED-PERF / PORT-MECHANISM; scenario YAMLs kept
  as L3 shape, not executed evidence.
- v3-phase-14-s8-p15-handoff.md — 11 rows (10 canonical P15 tracks
  + 1 P14 internal follow-up anchored to Claim 15 PARTIAL); §4
  integrity check split by row class.
- v3-phase-14-s8-closure.md — final P14 closure statement matching
  the close doc §10 wording; explicit non-goals; all 9 P15 tracks
  named with canonical numbering.

No claim of CSI / frontend / migration / security / performance /
production readiness. Every product gap is handed off with a
concrete first-proof gate.

Companion: seaweed_block commit adds the IntentRefreshEndpoint L1
route test that closes Claim 6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 01:44:11 -07:00
pingqiuandClaude Opus 4.6 247d9f6fa6 doc: V3 observability — structured logging, tracing, metrics, debug zip, alerts
Covers 6 areas based on CockroachDB/Ceph/etcd/Longhorn research:

1. Structured logging: zap + JSON + channel model (OPS/STORAGE/REPL/ISCSI/AUDIT/HEALTH)
2. Distributed tracing: OpenTelemetry spans across write/rebuild/failover paths
3. Metrics: 40+ must-have Prometheus metrics with histogram latency buckets
4. Debug tools: debug zip (logs+pprof+state), log merge, live tail
5. Audit logging: every admin mutation with actor/target/operation/result
6. Alert design: 3 tiers (page/ticket/log), anti-patterns to avoid

Identifies existing gaps: no I/O latency histogram, no rebuild duration
metric, no audit trail, no structured logging, no distributed tracing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 02:32:30 -07:00
pingqiuandClaude Opus 4.6 9437bd0b95 doc: V3 development process — branch strategy, CI/CD, review, release
Covers full engineering process based on SeaweedFS upstream audit:
- Branch strategy: feature/sw-block with checkpoint branches for perf baselines
- Commit conventions: type: description format
- Code review checklist with anti-pattern checks
- Testing standards: 5 levels, 1600+ tests, 4 hardware acceptance scenarios
- CI/CD pipeline: unit→component→hardware gates
- Release process: checklist, artifacts, versioning
- Issue/PR templates with anti-pattern classification
- Agent collaboration model (architect/sw/tester/manager roles)
- Code quality: golangci-lint config, race detection
- Upstream contribution path for SeaweedFS merger

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 01:58:19 -07:00
pingqiuandClaude Opus 4.6 f11a5829d7 doc: update operations design — add existing V1 UI foundation, code map
Added section 8: existing UI/admin infrastructure from V1:
- iSCSI admin HTTP server (admin.go: /status, /assign, /rebuild, /snapshot)
- Grafana dashboard JSON (block-overview.json, already built)
- Master UI HTML (master.html, add Block Volumes tab)
- Volume server UI HTML (volume.html, add Block section)
- Prometheus metrics (already integrated)

Added section 10: existing vs new code map showing most backend
exists — work is wiring to user-facing interfaces.

Updated Phase 1 to include Master UI tab (+200 lines HTML/JS).
Updated Phase 5 with two options (lightweight extend vs full SPA).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 01:44:47 -07:00
pingqiuandClaude Opus 4.6 3ba622d9e0 doc: V3 operations design — user-friendly setup, shell commands, REST API
Covers three personas (developer/operator/platform engineer) with:
- One-command setup: weed server -block (10 seconds to first volume)
- Shell commands: block.list, block.status, block.health, block.create, etc.
- REST API: /block/volumes CRUD, /block/health
- Observability: Prometheus metrics, alerting rules, Grafana dashboard
- Actionable error messages (every error tells you what to do next)
- Dry-run by default for all destructive operations

Competitive comparison: 10s setup vs Ceph 30min, 13.5x write IOPS,
single binary for object + block storage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 00:36:32 -07:00
pingqiuandClaude Opus 4.6 2bc8dfcdde doc: update testrunner roadmap — add runs.db text index for result tracking
P1 feature updated: replace generic "structured results" with concrete
runs.db design (newline-delimited JSON, one line per run). Leverages
existing RunBundle system (manifest.json, result.json already exist).

New CLI commands: list, trend, gc, reindex, diff.
Regression detection via stddev comparison against rolling baseline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:00:35 -07:00
pingqiuandClaude Opus 4.6 676539d3b9 doc: testrunner roadmap + dm-stripe scenario (42/42 PASS, 1.87x write IOPS)
testrunner-roadmap.md: P0-P3 feature plan for multi-version comparison,
Ceph adapter, result tracking, cluster templates, debug mode.

dm-stripe-two-server.yaml: proven Linux dm-stripe across 2 sw-block
volumes on 2 servers. Results: single=42K IOPS → striped=79K IOPS (1.87x).
Data integrity verified via md5. Zero sw-block code changes needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 21:57:03 -07:00
pingqiuandClaude Opus 4.6 25ede892b4 doc: external failure taxonomy — 20 real bugs from Ceph/DRBD/Mayastor/Longhorn
Catalogs production failures organized by semantic class:
- Membership/liveness misjudgment (4 cases)
- Recovery decision error (3 cases)
- Completion/durability illusion (4 cases)
- Ordering/race conditions (4 cases)
- Background work corrupts semantics (3 cases)

Each entry maps to V2 exposure and V3 prevention rules.
Includes "Would V2 Have This Bug?" self-audit checklist.

Sources: Ceph tracker, DRBD changelogs, Longhorn/Mayastor GitHub issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 01:21:08 -07:00
pingqiuandClaude Opus 4.6 8ecc506452 V2 stabilization: 144/144 hardware actions PASS + design docs + SmartWAL prototype
Hardware scenarios (all PASS on m01/m02, 25Gbps RoCE):
- I-V3 auto-failover: 43/43 (create→write→kill→promote→verify IO)
- I-R8 rebuild-rejoin: 58/58 (failover→write→restart→1GB rebuild in 2s→verify data)
- Fast rejoin: 43/43 (kill replica→3s→restart→recovery→data verified)

Performance: V2 RF=1 = 46,666 IOPS vs V1.5 RF=1 = 47,233 IOPS (-1.2%, noise)

New test scenarios:
- v2-rebuild-rejoin.yaml: full failover→rebuild→second failover→data integrity
- v2-fast-rejoin-catchup.yaml: replica kill→fast restart→recovery
- v2-rebuild-failure-retry.yaml: kill during rebuild→restart→data verified
- rf1-perf-compare.yaml: RF=1 perf baseline for V1.5 vs V2 comparison

Design documents:
- protocol-anti-patterns.md: 7 anti-patterns with cases from SeaweedFS/Ceph/DRBD
- smartwal-design-memo.md: extent-first write algorithm research (BlueStore/ZFS/DRBD)
- smartwal-prototype-spec.md: prototype spec with 16/16 crash tests PASS
- v3-clean-recovery-draft.md: V3 semantic cleanup principles
- v2-integration-matrix.md: 25-row integration coverage map
- v2-acceptance-evidence.md: gap analysis for remaining work

SmartWAL prototype (16/16 tests PASS):
- smartwal.go, smartwal_record.go, smartwal_recovery.go: core implementation
- smartwal_test.go: 9 single-node crash tests
- smartwal_repl_test.go: 7 two-node replication crash tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 00:18:20 -07:00
pingqiuandClaude Opus 4.6 943000ae8e fix: RebuildSourceDecision returns FullBase when CommittedLSN=0
When CommittedLSN=0 (sync_all mode, replica degraded), snapshot-tail
rebuild was chosen because IsRecoverable(checkpoint, 0) is vacuously
true (0 <= HeadLSN always). But snapshot-tail requires a valid committed
endpoint for tail-replay. Without it, ExecuteRebuildPlan calls
TransferSnapshot which RemoteRebuildIO doesn't support → immediate fail.

Fix: if CommittedLSN=0, force RebuildFullBase. This is the correct
source when the primary has data but no replica has confirmed durability.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:46:00 -07:00
pingqiuandClaude Opus 4.6 a79cba0be7 fix: PlanRebuild targetLSN=0 when replica is degraded (CommittedLSN fallback)
Root cause: StatusSnapshot().CommittedLSN reports 0 in sync_all mode when
the replica shipper has no flushed progress (NeedsRebuild state). This is
correct for lineage-safe committed boundary, but PlanRebuild uses
CommittedLSN as RebuildTargetLSN. With target=0, shouldStartSessionCommand
rejects the StartRebuildCommand, and the rebuild IO never executes.

Fix: PlanRebuild falls back to HeadLSN when CommittedLSN is 0. The
primary's WAL head IS the data boundary the replica needs to reach.
The fact that no replica has confirmed durability is exactly why we're
rebuilding.

Also adds command type logging to coreApplyAndLog so tester can verify
which commands are actually emitted vs silently dropped.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:35:31 -07:00
pingqiuandClaude Opus 4.6 e0116fc631 fix: three hardware blockers — WAL retention + registry race + shutdown beat
All 43 actions pass on m01/m02 hardware. Auto-failover PASS.
dd_write: 30s → 123ms. Post-failover write: 33,621 IOPS.

1. WAL retention: remove keepup retention floor (MinShippedLSN).
   WAL cannot be pinned during sustained async writes — any pin
   strategy either fills WAL (blocking writes) or over-recycles
   (breaking catch-up). Flusher recycles freely. Future LBA map
   will provide catch-up without WAL retention.
   MinShippedLSN on ShipperGroup retained as diagnostic surface.

2. Registry stale-cleanup race: add RegisteredAt grace period.
   Race: master registers volume → next VS heartbeat arrives before
   VS discovers the volume → stale cleanup deletes the entry →
   failover finds 0 entries. Fix: skip stale cleanup for entries
   registered within 30s (> 2 heartbeat intervals).
   2 new tests: grace protects new entry, old entry still cleaned.

3. Shutdown heartbeat: VS disconnect heartbeat no longer claims
   block inventory authority. Previously, the shutdown beat's
   empty inventory triggered stale cleanup, deleting the entry
   before failover could use it.

Scenario fix: recovery-baseline-failover.yaml now kills the
correct node (discovered primary, not hardcoded), connects to
the correct new primary for post-failover verification.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 22:59:46 -07:00
pingqiuandClaude Opus 4.6 39f1232fe2 feat: validation matrix closure — Rebuild Ready 12/12, Restore Ready 10/10
Close all Rebuild Ready and Restore Ready matrix gaps. V2 Ready at 10/14
(2 partial, 2 missing — honest assessment).

New tests (tester-written):
- R1: syncAck-driven trigger via protocol engine decision
- R3: stale replica restart beyond WAL → rebuild converges
- R5: connection drop mid-base → cancel → fresh rebuild converges
- R10: failover-rejoin with forced WAL recycling, strict rebuild assert
- R11: divergent replica full overwrite convergence
- R12: crash mid-rebuild → fresh session converges (not resume)
- S2: corrupt WAL entry + corrupt base block both rejected
- S5: snapshot-tail rebuild (base + WAL tail replay)
- S7: crash between base install and tail replay
- S8: snapshot under concurrent writes
- V5: rebuild complete without DurableLSN blocks publish_healthy
- V9: mixed replica health aggregate projection
- V14: negative fail-closed matrix (epoch, kind, stale)

Bug fix: StartRebuildSession now clears stale dirty map + resets WAL +
updates checkpoint AFTER safety check but BEFORE session.Start(). Fixes
stale extent data shadowing rebuild base blocks on reopened replicas.

Cleanup: remove 14 obsolete design docs (migration batches, old WAL-v2
specs, simulator goals) — all superseded by current protocol docs.

34 component tests + 8 protocol engine tests + server tests all pass.
1GB CRC validation passes in 19s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:31:55 -07:00
pingqiuandClaude Opus 4.6 59a36013d4 feat: rebuild hardening A1-A5 + session-controlled execution path
A1 Engine kind-routing fix:
  SessionProgressObserved/Completed/Failed now respect active session
  Kind. Rebuild progress no longer leaks into catch-up aggregate.
  sessionKindMismatch guard + observeRebuildProgress helper.
  2 regression tests lock kind isolation.

A2 Retention pin:
  Rebuild session ack drives progress-based WAL retention floor.
  Pin installed at base_lsn on accepted, advances with wal_applied_lsn,
  released on completed/failed/cancelled. rebuildProgressPinFloor
  returns min across all active replicas.
  Retention pin test: 100 blocks fill WAL, 5 flusher cycles with
  20 pinned rebuild entries — all verified correct.

A3 Progress ack emission:
  Automatic sessionAck(running/base_complete/completed/failed) emitted
  from rebuild session lifecycle transitions. sessionAckLocked builds
  ack under session lock. emitRebuildSessionAck callback wired through
  SetOnRebuildSessionAck on BlockVol.
  ObserveReplicaRebuildSessionAck maps acks to core engine events.
  WireLocalReplicaRebuildSessionAcks bridges local callback to server.
  5 server tests proving ack→core, pin advance, pin cleanup.

A4 Deadline/timeout:
  rebuildAckWatch watchdog: armed on accepted/running/base_complete,
  refreshed on each ack, cleared on completed/failed. Timeout
  cancels local session + clears pin + fail-closes.
  2 tests: timeout→fail-close, progress→refresh.

A5 Session-controlled execution path:
  v2bridge.Executor.TransferFullBase now uses session-controlled loop:
  beginControlledFullBase → real sessionControl over TCP →
  transferExtentToSession via RebuildTransportClient →
  PrepareFullBaseRebuild → TryCompleteRebuildSession.
  ReplicaReceiver control channel handles MsgSessionControl alongside
  MsgBarrierReq. Session acks written back on same TCP connection.
  RebuildSessionBase request type separates new per-block stream from
  legacy raw extent stream. Full-base cleanup deferred until success.
  Deadlock fix: ApplyBaseBlock releases session lock before ioMu.
  Hydration skip for full-base sessions.

23 rebuild component tests (all pass):
  11 kernel correctness, 8 transport/runtime, 3 scenario-scale,
  including 1GB primary-initiated with CRC validation.

29 files changed, ~2500 insertions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:39:11 -07:00
pingqiuandClaude Opus 4.6 49845dd509 feat: server-layer rebuild session skeleton — host routing for MVP
Add BlockService replica-side rebuild routing API that bridges
transport/host layer to BlockVol session surface:

  StartReplicaRebuildSession(path, config)
  ApplyReplicaRebuildWALEntry(path, sessionID, entry)
  ApplyReplicaRebuildBaseBlock(path, sessionID, lba, data)
  MarkReplicaRebuildBaseComplete(path, sessionID, totalBlocks)
  TryCompleteReplicaRebuildSession(path, sessionID)
  CancelReplicaRebuildSession(path, sessionID, reason)
  ReplicaRebuildSession(path) → snapshot

Each method does one thing: validate → WithVolume → delegate to BlockVol.
No wire decoding, no protocol decisions, no state invention. Transport
wiring (sessionControl/walData/sessionData handlers) is the next step.

2 focused tests: skeleton routes correctly, stale session ID rejected.

Updated v2-rebuild-mvp-session-protocol.md with server skeleton section.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:53:32 -07:00
pingqiuandClaude Opus 4.6 d2d57851b0 feat: rebuild MVP — dual-lane session with bitmap protection
Rebuild session protocol implementation for v2-rebuild-mvp-session-protocol.md.

New files:
- rebuild_bitmap.go: RebuildBitmap — session-scoped dense bitset for
  WAL-applied LBA tracking. MarkApplied on local WAL write (not receive).
  ShouldApplyBase returns false for WAL-covered LBAs (WAL always wins).

- rebuild_session.go: RebuildSession — replica-side two-line rebuild.
  WAL lane (ApplyWALEntry) + base lane (ApplyBaseBlock) with bitmap
  conflict resolution. TryComplete requires BOTH base_complete AND
  wal_applied_lsn >= target_lsn. Volume-level control surface:
  StartRebuildSession, ApplyRebuildSessionWALEntry/BaseBlock,
  MarkRebuildSessionBaseComplete, TryCompleteRebuildSession,
  CancelRebuildSession, ActiveRebuildSession.

- rebuild_mvp_test.go: 4 correctness tests — base+WAL converge,
  WAL-applied never overwritten by base, bitmap set on applied not
  received, control surface start/supersede/complete.

- rebuild_transport_test.go: 2 transport-level tests — two-line with
  real WAL shipping, live writes during base copy with bitmap conflict.

Design docs:
- v2-rebuild-mvp-session-protocol.md: MVP spec with message set, apply
  rules, completion/failure/crash rules, test matrix
- v2-sync-recovery-protocol.md: full protocol context (keepup/catchup/
  rebuild unified design, primary decision logic, two-line model)
- v2-session-protocol-shape.md: protocol shape overview

Protocol engine (reference, not production):
- sw-block/protocol/: 7-event engine with ~300 lines, 13 tests

6 rebuild tests pass, all existing component tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 14:30:34 -07:00
pingqiuandClaude Opus 4.6 55013e103b feat: Phase 20 Stage 0+1 closure — bootstrap + sustained workload on hardware
Stage 0 (bootstrap closure): PASS on m01/M02
  - create RF=2 sync_all → 10s shipper wait → 4k fsync → publish_healthy
  - Proves: BarrierAccepted observation, ShipperConnected, DurableLSN > 0

Stage 1 (sustained workload): 32/33 actions PASS
  - bootstrap → fio 10s randwrite → dd_write 1M×2 fsync → data checksum
  - Remaining: auto-failover promotion (separate issue)

Key fixes:
  - BarrierAccepted callback: SyncCache success → core DurableLSN update
  - BarrierRejected callback: barrier failures surface to core with reason
  - Shipper state callback for new volumes (not just startup volumes)
  - CatchUpTo ctrl conn reset: prevents stale control channel after recovery
  - CP13-6 max-bytes budget suspended: uses replicaFlushedLSN which can't
    advance without barrier; kills healthy shippers during async writes.
    Will be replaced by v2 negotiated sync/recovery protocol.
  - Barrier diagnostic logging: start/fail/success with reason and LSN
  - Scenario restructured: Stage 0 (bootstrap-closure) + Stage 1 (failover)
  - dd_write: sync_mode param + real stderr capture
  - sw-test-runner suite command: deploy once, run N scenarios
  - WAL size plumbing: proto + API + handler (forward-compatible)

Known: 6 blockvol/server test failures from Barrier() path change
(bounded catch-up in Barrier). Need test updates to match new semantics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 19:55:12 -07:00
pingqiuandClaude Opus 4.6 44103a1bd7 feat: Phase 20 acceptance fixes + sw-test-runner suite mode
Acceptance rows closed:
- WriteLBA/SyncCache contract: code comments document write-back vs
  durability fence semantics
- RF=2 stable identity: v2bridge always uses SetReplicaAddrs (preserves
  ServerID); blockcmd dispatcher also fixed to use setupPrimaryReplicationMulti;
  test asserts exact expected ReplicaID="vs-2" (not just non-empty)
- Tests treating WriteLBA as commit: replica_read_test rewritten with
  SyncCache as durability fence
- publish_healthy contract: 3 gate tests with hard assertions including
  gate 3 (PrimaryShipperConnected)
- SetReplicaAddr deprecation warning added
- WALShipper.ReplicaID() getter added for identity verification

Test runner enhancements:
- sw-test-runner suite command: build → deploy → run N scenarios in one
  invocation with --skip-deploy support
- Suite YAML definitions for T6 Stage 0 and Stage 1
- deploy action: kill stale processes, clean dirs, cross-compile, upload
- run-phase20-t6.ps1 PowerShell script (deprecated by suite command)

Engine/runtime fixes:
- Recovery executor nil-safety improvements
- Recovery bundle BuildRecoveryBundle defensive checks
- ShipperGroup MinReplicaFlushedLSNAll surface

Docs: acceptance checklist refined, test matrix updated, T6 runbook,
engine maintainer tutorial, design README updated.

26 files changed, ~1600 insertions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:30:54 -07:00
pingqiuandClaude Opus 4.6 275c3ee1c7 docs: Phase 20 acceptance checklist — architect-refined signoff matrix
Tighten acceptance matrix with explicit per-boundary rows, signoff
reading split into hard blockers vs product hardening, and clear
rule: architecture-complete ≠ product-complete.

6 hard blockers before T6/T7:
1. WriteLBA/SyncCache/sync_all contract closure
2. Fresh replica bounded catch-up before live tail
3. Timeout/retention-loss classification for catch-up
4. publish_healthy alignment with one protocol contract
5. RF=2 stable identity on all shipping paths
6. Test audit for incorrect WriteLBA==commit assumptions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:12:32 -07:00
pingqiuandClaude Opus 4.6 58aa842802 docs: Phase 20 product acceptance checklist
7-area acceptance matrix mapping current state vs product requirements:
write/durability contract, fresh replica bootstrap, host observation
completeness, serving/publish alignment, snapshot/rebuild convergence,
adapter consistency, test contract alignment.

Each item marked with: current state, required for product, blocks
T6/T7, best test level. Priority ordered into must-close-before-Stage-1,
should-close-before-Stage-2, and can-close-after-T6/T7.

Key diagnosis: architecture-complete, execution-incomplete. The engine
thinks like a product; the data plane still behaves partly like a
prototype. The gap is end-to-end contract closure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 00:05:22 -07:00
pingqiuandClaude Opus 4.6 d1a16fac03 feat: protocol-aware execution wave — phase gate for live WAL shipping
Add host-side protocol state seam that derives per-replica execution
state from V2 sender/session snapshots and blocks live-tail WAL
shipping while an active recovery session is in progress.

New file: weed/server/block_protocol_state.go
  - replicaProtocolExecutionState derived from engine snapshots
  - LiveEligible=false during active catch-up/rebuild sessions
  - bindProtocolExecutionPolicy wires policy into BlockVol
  - syncProtocolExecutionState called after assignments + core events

Data plane changes:
  - WALShipper.Ship() checks liveShippingPolicy before dial/send
  - BlockVol.SetLiveShippingPolicy persists across shipper group rebuilds
  - ShipperGroup propagates policy to all shippers

Design contract: sw-block/design/v2-protocol-aware-execution.md

Scope: WAL-first rollout only. Prevents illegal live-tail delivery
during active recovery. Does not change snapshot/build behavior or
move backlog. Next wave: bounded WAL catch-up under same contract.

Tests: 4 unit/component tests for phase gate behavior, plus bootstrap
seam tests that confirmed the two pre-existing bugs locally.

13 files changed, 900 insertions, 69 deletions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:47:07 -07:00
pingqiuandClaude Opus 4.6 f8e8c2c4d1 docs: fix Phase 20 test count — 48 not 49
Verified by counting: T1(5) + T2(12) + T3(8) + T4(8) + T5(13) + Proto(2) = 48.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 21:06:00 -07:00
pingqiuandClaude Opus 4.6 c7dd90c623 docs: Phase 20 test matrix — update with Tier 1 results + full roster status
Update coverage reading to reflect 49 tests (6 new component tests).
Add full roster status table with per-item strong/bounded/missing
marking and mapped test function names.

Unit+component: 32 of 33 items strong (T4-C7 NVMe bounded).
Integration: 6 of 10 missing (Tier 2 next).
Hardware: 4 of 4 missing (T6/T7 staged plan).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:42:13 -07:00
pingqiuandClaude Opus 4.6 1c7154a11a docs: Phase 20 test matrix — gap inventory + component test specs
Add detailed coverage mapping of 43 existing tests against the test
roster. Identify 7 missing component tests and 3 missing integration
tests with concrete scenarios, file placement, and must-prove criteria.

Key finding: every tester-found bug during T1-T5 was a wiring bug caught
by reviewing the production path, not by unit tests on pure logic. This
confirms component tests are the highest-value gap for CI/CD protection.

Priority order: Tier 1 (7 component tests, do now), Tier 2 (3 integration
tests, do before hardware), Tier 3 (4 hardware scenarios, T6/T7).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:33:55 -07:00
pingqiuandClaude Opus 4.6 3e6155c18e docs: Phase 20 T5 — wire ClusterReplicationMode into diagnostic surface
Add ClusterReplicationMode and EngineProjectionMode to
FailoverVolumeState so each volume in the failover diagnostic
carries its cluster/engine mode at diagnosis time.

FailoverDiagnosticSnapshot() enriches volume entries by looking up
the registry entry for each volume. This covers both the block
volume API (GET /block/volume/{name}) and the failover diagnostic
snapshot surface.

Update phase doc to reflect actual exposure paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:56:40 -07:00
pingqiuandClaude Opus 4.6 2b97cd04b8 fix: Phase 20 T3 — add V2 promotion observability to FailoverDiagnostic
FailoverDiagnostic now carries V2PromotionEnabled and V2PromotionReady
fields. MasterServer.FailoverDiagnosticSnapshot() enriches the failover
state diagnostic with rollout gate visibility so operators can confirm
whether the master is on V1, V2, or V2-fail-closed-placeholder mode.

Update phase-20.md: document default=false rollout policy (safe default
until proto regen enables evidence RPC, then flip to default true).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:27:02 -07:00
pingqiuandClaude Opus 4.6 1ca13143b6 feat: Phase 20 T2 — promotion evidence semantics + selection substrate
VS-side evidence handler (QueryBlockPromotionEvidence) reads live
blockvol.Status() + V2 core projection at call time. Fail-closed:
no core projection → ineligible with reason "missing_engine_projection".
Engine CommittedLSN used unconditionally when core present (no WALHeadLSN
overstatement). Eligibility owned by local V2 engine, not master.

Master-side selection (selectDurabilityFirstCandidate): durability-first
ordering by CommittedLSN, tie-break WALHeadLSN then HealthScore. All
ineligible → fail-closed, no promotion. Pluggable querier
(BlockPromotionEvidenceQuerier) for T3 wiring.

Proto messages added to volume_server.proto. gRPC transport binding
pending proto regen on M01 — this commit delivers evidence semantics
and selection substrate, not full end-to-end RPC closure.

Phase 20 doc updated with T2-T5 reviewer packs and cross-task guardrails.

13 tests: live facts, core projection mode, fail-closed no-core, 4 gated
modes, missing volume, epoch mismatch, CommittedLSN ordering, WALHeadLSN
tie-break, HealthScore tie-break, all-ineligible, mixed collection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:10:57 -07:00
pingqiuandClaude Opus 4.6 85dad8e0c9 feat: Phase 20 T1 — EngineProjectionMode in heartbeat
Add engine_projection_mode as a distinct proto/wire/registry field
that carries pure V2 engine-derived local projection mode from VS
to master. Reads ONLY from CoreProjection — no ad-hoc fallback.

Separate from existing VolumeMode: EngineProjectionMode is VS-local
V2 engine truth, VolumeMode is the existing field that conflates V2
and V1 paths. Both exist during transition; only EngineProjectionMode
is V2-authoritative.

Clears stale value on primary turnover: when a newly promoted primary
heartbeats without the field, the old primary's projection is not
preserved (prevents synthetic master-side truth).

5 focused tests: propagation, distinctness (hard assertion), backward
compat preservation, turnover-clears, turnover-with-field.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 15:45:26 -07:00
pingqiuandClaude Opus 4.6 044a6d770b feat: Phase 19 — bounded working RF2 block path
Live HTTP evidence transport, continuous Loop2 service, bounded auto
failover trigger, runtime-managed frontend export, bounded replica
repair, end-to-end RF2 handoff with continued I/O on new primary,
bounded operator HTTP surface, and CSI V2 runtime backend adapter.

11 new proof tests covering the full M6-M10 chain plus CSI create/
lookup/publish through the V2 runtime path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 15:12:00 -07:00
pingqiuandClaude Opus 4.6 5aedada53a feat: Phase 18 M3 — replicated data continuity closure
M3 milestone: write → Loop2 observe → failover → readback verify.

Continuity runtime (continuity_runtime.go):
- ExecuteReplicatedContinuity: composes mirror write + sync + Loop2
  observation + failover + readback verify into one bounded path
- ReplicatedContinuityResult: captures pre-failover Loop2 snapshot,
  failover result, selected primary, readback length, data match

Runtime manager extensions:
- Local node registry for write/readback during continuity verification
- RegisterNode now stores node reference for local I/O access

Tests prove two paths:
- Happy: write on source → failover → promoted node reads correct data
- Gated: degraded peer → failover gate stops → continuity reports failure

Phase 18 docs: M3 delivered, M4 next.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:10:05 -07:00
pingqiuandClaude Opus 4.6 cae07c0bf1 feat: Phase 18 M2 — active Loop 2 replication runtime
M2 milestone: bounded summary-driven active Loop 2 runtime.

Loop 2 runtime session (loop2_runtime.go):
- Loop2RuntimeSession: primary-led active observation of replica set
- ObserveOnce: collects replica summaries via transport seam, evaluates
  runtime mode (keepup / catching_up / degraded / needs_rebuild)
- Fail-closed severity escalation: mode only degrades, never reverts
- Detection: epoch mismatch, barrier failure, peer behind primary,
  recovery in progress, needs_rebuild sticky

Runtime manager integration:
- NewLoop2RuntimeSession, ObserveLoop2, LastLoop2Snapshot, Loop2Snapshot
- Runtime manager now retains active Loop 2 snapshots alongside failover

Tests prove three paths:
- healthy replica set → keepup
- peer behind → catching_up
- peer needs_rebuild → needs_rebuild (fail-closed)

Phase 18 docs updated: M2 delivered, M3 next.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:02:13 -07:00
pingqiuandClaude Opus 4.6 b82df09856 feat: Phase 18 M1 — transport-backed RF2 failover runtime
M1 milestone: failover evidence crosses transport/session seam.

Adapter seam (failover_adapter.go):
- FailoverEvidenceAdapter: query-side (promotion evidence + replica summary)
- FailoverTakeoverAdapter: execution-side (prepare + gate)
- FailoverTarget: binds NodeID + both adapters
- NewInProcessFailoverTarget: factory for in-process case

Transport seam (failover_evidence_transport.go):
- FailoverEvidenceTransport: request/response interface with nodeID routing
- FailoverEvidenceHandler: server-side registration
- InMemoryFailoverEvidenceTransport: first transport impl (in-memory)
- NewHybridInProcessFailoverTarget: transport-backed evidence + local takeover

Runtime manager (runtime_manager.go):
- InProcessRuntimeManager: participant registry + ExecuteFailover entry point
- Persisted failover snapshots/results per-volume and global-last

All failover paths (session/driver/manager) now go through adapter seam.
Old FailoverParticipant preserved as compatibility wrapper only.

Phase 18 docs: phase-18.md (M1-M5 structure), log, decisions.
Design docs updated: kernel-closure-review, claim-and-evidence ledger.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 13:57:08 -07:00
pingqiuandClaude Opus 4.6 b8c6944e3f feat: V2 MVP milestone — masterv2 + volumev2 + in-process failover
V2 runtime packages:
- sw-block/runtime/masterv2: identity authority (desired state,
  heartbeat handling, promotion arbitration via SelectPromotionCandidate)
- sw-block/runtime/volumev2: per-volume micro-cluster shell (node,
  orchestrator, control session, iSCSI frontend, takeover gate,
  failover session + driver, replica summary reconstruction)
- sw-block/runtime/purev2: RF1 execution shell (engine + store +
  dispatcher + local boundary observations)
- sw-block/runtime/protocolv2: three-channel separation
  (heartbeat/assignment/query + replica summary)

V2 binaries:
- sw-block/cmd/v2singleblock: single-node RF1 block server
- sw-block/cmd/purev2rf1: minimal RF1 runtime binary

Milestone capabilities:
- RF1 write/read/sync with engine-driven mode projection
- masterv2 ↔ volumev2 heartbeat convergence + assignment reissue
- Promotion query with fresh CommittedLSN/WALHeadLSN evidence
- Replica summary for bounded takeover reconstruction
- Primary-loss reconstruction from peer summaries (fail-closed gate)
- In-process failover driver with session observability
- Local boundary observations feed engine (Committed/Durable/Checkpoint)

Design docs:
- v2-two-loop-protocol.md: identity vs data-control separation
- v2-automata-ownership-map.md: event/command ownership split
- v2-loop1-surface-draft.md: heartbeat/query/assignment field spec
- v2-volumev2-single-node-mvp.md: target layering
- v2-kernel-closure-review.md: per-volume micro-cluster principle
- v2-pure-runtime-rf1-bootstrap.md, v2-capability-map.md,
  v2-proof-and-retest-pyramid.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 13:08:02 -07:00
pingqiuandClaude Opus 4.6 cf16e53b04 feat: Phase 16M/17 + promote fixes + testrunner updates
Phase 16M: explicit replica readiness on heartbeat seam
- master.proto: optional bool replica_ready = 19 (proto regenerated on M01)
- block_heartbeat_proto.go: write/read ReplicaReady with presence semantics
- master_block_registry.go: replicaReadyObservedFromHeartbeat prefers
  explicit proto field, falls back to address heuristic when absent
- volume_server_block.go: heartbeat emits ReplicaReady from core projection

Phase 17: host effects extraction + stop line
- phase-17-log.md: Batch 10/11 delivery notes

Promote fixes:
- master_block_failover.go: deterministic replica addrs from path hash
- qa_promote_replication_test.go: address-upgrade trigger test
- qa_promote_rejoin_live_test.go: new live rejoin test

Testrunner:
- devops.go: action improvements
- recovery-baseline-failover.yaml, suite-ha-failover.yaml: scenario updates
- cp11b3-manual-promote.yaml: promote scenario alignment
- fresh_volume_write_test.go: new component test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:38:05 -07:00
pingqiu 7855d5240c docs: add bounded productionization pilot artifacts
Freeze the first bounded pilot/preflight/stop/rollout-review artifact set and sync the global product ledgers so productionization can start from an explicit chosen-envelope discipline instead of ad hoc rollout judgment.

Made-with: Cursor
2026-04-04 19:01:56 -07:00
pingqiu 4f95a1e868 docs: package phase 17 product claim checkpoint
Freeze the first Phase 17 branch/contract/policy/envelope package, add review and supported-matrix artifacts, and sync the product-completion and claim-evidence ledgers to the new bounded post-Phase-16 checkpoint.

Made-with: Cursor
2026-04-04 18:21:16 -07:00
pingqiu 0f72c8d062 refactor: close bounded phase 16 restart truth seams
Bind non-authoritative inventory, restart primary-truth rebasing, and sparse replica readiness retention into the heartbeat/master seam, and package the bounded finish-line checkpoint with explicit claims, non-claims, and proof commands.

Made-with: Cursor
2026-04-04 16:13:06 -07:00
pingqiu 10833c8b68 refactor: preserve bounded volume mode reason heartbeat truth
Carry explicit volume_mode_reason across the heartbeat/master/API seam so outward surfaces retain the bounded core-owned explanation behind mode transitions.

Made-with: Cursor
2026-04-04 14:21:31 -07:00
pingqiu 6cad5bb8e1 refactor: rebind bounded volume mode heartbeat truth
Make the heartbeat/master boundary preserve explicit volume_mode truth so master consume no longer reconstructs outward mode only from secondary heartbeat signals. Keep backward compatibility by falling back to the previous reconstruction when older heartbeats do not send the field.

Made-with: Cursor
2026-04-04 13:56:41 -07:00
pingqiu 6794f79df9 refactor: preserve bounded publish healthy heartbeat truth
Make the heartbeat/master boundary preserve explicit publish_healthy truth so master consume no longer reconstructs healthy publication only from secondary readiness and degraded heuristics. Keep backward compatibility by falling back to the previous reconstruction when older heartbeats do not send the field.

Made-with: Cursor
2026-04-04 13:43:19 -07:00
pingqiu eb610deb92 refactor: preserve bounded needs_rebuild heartbeat truth
Make the heartbeat/master boundary preserve explicit needs_rebuild truth so primary heartbeat consume no longer collapses that stronger mode into a generic degraded signal. Keep backward compatibility by falling back to the previous heuristic when older heartbeats do not send the field.

Made-with: Cursor
2026-04-04 13:11:42 -07:00
pingqiu 69b41a7f16 refactor: rebind bounded replica-ready heartbeat truth
Make the heartbeat/master boundary carry explicit replica readiness truth so the registry no longer depends only on replica transport-address presence as a readiness proxy. Keep backward compatibility by falling back to the old address heuristic when older heartbeats do not send the field.

Made-with: Cursor
2026-04-04 12:06:53 -07:00
pingqiu 43dbebfa04 refactor: close bounded recovery drain and invalidation seams
Move removed-replica drain and replica-scoped invalidation onto explicit core-command paths so the widened multi-replica runtime no longer depends on coarse host-side recovery handling.

Made-with: Cursor
2026-04-04 11:01:12 -07:00
pingqiu 5fd9ec0edf refactor: widen bounded multi-replica catch-up startup ownership
Emit one core-owned start_recovery_task per primary catch-up replica so the bounded multi-replica startup path no longer depends on a single-replica assumption.

Made-with: Cursor
2026-04-04 10:21:28 -07:00
pingqiu 92c006eb29 refactor: aggregate bounded multi-replica catch-up conservatively
Track catch-up observations per replica so the volume-level recovery view stays in catching_up until all bounded replicas complete. This preserves the current bounded semantics while removing an overclaim that would block later multi-replica startup ownership work.

Made-with: Cursor
2026-04-04 09:27:03 -07:00
pingqiu 16ba70f856 refactor: make bounded recovery observation events replica-scoped
Carry replica-scoped addressing through bounded recovery planning and completion events so the core no longer depends on a volume-only observation seam. This preserves the current single-replica catch-up and rebuilding behavior while aligning the observation side with the replica-scoped command path.

Made-with: Cursor
2026-04-04 09:18:07 -07:00