diff --git a/sw-block/design/v3-invariant-ledger.md b/sw-block/design/v3-invariant-ledger.md index 5bd7a0d50..9f8f3df2f 100644 --- a/sw-block/design/v3-invariant-ledger.md +++ b/sw-block/design/v3-invariant-ledger.md @@ -109,6 +109,7 @@ IDs are **append-only**. If an invariant is retired, move its row to §6 (archiv | `INV-BIN-WIRING-LISTENER-LIFECYCLE-LIFO` | `ReplicaListener` Stop runs BEFORE durable storage close in `cmd/blockvolume/main.go` shutdown chain (LIFO with construction). Listener borrows `LogicalStorage` from `DurableProvider`; tearing down storage first would race with in-flight handler conns | P15 G5-4 | host (binary lifecycle) | `cmd/blockvolume/g5_4_l2_replication_test.go:TestG54_BinaryWiring_RoleSplit_2NodeSmoke` (TCP dial on replica's `--data-addr` succeeds during host lifetime — pins the listener IS bound during the data-addr window). Source-side check: `cmd/blockvolume/main.go` shutdown block — `replListen.Stop()` before `durableProv.Close()` | 2026-04-27 (G5-5 §close — Tier 2 m01 cross-node hardware Integration backstop: `seaweed_block@5c4718f` rounds 1-14 surfaced + verified the binary-wiring chain end-to-end; G5-4 close pinned at component+subprocess scope, G5-5 upgrades to real-hardware) | ACTIVE | | `INV-BIN-WIRING-ASSIGNMENT-DRIVES-MEMBERPRESENT` | Binary doesn't fake `MemberPresent`; it waits for first `AssignmentFact` with `fact.ReplicaID == self.ReplicaID` to set it via the engine apply path. Replica role (no self-named fact) keeps `MemberPresent=false` indefinitely (correct: replica isn't frontend-primary-write-ready) | P15 G5-4 | engine + host (binary) | `cmd/blockvolume/g5_4_l2_replication_test.go:TestG54_BinaryWiring_RoleSplit_2NodeSmoke` (replica's status response shows `Healthy=false` even after 10s — pins MemberPresent never flips for replica role; primary's `Healthy=true` pins MemberPresent flips on self-named fact). Backstop: `core/engine/apply.go:applyAssignment` test coverage in `apply_test.go` | 2026-04-27 (G5-5 §close — Tier 2 m01 cross-node hardware Integration backstop: `seaweed_block@5c4718f` rounds 1-14 surfaced + verified the binary-wiring chain end-to-end; G5-4 close pinned at component+subprocess scope, G5-5 upgrades to real-hardware) | ACTIVE | | `INV-BIN-WIRING-SESSIONID-VIA-ADAPTER` | Binary path mints sessionIDs ONLY through `core/adapter` (process-wide `sessionIDCounter atomic.Uint64` at `adapter.go:70`); MUST NOT bypass adapter with hardcoded sessionIDs the way component-framework shortcuts (`WithLiveShip`, `CatchUpReplica`) do. Adapter-routed dispatch is the production path; framework shortcuts are test conveniences with a known sessionID-collision gap (T4c §I carry; QA G5-1 round 1 SKIP). Pinning this invariant ensures the gap stays test-side | P15 G5-4 | adapter | `cmd/blockvolume/main.go` (binary path: peer-set updates flow through `replication.UpdateReplicaSet` → `ReplicaPeer` → `BlockExecutor` calls dispatched by `adapter.VolumeReplicaAdapter`, which mints fresh sessionIDs at every dispatch site). Source-side check: `cmd/blockvolume/main.go` does not import `core/replication/component` (mini-plan v0.4 §7.1 #2 fence). Backstop: `core/adapter/adapter.go:70` declares `sessionIDCounter` with file-local visibility; only `adapter.go:353,359,377,392,402` mint via `Add(1)` | 2026-04-27 (G5-5 §close — Tier 2 m01 cross-node hardware Integration backstop: `seaweed_block@5c4718f` rounds 1-14 surfaced + verified the binary-wiring chain end-to-end; G5-4 close pinned at component+subprocess scope, G5-5 upgrades to real-hardware) | ACTIVE | +| `INV-G5-FRONTEND-CAPACITY-FROM-DURABLE-CONFIG` | iSCSI/NVMe externally-visible volume capacity and block size MUST derive from the daemon's `--durable-blocks` × `--durable-blocksize`, not silently fall back to frontend defaults (`DefaultVolumeBlocks=2048` × `DefaultBlockSize=512` = 1 MiB). Without this plumb-through, a daemon configured for N MiB durable storage advertises a 1 MiB iSCSI/NVMe LUN and any workload above LBA 256 fails | P15 G5-5C addendum (P0 product fix) | host (binary, frontend wiring) | Unit: `cmd/blockvolume/frontend_capacity_test.go:TestComputeFrontendVolumeSize_ProductOfBlocksAndBlockSize` (5 cases incl. 256 MiB), `TestComputeFrontendVolumeSize_RejectsZero`, `TestComputeFrontendVolumeSize_OverflowGuard`, `TestIscsiHandlerCapacity_FromDurableConfig`, `TestNvmeHandlerCapacity_FromDurableConfig` (capacity round-trip via `iscsi.NewSCSIHandler` / `nvme.NewIOHandler`), `TestFrontendDefaults_StillReturn1MiB` (negative-control documenting the bug shape). Source-side check: `cmd/blockvolume/main.go::computeFrontendVolumeSize` flows into both `iscsi.TargetConfig.Handler.{BlockSize, VolumeSize}` and `nvme.TargetConfig.Handler.{BlockSize, VolumeSize}`. Hardware: m01 scale-sequential (1000 LBAs × 4 KiB = 4 MiB) verified at G5-5C addendum hardware re-run | 2026-04-28 (G5-5C addendum P0 — surfaced by QA scale-sequential test on `seaweed_block@712cbc47`; fixed at `seaweed_block@a250b52`; m01 hardware re-verification pending QA on this tree) | ACTIVE | > **The table above is a schema seed.** P15 QA establishment populates it fully from the four sources listed in §2, cross-referenced with [`v3-semantic-constraint-checklist.md`](./v3-semantic-constraint-checklist.md).