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>
This commit is contained in:
pingqiuandClaude Opus 4.6 committed 2026-04-05 23:47:07 -07:00
1 parent f8e8c2c4d1
commit d1a16fac03
17 files changed
+1631 -69

No files matched your search

@@ -119,6 +119,16 @@ func assignmentConfirmedByHeartbeat(a blockvol.BlockVolumeAssignment, infos []bl
return true
}
if info.ReplicaDataAddr == expectedData && info.ReplicaCtrlAddr == expectedCtrl {
// A primary refresh assignment that carries replica transport should not
// be confirmed while the local V2 core still projects allocated_only.
// Otherwise the master can drop the refresh based only on legacy
// transport fields before the VS actually re-applies the assignment to
// the core and grows replica membership.
if blockvol.RoleFromWire(a.Role) == blockvol.RolePrimary &&
info.EngineProjectionMode != "" &&
info.EngineProjectionMode == "allocated_only" {
return false
}
return true
}
}