Batch 7: Command dispatch binding extraction - New weed/server/blockcmd package: CommandHandler interface + DispatchCommands - volume_server_block.go applyCoreCommandsWithAssignment delegates to dispatcher - weed/server still owns RecordCommand, EmitCoreEvent, PublishProjection - v2bridge NOT given command-switch or event-emission semantics Phase 16C: Rebuilding assignment enters core command path Phase 16D: Rebuild recovery-task startup is command-driven Phase 16E: Catch-up recovery-task startup is command-driven Engine refinements: - RecoveryTarget on AssignmentDelivered event - shouldStartRecoveryTask / shouldStartReceiver guards - bootstrapReason: awaiting_rebuild_start Bridge/contract updates: - control_adapter.go: refined translation helpers - contract.go: executor port alignment Migration design docs (Batch 1-3 delivered, design artifacts): - v2-first/second/third-migration-batch.md + task-pack.md - v2-assignment-translation-unification.md - v2-execution-muscles-inventory.md - v2-separation-port-layer-audit.md - v2-legacy-runtime-exit-criteria.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8.1 KiB
V2 First Migration Task Pack
Date: 2026-04-04 Status: delivered
Purpose
This note turns the first separation batch into validate-able engineering tasks.
Each task must name:
- source
- destination
- authority rule
- adapter boundary
- acceptance criteria
- validation proof
The goal is to make separation work parallelizable without letting V1
runtime-owner behavior silently leak back in.
Shared Rules
All tasks in this pack inherit these rules:
sw-blockmust not directly importweed/storage/blockvolweed/may implement ports, but must not redefine semantic truth- each task must move one boundary, not redesign the whole runtime
- compatibility guards may stay, but must not be treated as semantic-authority proof
Existing landing zones already exist:
sw-block/bridge/blockvolsw-block/bridge/blockvol/control_adapter.gosw-block/bridge/blockvol/contract.go
So Task A does not begin with package creation. It begins with canonical-rule
consolidation into the existing sw-block bridge layer.
Task A: Canonical Assignment Translation
Goal
Make sw-block own the canonical helper rules for:
- replica identity
- recovery-target mapping
- engine replica-assignment packaging
Source
weed/storage/blockvol/v2bridge/control.goweed/server/volume_server_block.go
Destination
sw-block/bridge/blockvol/control_adapter.go
Authority Rule
This is semantic translation logic, so the canonical rule belongs in
sw-block, not in product adapters.
Adapter Boundary
weed/ may still:
- parse
BlockVolumeAssignment - decide which source fields exist on the wire/runtime side
weed/ must not separately redefine:
ReplicaID = <volume>/<server>replica -> catchuprebuilding -> rebuild
Acceptance
sw-blockexports canonical helpers for identity and recovery-target mappingweed/storage/blockvol/v2bridge/control.goandweed/server/volume_server_block.goboth use those helpers- no direct address-derived identity logic remains in adapter code
Validation
go test ./sw-block/bridge/blockvolgo test ./weed/storage/blockvol/v2bridge -run "TestControl_|TestBridge_"- focused server path still passes:
go test ./weed/server -run "TestBlockService_ApplyAssignments_(PrimaryRole_UsesCoreStartRecoveryTaskForCatchUp|RebuildingRole_UsesCoreRecoveryPathWithoutLegacyDirectStart)"
Current proof anchors
TestControlAdapter_StableIdentityTestControlAdapter_RebuildRoleMappingTestControl_PrimaryAssignment_StableServerIDTestControl_RebuildAssignment
Task B: Reader Port Separation
Goal
Separate retained-history state reading as a pure execution muscle behind a
stable sw-block port.
Source
weed/storage/blockvol/v2bridge/reader.go
Destination
- contract remains in
sw-block/bridge/blockvol/contract.go - implementation stays thin in
weed/storage/blockvol/v2bridge/reader.go - future code landing zone, if needed:
sw-block/bridge/blockvol/runtime- or equivalent execution package under
sw-block
Authority Rule
Reader logic is not semantic authority. It must only read backend facts and project them into the engine-facing retained-history shape.
Adapter Boundary
weed/ may:
- read
BlockVol.StatusSnapshot() - map backend fields into the contract shape
weed/ must not:
- reinterpret durability meaning
- patch semantic fallbacks into the reader
Acceptance
BlockVolReadercontract stays complete and stable insw-blockReaderremains a thin adapter over realBlockVolStorageAdapter.GetRetainedHistory()depends only on the contract, not on weed internals
Validation
go test ./sw-block/bridge/blockvolgo test ./weed/storage/blockvol/v2bridge -run "TestReader_"
Current proof anchors
TestStorageAdapter_RetainedHistoryFromReaderTestReader_RealBlockVol_StatusSnapshotTestReader_RealBlockVol_HeadAdvancesWithWrites
Task C: Pinner Port Separation
Goal
Separate WAL/snapshot/full-base hold mechanics as execution muscles behind a
stable sw-block pinning port.
Source
weed/storage/blockvol/v2bridge/pinner.go
Destination
- contract remains in
sw-block/bridge/blockvol/contract.go - implementation stays thin in
weed/storage/blockvol/v2bridge/pinner.go - future migration target is a
sw-block-owned execution-muscle package, with weed-sideBlockVolbinding left thin
Authority Rule
Hold/release mechanics are execution detail. Recovery policy decides when to hold; pinner only decides how to pin in the backend.
Adapter Boundary
weed/ may:
- wire retention floor into
BlockVol - validate concrete hold positions against backend state
weed/ must not:
- decide recovery target
- redefine which boundary is authoritative
Acceptance
BlockVolPinneris the sole engine-facing pin contract- pinner implementation remains backend-thin and side-effect-local
- pin lifecycle symmetry is covered in
sw-blockcontract tests
Validation
go test ./sw-block/bridge/blockvolgo test ./weed/storage/blockvol/v2bridge -run "TestPinner_|TestBridge_"
Current proof anchors
TestStorageAdapter_WALPinRejectsRecycledTestStorageAdapter_SnapshotPinRejectsUntrustedTestStorageAdapter_PinReleaseSymmetryTestPinner_RealBlockVol_HoldWALRetentionTestPinner_RealBlockVol_HoldRejectsRecycled
Task D: Executor Muscle Separation
Goal
Separate catch-up / rebuild execution mechanics from weed/ runtime ownership
so that executor behavior is treated as a reusable muscle behind
sw-block-owned ports.
Source
weed/storage/blockvol/v2bridge/executor.go- related tests in
weed/storage/blockvol/v2bridge/*transfer* - related tests in
weed/storage/blockvol/v2bridge/*snapshot* - related tests in
weed/storage/blockvol/v2bridge/*truncate*
Destination
- contract shape in
sw-block/bridge/blockvol/contract.go - engine-facing use through:
engine.CatchUpIOengine.RebuildIO
- implementation remains thin in
weed/until backend-binding interfaces are fully extracted
Authority Rule
Executor code is allowed to:
- transfer bytes
- apply WAL entries
- install snapshots/full base
- truncate local WAL
Executor code is not allowed to:
- classify recovery outcome
- decide whether rebuild vs catch-up is needed
- own publication or health meaning
Adapter Boundary
weed/ may:
- call real
BlockVolAPIs - speak TCP rebuild/catch-up protocol
- update local backend runtime state during execution
weed/ must not:
- redefine engine recovery phases
- redefine target/achieved boundary meaning
Acceptance
BlockVolExecutoraligns exactly with engine execution port expectations- engine/executor integration is possible without
sw-blockimporting weed - executor logic is documented as reusable execution muscle, not semantic authority
Validation
go test ./sw-block/bridge/blockvolgo test ./weed/storage/blockvol/v2bridge -run "TestExecutor_|TestBridge_"- focused integrated runtime tests remain green:
go test ./weed/server -run "TestBlockService_ApplyAssignments_(PrimaryRole_UsesCoreStartRecoveryTaskForCatchUp|RebuildingRole_UsesCoreRecoveryPathWithoutLegacyDirectStart)"
Current proof anchors
TestContract_BlockVolReaderInterfaceTestExecutor_RealBlockVol_StreamWALEntriesTestExecutor_RealBlockVol_StreamPartialRangeTestExecutor_ErrorPaths
Parallel Execution Recommendation
These four tasks are safe to run in parallel if ownership stays clear:
- Task A: canonical translation rules
- Task B: reader port hardening
- Task C: pinner port hardening
- Task D: executor contract alignment
Recommended order for merge:
- Task A
- Task B
- Task C
- Task D
Reason:
- Task A removes semantic drift first
- Tasks B/C/D then migrate pure muscles behind that stable rule layer
Delivery Note
Final outcome:
- Task A required code change and is now delivered
- Tasks B/C/D were reviewed and confirmed already at the acceptance bar
- the next migration frontier is backend-binding extraction, not more contract cleanup