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>
6.6 KiB
V2 Second Migration Task Pack
Date: 2026-04-04 Status: delivered
Purpose
This note turns the second separation batch into validate-able engineering tasks.
The first batch proved that contract ownership and translation authority already
belong in sw-block. The second batch now targets the remaining physical
coupling: backend bindings.
Shared Rules
All tasks in this pack inherit these rules:
sw-blockmust not directly importweed/storage/blockvol- reusable execution-muscle logic should move toward
sw-block - weed-side code should shrink toward thin concrete bindings
- no task in this pack may redefine engine semantics or recovery policy
Task E: Reader Backend-Binding Extraction
Goal
Extract reusable reader logic from direct BlockVol coupling so the
BlockVol-specific part becomes a thin snapshot binding.
Source
weed/storage/blockvol/v2bridge/reader.goweed/server/block_recovery.goreader shim
Destination
- reusable reader logic in
sw-block/bridge/blockvol/runtime - thin
BlockVolsnapshot binding inweed/storage/blockvol/v2bridge
Authority Rule
The reusable logic that shapes backend snapshot data into
bridge.BlockVolState belongs with sw-block execution muscles.
The weed-side binder may fetch snapshot fields from real BlockVol, but it
must not own the reusable state-shaping layer.
Adapter Boundary
weed/ may:
- call
StatusSnapshot()on realBlockVol - expose raw backend snapshot data to the extracted layer
weed/ must not:
- keep a second contract-shape mapping layer in
block_recovery.go - reinterpret retained-history meaning
Acceptance
- reusable reader logic no longer depends on direct
BlockVolimport weed/storage/blockvol/v2bridge/reader.gois reduced to thin binding codereaderShimForRecoveryis removed or reduced to trivial wiring
Validation
go test ./sw-block/bridge/blockvolgo test ./weed/storage/blockvol/v2bridge -run "TestReader_"- if the recovery shim changes, run:
go test ./weed/server -run "TestP4_|TestP16B_"
Current proof anchors
TestStorageAdapter_RetainedHistoryFromReaderTestReader_RealBlockVol_StatusSnapshotTestReader_RealBlockVol_HeadAdvancesWithWrites
Task F: Pinner Backend-Binding Extraction
Goal
Extract hold bookkeeping and release lifecycle from direct BlockVol coupling
so weed-side code only performs concrete retention-floor binding and state
validation.
Source
weed/storage/blockvol/v2bridge/pinner.goweed/server/block_recovery.gopinner shim
Destination
- reusable hold bookkeeping in
sw-block/bridge/blockvol/runtime - thin
BlockVolretention binding inweed/storage/blockvol/v2bridge
Authority Rule
Hold bookkeeping is reusable execution-muscle logic. Concrete interaction with
the flusher and StatusSnapshot() stays in weed/, but ID tracking and release
symmetry should not require direct BlockVol imports.
Adapter Boundary
weed/ may:
- install retention-floor callbacks on real
BlockVol - validate requested hold positions against live backend snapshot state
weed/ must not:
- keep reusable hold lifecycle ownership trapped in
weed/ - force recovery policy knowledge into the pinner binding
Acceptance
- reusable hold bookkeeping can live in
sw-blockwithoutBlockVolimports - weed-side pinner code shrinks toward concrete callback/state binding
pinnerShimForRecoveryis removed or reduced to trivial wiring
Validation
go test ./sw-block/bridge/blockvolgo test ./weed/storage/blockvol/v2bridge -run "TestPinner_|TestBridge_"- if the recovery shim changes, run:
go test ./weed/server -run "TestP4_|TestP16B_"
Current proof anchors
TestStorageAdapter_WALPinRejectsRecycledTestStorageAdapter_SnapshotPinRejectsUntrustedTestStorageAdapter_PinReleaseSymmetryTestPinner_RealBlockVol_HoldWALRetentionTestPinner_RealBlockVol_HoldRejectsRecycled
Task G: Executor Backend-Capability Extraction
Goal
Split executor logic into:
- reusable orchestration that belongs with
sw-blockexecution muscles - concrete backend capabilities and wire operations that remain in
weed/
Source
weed/storage/blockvol/v2bridge/executor.go- related tests in:
weed/storage/blockvol/v2bridge/*transfer*weed/storage/blockvol/v2bridge/*snapshot*weed/storage/blockvol/v2bridge/*truncate*
Destination
- reusable executor orchestration in
sw-block/bridge/blockvol/runtime - thin backend capability bindings in
weed/storage/blockvol/v2bridge
Authority Rule
The engine still owns recovery policy. This task does not move policy.
The reusable execution sequence for:
- bounded WAL replay
- full-base install plus second catch-up
- snapshot transfer verification
- truncate escalation boundary
should no longer be inseparable from direct BlockVol imports.
Adapter Boundary
weed/ may:
- implement concrete backend operations on real
BlockVol - own rebuild TCP framing and network transport while it still depends on
blockvolprotocol types
weed/ must not:
- keep the whole recovery step orchestration trapped behind direct
BlockVolimports when capability interfaces can be extracted - redefine engine-visible boundary meaning
Acceptance
- executor reusable logic depends on extracted capability interfaces, not
direct
BlockVolimports - weed-side executor code is reduced to concrete backend/network bindings
- outcome classification still remains outside the executor layer
Validation
go test ./sw-block/bridge/blockvolgo test ./weed/storage/blockvol/v2bridge -run "TestExecutor_|TestBridge_"- focused runtime integration still passes:
go test ./weed/server -run "TestBlockService_ApplyAssignments_(PrimaryRole_UsesCoreStartRecoveryTaskForCatchUp|RebuildingRole_UsesCoreRecoveryPathWithoutLegacyDirectStart)"
Current proof anchors
TestContract_BlockVolReaderInterfaceTestExecutor_RealBlockVol_StreamWALEntriesTestExecutor_RealBlockVol_StreamPartialRangeTestExecutor_ErrorPaths
Recommended Execution Order
Recommended order:
- Task E
- Task F
- Task G
Reason:
- reader extraction is lowest risk and pure read-path
- pinner extraction adds lifecycle but still avoids policy
- executor extraction is the largest surface and should build on the previous two cuts
Delivery Note
Final outcome:
- Task E was completed by code change
- Task F was completed by code change
- Task G was reviewed and confirmed already clean
- after Batch 2,
weed/server/block_recovery.gono longer carries reader/pinner shim types