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>
4.7 KiB
V2 Loop1 Surface Draft
Date: 2026-04-05 Status: active
Purpose
This note turns the two-loop design into a code-facing draft for the current
masterv2 and volumev2 packages.
It does not implement the new protocol yet. It defines the smallest surface refactor that should happen first.
Goal
Replace the current mixed heartbeat -> assignments MVP surface with three
separate Loop 1 surfaces:
- periodic heartbeat
- promotion query
- assignment
Current State
Today, masterv2 uses one heartbeat type and one assignment type:
sw-block/runtime/masterv2/master.gosw-block/runtime/volumev2/control_session.gosw-block/runtime/volumev2/volume.go
The current heartbeat still mixes:
- applied identity
- outward projection naming
- fields that should later become failover evidence
Target Surfaces
1. Heartbeat
Purpose:
- liveness
- applied identity
- outward compressed mode
Recommended fields:
NodeIDReportedAt- per-volume:
NamePathEpochRoleModeModeReason- passive
CommittedLSNcache RoleAppliedReplicaReady
Not included:
- per-replica progress
- catch-up target
- detailed recovery phase
If CommittedLSN is present here, it is only a convenience cache for
masterv2. Fresh promotion judgment still uses the query channel.
2. Promotion Query
Purpose:
- fresh promotion evidence
- failover arbitration
Recommended request:
VolumeNameExpectedEpoch
Recommended response:
VolumeNameNodeIDEpochRoleCommittedLSNWALHeadLSNReceiverReadyEligibleReason
Rule:
CommittedLSNis the primary selection keyWALHeadLSNis only a tiebreaker
3. Assignment
Purpose:
- authorize role
- fence stale owners
- deliver member identity
Recommended fields:
NamePathNodeIDEpochLeaseTTLRoleReplicaSetCreateOptions
ReplicaSet should carry identity plus transport addresses, not progress.
Field Migration From Current MVP
Current masterv2.VolumeHeartbeat
Today:
NamePathEpochRoleProjectionModePublicationReasonRoleApplied
Should become:
NamePathEpochRoleModeModeReasonRoleAppliedReplicaReady
Interpretation change:
ProjectionModeshould be renamed toModePublicationReasonshould stop pretending to be a generic reason fieldReplicaReadyshould be explicit on the identity surface
Current masterv2.VolumeView
Today:
ObservedEpochObservedRoleProjectionModePublicationReasonRoleApplied
Should become:
ObservedEpochObservedRoleModeModeReasonRoleAppliedReplicaReady
Optional later:
- bounded cached failover evidence for debugging only
Current volumev2.Node.Heartbeat()
Current source:
snap.Statussnap.Projection
Recommended extraction:
Modefromsnap.Projection.Mode.NameModeReasonfromsnap.Projection.Mode.Reason- passive
CommittedLSNcache from local status snapshot RoleAppliedfromsnap.Projection.Readiness.RoleAppliedReplicaReadyfromsnap.Projection.Readiness.ReplicaReady
Not from heartbeat:
CatchUpTargetRecoveryProgress
CommittedLSN may appear in heartbeat as a passive cache only.
Fresh promotion authority still belongs to the promotion-query channel.
CatchUpTarget and RecoveryProgress belong to Loop 2.
Code Refactor Order
Step 1
Add a small shared contract package for Loop 1 types, for example under:
sw-block/runtime/protocolv2/
Start with:
HeartbeatAssignmentPromotionQueryRequestPromotionQueryResponse
Step 2
Make masterv2 use the shared Loop 1 contract types instead of local ad hoc
duplicates.
Step 3
Make volumev2 heartbeat generation write to the narrowed heartbeat shape.
Step 4
Add a promotion-query interface without implementing full failover yet.
The first code slice only needs:
- request and response types
- local evidence extraction helper
- one focused test proving query returns fresh local evidence
Test Guidance
The first code refactor should keep existing control-loop tests and add one new test:
- existing heartbeat-assignment convergence should still pass
- new promotion-query test should prove:
- query is separate from heartbeat
- fresh state is returned at call time
- heartbeat
CommittedLSNis only a passive cache WALHeadLSNis not part of periodic heartbeat
Non-Goals
This draft does not yet define:
- full Loop 2 message schema
- full new primary truth reconstruction choreography
- quorum-specific
CommittedLSNalgorithm
Those come after the Loop 1 surface is cleanly split.