diff --git a/FABRIC-3.md b/FABRIC-3.md
index 8b15ff4..dff5c33 100644
--- a/FABRIC-3.md
+++ b/FABRIC-3.md
@@ -135,8 +135,11 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
`RELOCATE-BLOCK`, `FABRIC-2.md`, commit `36d832f`. This item is about the identityβrange
allocation that decides what to relocate blocks* into*, still unbuilt.)*
-- [ ] Design the on-drive block-map format (Section U item 4) β what it records (block ranges
- claimed? individual block liveness? something else), how it's serialized.
+- [x] **SCOPED 2026-08-27 (FABRIC-3.md Β§F.4).** Design the on-drive block-map format (Section
+ U item 4). Resolved as: no separate table β repurpose the existing, fully-wired-but-
+ zero-callers `blk_meta_t`/`blk_get_meta()`/`blk_set_meta()` (`block_subsystem.c`), replacing
+ its stale POSIX-flavored ownership fields with a pubkey-fingerprint owner + a fast-deny ACL
+ bit + deliberate reserved slack. Field design done; the actual code edit is not.
- [ ] Implement writing the block-map to a drive.
@@ -1395,7 +1398,7 @@ graph TD
CERTVERIFY["π CA-signed-cert verification path (M3)
UNBLOCKED by M6, not yet built"]
FIRSTTOUCH["β First-touch identityβblock-range
allocation (M3)"]
- BMAPFMT["β On-drive block-map format (M3)
real design decision, not started"]
+ BMAPFMT["β On-drive block-map format (M3)
SCOPED 2026-08-27 (Β§F.4) β repurpose blk_meta_t"]
BMAPWRITE["β Write block-map to drive (M3)"]
BMAPREAD["β Read/validate block-map on insert (M3)"]
MIGSM["π‘ Migration state machine (M3)
partially answered: ACL decides *when*"]
@@ -1452,9 +1455,9 @@ graph TD
classDef open fill:#666,stroke:#333,color:#fff
classDef partial fill:#883,stroke:#333,color:#fff
class M6,PH8,EXPIRE,MSGSHAPE,HOTPLUG done
- class W10,STALL,FIRSTTOUCH,BMAPWRITE,BMAPREAD,UNCLEAN,WIREBIND,BINDSTEP,DETACH,MINT blocked
+ class W10,STALL,FIRSTTOUCH,BMAPWRITE,BMAPREAD,UNCLEAN,WIREBIND,BINDSTEP,DETACH,MINT,BMAPFMT blocked
class CERTVERIFY unblocked
- class BMAPFMT,ACLKEY,RUNCAP,MSGMIGRATE,SSDSCOPE,ROUNDTRIP,POLYBLOCK open
+ class ACLKEY,RUNCAP,MSGMIGRATE,SSDSCOPE,ROUNDTRIP,POLYBLOCK open
class MIGSM partial
```
@@ -1573,6 +1576,82 @@ yes β the *lock itself* being immutable once set at a VM's birth is a differen
"binding stays retargetable," and shouldn't be conflated with decision 3 above) β worth a
dedicated look when this is actually built, not decided in this pass.
+### F.4 β `BMAPFMT` (on-drive block-map format)
+
+Worked through conversationally, one step at a time, rather than dumped as a finished design β
+each step below only followed once the previous one was grounded in real code, not assumption.
+
+**Step 1 β does a map cell need a state field, not just ownership?** Yes, confirmed against
+existing precedent before answering: the internal Artemis BAM entry
+(`blk_bam_entry_t{allocated, dirty}`, `block_subsystem.h:257-261`) already conflates ownership
+and state in the simplest map this codebase has. More importantly, `MIGSM` and `UNCLEAN` (two
+already-identified, currently-blocked graph nodes) have nowhere else to record "this range is
+mid-migration" or "this range's flush was interrupted" without a state field β this node was
+quietly upstream of both.
+
+**Step 2 β should a cell also carry an ACL?** Yes, and it can't reuse `acl_mode`/`acl_allow`/
+`acl_pinned` directly for the same reason `ACLKEY` (F.2) couldn't β those are `DictEntry`
+fields. Decided: the owner field and any future ACL grant should share **the same identity
+representation** `VMIdentity` already established (a pubkey), not a second encoding β keeps
+"identity is one common primitive" (D.5) actually true in practice, not just in name.
+
+**Step 3 β ACL check ordering.** "ACL denial is the fast exit path" β grounded directly
+against `vm.c:611-624`'s live word-execution ACL check: a cached `acl_allow` bit is
+checked *first*, before any other work, and denial short-circuits immediately. A block cell's
+ACL needs the identical shape at its top: one cheap cached allow bit, checked before range/
+state/owner logic runs.
+
+**Step 4 β buffering/granularity, and a major discovery.** Confirmed: this kernel already
+buffers a whole 4 KiB devblock on any block touch (`LOAD` and friends), and
+`BLK_META_PER_BLOCK` (`block_subsystem.h:83`, `341u /* 341Γ3 ~= 1023, padded to 1024 */`)
+already packs **exactly** 3Γ1 KiB Forth-block metadata slices into that same devblock's spare
+1 KiB β the "3-block cluster + 1 KiB metadata" shape volunteered in conversation *already
+exists as `blk_meta_t`*, with real, wired, non-stub accessors (`blk_get_meta()`/
+`blk_set_meta()`, cached, dirty-tracked, `block_subsystem.c:1175-1207`). **Grepped and
+confirmed it has zero callers anywhere in the codebase** β fully built, fully unused. Its
+existing "Security & ownership" 40 bytes (`owner_id`/`permissions`/`acl_block`/`signature[2]`)
+predate and directly conflict with both the anti-POSIX principle and `VMIdentity`'s
+pubkey-based model β flagged, not silently reused.
+
+**Decisions made 2026-08-27:**
+
+1. **`BMAPFMT` is not a new structure β it's repurposing `blk_meta_t`.** No separate on-drive
+ block-map table gets built. Ownership/ACL/state travel *with* the block itself
+ (distributed), not in a centralized table (the shape Milestone 3's original wording
+ pictured). One I/O gets payload and map info together; nothing separate to keep in sync.
+2. **Consequence flagged, not silently absorbed:** `homeblocks_sig_t`'s reserved
+ `blockmap_offset`/`blockmap_devblocks` fields (`homeblocks_sig.h:93-95`) become unnecessary
+ under this decision β that header reserved space for a centralized table this design no
+ longer needs. Needs a comment update in that file once this is actually built, not left
+ silently stale.
+3. **Replacement for the 40-byte "Security & ownership" block** (same budget the old
+ `owner_id`/`permissions`/`acl_block`/`signature[2]` occupied; nothing else in `blk_meta_t`
+ moves):
+ ```
+ uint8_t owner_fp[8]; /* truncated fingerprint of owner's VMIdentity pubkey, not the
+ full 32 bytes -- keeps this cheap per-block; full pubkey
+ resolves via the drive's own identity record. */
+ uint8_t acl_allow; /* cached fast-deny bit, checked first -- vm.c:611-624's exact
+ pattern, applied to a block instead of a word. */
+ uint8_t acl_reserved[7]; /* explicitly undecided -- deliberate slack per "flexibility
+ until we understand the recipe," not a placeholder to fill
+ reflexively. */
+ uint64_t reserved_future; /* untouched budget, same reasoning. */
+ ```
+ `flags` (already existing, already generic) does double duty as the **state** field from
+ Step 1 β no new field, just future-defined bit values (`CLAIMED`/`MIGRATING`/`STALE`/etc.).
+ Everything else in `blk_meta_t` (`checksum`, timestamps, `content_type`, hash, chain links,
+ `app_data[15]`) is untouched.
+4. **Allocation granularity**: claims quantize to whole devblocks (3 Forth blocks), matching
+ the existing packing β a cell never needs to describe partial-devblock ranges.
+
+**Not yet scoped (deferred within this node):** the actual allow-list/grant shape beyond the
+single fast-deny bit (lands in `acl_reserved`, once designed); the specific `flags` bit
+values for each state; whether `blk_get_meta()`/`blk_set_meta()` need new FORTH word wrappers
+or stay C-only like `zuse_cert_seed`'s "no FORTH access" precedent; the actual repurposing
+edit to `block_subsystem.h`/`.c` itself (this pass produced the field design, not the code
+change).
+
### D.5 β Scope expansion (2026-08-27): identity is common to every VM, not just users
Surfaced while scoping `ACLKEY`, stated directly: *"the whole object is to deliver a